temp update (currently has issues)

master
michael 2021-08-04 16:22:15 -07:00
parent 22b7fe7333
commit 6c3c900570
7 changed files with 1274 additions and 545 deletions

126
app.py
View File

@ -9,7 +9,6 @@ import pathlib
class AppWindow(QtWidgets.QMainWindow, Ui_MainWindow): class AppWindow(QtWidgets.QMainWindow, Ui_MainWindow):
csv_selected = QtCore.pyqtSignal()
correlation_complete = QtCore.pyqtSignal() correlation_complete = QtCore.pyqtSignal()
fitting_complete = QtCore.pyqtSignal() fitting_complete = QtCore.pyqtSignal()
@ -17,14 +16,6 @@ class AppWindow(QtWidgets.QMainWindow, Ui_MainWindow):
super(AppWindow, self).__init__() super(AppWindow, self).__init__()
self.setupUi(self) self.setupUi(self)
# Graphing actions
self.csv_selected.connect(self.raw_data_graph.plot)
def corr_act():
self.groups_graph.plot()
self.graph_tabs.setCurrentIndex(1)
self.correlation_complete.connect(corr_act)
# Helpers # Helpers
def display_warning(message: str): def display_warning(message: str):
@ -52,15 +43,24 @@ class AppWindow(QtWidgets.QMainWindow, Ui_MainWindow):
return return
mem['x_data'] = data.transpose()[0] mem['x_data'] = data.transpose()[0]
mem['y_data'] = data.transpose()[1] mem['y_data'] = data.transpose()[1]
mem['timestep'] = mem['x_data'][1] - mem['x_data'][0]
self.raw_data_graph.plot() # Graph new stuff
# self.groups_graph.clear() # Clear old stuff
self.voltage_graph.clear()
self.added_peaks_graph.clear()
# self.tau_graph.clear()
mem['ymin'], mem['ymax'] = crds_calc.minmax(mem['y_data'])
try: try:
mem['v_data'] = data.transpose()[2] mem['v_data'] = data.transpose()[2]
self.voltage_graph.plot()
self.graph_tabs.setCurrentIndex(1)
except IndexError: except IndexError:
display_warning('No voltage column detected. VThreshold algo will not work.') display_warning('No voltage column detected. VThreshold algo will not work.')
self.voltage.setVisible(False)
self.groups_graph.clear()
self.tau_graph.clear()
self.graph_tabs.setCurrentIndex(0) self.graph_tabs.setCurrentIndex(0)
self.csv_selected.emit()
# Universal Actions stuff # Universal Actions stuff
@ -68,6 +68,11 @@ class AppWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.actionOpen_CSV_File.triggered.connect(select_csv) self.actionOpen_CSV_File.triggered.connect(select_csv)
self.actionGithub_Repository.triggered.connect(lambda: QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://github.com/turtlebasket/crds_analyze'))) self.actionGithub_Repository.triggered.connect(lambda: QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://github.com/turtlebasket/crds_analyze')))
# NOTE: Do later, use QDialog
# def check_if_quit():
# <stuff here>
self.actionQuit_2.triggered.connect(sys.exit)
# Inputs # Inputs
def switch_grouping_algo(): def switch_grouping_algo():
@ -86,19 +91,45 @@ class AppWindow(QtWidgets.QMainWindow, Ui_MainWindow):
if self.check_custom_end.isChecked(): if self.check_custom_end.isChecked():
self.spin_end_time.setDisabled(False) self.spin_end_time.setDisabled(False)
else: else:
# mem['end_time'] = self.spin_end_time.value()
self.spin_end_time.setDisabled(True) self.spin_end_time.setDisabled(True)
self.check_custom_end.stateChanged.connect(set_end_time) self.check_custom_end.stateChanged.connect(set_end_time)
# Sync up peak detection settings between input locations
self.spin_min_peakheight.valueChanged.connect(lambda x: self.spin_min_peakheight_2.setValue(x))
self.spin_min_peakheight_2.valueChanged.connect(lambda x: self.spin_min_peakheight.setValue(x))
self.spin_min_peakprominence.valueChanged.connect(lambda x: self.spin_min_peakprominence_2.setValue(x))
self.spin_min_peakprominence_2.valueChanged.connect(lambda x: self.spin_min_peakprominence.setValue(x))
self.spin_moving_average_denom.valueChanged.connect(lambda x: self.spin_moving_average_denom_2.setValue(x))
self.spin_moving_average_denom_2.valueChanged.connect(lambda x: self.spin_moving_average_denom.setValue(x))
# Make advanced peak detection optional
def update_advanced_peak_detection_setting():
enabled = self.check_advanced_peak_detection.isChecked()
self.spin_min_peakheight_2.setEnabled(enabled)
self.spin_min_peakprominence_2.setEnabled(enabled)
self.spin_moving_average_denom_2.setEnabled(enabled)
self.check_advanced_peak_detection.stateChanged.connect(update_advanced_peak_detection_setting)
def init_correlate(): def init_correlate():
groups_raw = None groups_raw = None
algo = self.combo_grouping_algo.currentIndex() algo = self.combo_grouping_algo.currentIndex()
try: try:
if algo == 0: if algo == 0:
display_error('VThreshold not yet implemented.') groups_raw = crds_calc.vthreshold(
return mem['x_data'],
mem['y_data'],
mem['v_data'],
self.spin_min_voltage.value(),
self.spin_max_voltage.value(),
mirrored=False if self.check_skip_groups.checkState() == 0 else True,
start=self.spin_start_time.value() if self.check_custom_start.isChecked() else None,
end=self.spin_end_time.value() if self.check_custom_end.isChecked() else None
)
# display_error('VThreshold not yet implemented.')
# return
elif algo == 1: elif algo == 1:
try:
groups_raw = crds_calc.spaced_groups( groups_raw = crds_calc.spaced_groups(
mem['x_data'], mem['x_data'],
mem['y_data'], mem['y_data'],
@ -110,16 +141,71 @@ class AppWindow(QtWidgets.QMainWindow, Ui_MainWindow):
start=self.spin_start_time.value() if self.check_custom_start.isChecked() else None, start=self.spin_start_time.value() if self.check_custom_start.isChecked() else None,
end=self.spin_end_time.value() if self.check_custom_end.isChecked() else None end=self.spin_end_time.value() if self.check_custom_end.isChecked() else None
) )
except (ValueError, TypeError):
display_error('Failed to correlate. This could be because no groups are being detected.') if groups_raw == None or len(groups_raw) < 1:
display_error("No groups were detected. Try adjusting grouping parameters.")
mem['groups_correlated'] = crds_calc.correlate_groups(groups_raw) mem['groups_correlated'] = crds_calc.correlate_groups(groups_raw)
self.correlation_complete.emit()
# Graphing action
self.groups_graph.plot()
self.graph_tabs.setCurrentIndex(2)
except KeyError: except KeyError:
display_error('Failed to correlate. Did you import a data file & set parameters?') display_error('Failed to correlate. Did you import a data file & set parameters?')
self.correlate_button.pressed.connect(init_correlate) self.correlate_button.pressed.connect(init_correlate)
def init_add_simple():
try:
mem['added_peaks'] = crds_calc.add_peaks_only(mem['groups_correlated'])
self.added_peaks_graph.set_params(None, shift_over=None)
self.added_peaks_graph.plot()
self.graph_tabs.setCurrentIndex(3)
except KeyError:
display_error("Correlated groups not found. Group peaks first.")
self.peak_add_button.pressed.connect(init_add_simple)
def init_add():
try:
mem['added_peaks'], mem['peak_indices'], mem['isolated_peaks'] = crds_calc.isolate_peaks(
mem['groups_correlated'],
self.spin_peak_overlap.value(),
self.spin_moving_average_denom.value(),
peak_prominence=self.spin_min_peak_height_added.value(),
peak_minheight=self.spin_peak_prominence_added.value(),
shift_over=self.spin_shift_over.value()
)
self.added_peaks_graph.set_params(self.spin_peak_overlap.value(), shift_over=self.spin_shift_over.value())
self.added_peaks_graph.plot()
self.graph_tabs.setCurrentIndex(3)
except KeyError:
display_error("Correlated groups not found. Group peaks first.")
self.isolate_button.pressed.connect(init_add)
def init_fit():
if not 'isolated_peaks' in mem:
display_error('Peaks not yet isolated.')
return
mem['fit_equations'] = crds_calc.fit_peaks(
mem['isolated_peaks'],
mem['peak_indices'],
self.spin_min_peakheight_2.value(),
self.spin_min_peakprominence_2.value(),
self.spin_moving_average_denom_2.value(),
self.spin_var_a.value(),
self.spin_var_tau.value(),
self.spin_var_y0.value(),
self.spin_shift_over_fit.value(),
self.check_advanced_peak_detection.isChecked()
)
# print(mem['fit_equations'])
self.peak_fits_graph.plot()
self.fit_button.pressed.connect(init_fit)
# Show equation # Show equation
pix = QtGui.QPixmap(f"{pathlib.Path(__file__).parent.resolve()}/assets/eq3.png") pix = QtGui.QPixmap(f"{pathlib.Path(__file__).parent.resolve()}/assets/eq3.png")

View File

@ -3,12 +3,25 @@
set compileUI=0 set compileUI=0
set build=0 set build=0
IF "%~1" == "" (
echo --------------
echo BUILD SCRIPT
echo --------------
echo -compileUI Generate main window class from .UI file
echo -build Build standalone desktop app with embedded python runtime
exit
)
FOR %%A IN (%*) DO ( FOR %%A IN (%*) DO (
IF "%%A"=="-compileUI" set compileUI=1 IF "%%A"=="-compileUI" set compileUI=1
IF "%%A"=="-build" set build=1 IF "%%A"=="-build" set build=1
) )
if %compileUI%==1 pyuic5.exe -x .\ui\mainwin.ui -o mainwin.py if %compileUI%==1 (
pyuic5.exe -x .\ui\mainwin.ui -o mainwin.py
if %ERRORLEVEL% EQU 0 echo Generated mainwin.py.
if %ERRORLEVEL% EQU 1 echo Generation failed.
)
if %build%==1 pyinstaller --onefile --windowed --icon=favicon.ico app.py if %build%==1 pyinstaller --onefile --windowed --icon=favicon.ico app.py

View File

@ -1,7 +1,14 @@
import numpy as np import numpy as np
from scipy.signal import find_peaks, correlate from scipy.signal import find_peaks, correlate
from scipy.optimize import curve_fit
from memdb import mem from memdb import mem
def minmax(data):
return np.min(data), np.max(data)
def exp_func(x, x0, a, y0, tau): # NOTE: X is not something we pass in 🤦‍♂️
return y0 + a*np.exp(-(x-x0)/tau)
def spaced_groups( def spaced_groups(
x_data: np.array, x_data: np.array,
y_data: np.array, y_data: np.array,
@ -16,7 +23,9 @@ def spaced_groups(
""" """
Use SpacedGroups algo to separate groups Use SpacedGroups algo to separate groups
Returns 2D array of raw data; every other group Returns
-------
2D array of raw data; every other group
""" """
# Helpers # Helpers
@ -107,11 +116,44 @@ def spaced_groups(
return groups_raw return groups_raw
def vthreshold(
x_data: np.array,
y_data: np.array,
v_data: np.array,
vmin: float,
vmax: float,
mirrored: bool=True,
start=None,
end=None
):
"""
Voltage-threshold grouping algorithm
Returns
-------
A `list` of all peak groups
"""
# Helpers
def t2i(t):
delta_t = abs(x_data[0] - t)
timestep = abs(x_data[1] - x_data[0])
return int(delta_t / timestep)
def t2i_range(t):
timestep = abs(x_data[1] - x_data[0])
return int(t / timestep)
groups_raw = []
return groups_raw
def correlate_groups(groups_raw): def correlate_groups(groups_raw):
""" """
Overlay groups using `scipy.correlate`. Overlay groups using `scipy.correlate`.
Returns 2D array of overlayed groups Returns
-------
2D array of overlayed groups
""" """
# Compare groups (scipy correlate) # Compare groups (scipy correlate)
@ -135,19 +177,121 @@ def correlate_groups(groups_raw):
return groups_adjusted return groups_adjusted
def add_peaks_only(groups_adjusted: list):
def unequal_add_truncation(a,b): # Instead of padding with 0, truncate
if len(a) < len(b):
c = b.copy()
c = c[:len(a)]
c += a
else:
c = a.copy()
c = c[:len(b)]
c += b
return(c)
added_peaks = np.array(groups_adjusted[0])
for g in groups_adjusted[1:]:
g1 = np.array(g)
g0 = added_peaks
added_peaks = unequal_add_truncation(g0, g1)
return added_peaks
def isolate_peaks( def isolate_peaks(
peak_width: int,
groups_adjusted: list, groups_adjusted: list,
peak_minheight: int, peak_width: int,
peak_prominence: int, sma_denom: int,
sma_denom: int peak_minheight: int = None,
peak_prominence: int = None,
shift_over: int = 0
): ):
def unequal_add(a,b): # NOTE: See https://www.delftstack.com/howto/numpy/vector-addition-in-numpy/
if len(a) < len(b):
c = b.copy()
c[:len(a)] += a
else:
c = a.copy()
c[:len(b)] += b
return(c)
def unequal_add_truncation(a,b): # Instead of padding with 0, truncate
if len(a) < len(b):
c = b.copy()
c = c[:len(a)]
c += a
else:
c = a.copy()
c = c[:len(b)]
c += b
return(c)
def moving_average(x, w): def moving_average(x, w):
return np.convolve(x, np.ones(w), 'valid') / w return np.convolve(x, np.ones(w), 'valid') / w
group_peaks = [] added_peaks = np.array(groups_adjusted[0])
for g in groups_adjusted[1:]:
g1 = np.array(g)
g0 = added_peaks
added_peaks = unequal_add_truncation(g0, g1)
added_peaks_av = moving_average(added_peaks, sma_denom)
peak_indices = find_peaks(added_peaks_av, height=peak_minheight, prominence=peak_prominence, distance=peak_width/2)[0] # Get indices of all peaks
isolated_peaks = []
delta = peak_width/2
for g in groups_adjusted: for g in groups_adjusted:
y_data_av = moving_average(g, sma_denom) peaks_cut = []
peak_indices = find_peaks(y_data_av, height=peak_minheight, prominence=peak_prominence) # Get indices of all peaks for i in peak_indices:
group_peaks.append(peak_indices) peak = g[int(i-delta+shift_over):int(i+delta+shift_over)]
peaks_cut.append(peak)
isolated_peaks.append(peaks_cut)
return added_peaks, peak_indices, isolated_peaks
def fit_peaks(
isolated_peaks: list,
peak_indices: list,
min_peak_height: float,
min_peak_prominence: float,
moving_avg_size: int,
a: float,
tau: float,
y0: float,
shift_over: int,
use_advanced: bool
):
print(f'{use_advanced=}')
"""
Returns
-------
Peak fit equations. Linked to `mem['isolated_peaks']`.
"""
params_guess = (0.0000, a, y0, tau)
equations = []
for peaks_cut in isolated_peaks:
row = []
for peak_data in peaks_cut:
x_data = np.arange(len(peak_data)) # just placeholder indices
if not use_advanced:
peak_index = np.argmax(peak_data, axis=0)
else:
peak_index = find_peaks(peak_data, height=min_peak_height, prominence=min_peak_prominence)[0][0]
# print(peak_index)
params_guess = (peak_index+shift_over, a, y0, tau)
x_data_target = x_data[peak_index+shift_over:]
peak_data_target = peak_data[peak_index+shift_over:]
# popt, pcov = curve_fit(exp_func, x_data_target, peak_data_target, bounds=([-np.inf, 0.0, -np.inf, 0.0], np.inf))
popt, pcov = curve_fit(exp_func, x_data_target, peak_data_target, bounds=([-np.inf, 0.0, -np.inf, 0.0], np.inf), p0=params_guess, maxfev=10000000)
row.append({'popt': popt, 'pcov': pcov})
equations.append(row)
return equations # list linked with isolated_peaks

View File

@ -1,4 +1,5 @@
from sqlitedict import SqliteDict from sqlitedict import SqliteDict
from varname.core import nameof
class ModSqliteDict(SqliteDict): class ModSqliteDict(SqliteDict):
def __init__(self): def __init__(self):
@ -7,4 +8,8 @@ class ModSqliteDict(SqliteDict):
self.filename = ':memory:' self.filename = ':memory:'
super().__init__() super().__init__()
def set_key(self, item):
name = nameof(item)
self[name] = item
mem = ModSqliteDict() mem = ModSqliteDict()

View File

@ -3,3 +3,5 @@ pandas
numpy numpy
pyinstaller==4.4 pyinstaller==4.4
sqlitedict sqlitedict
scipy
varname

View File

@ -6,128 +6,68 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1091</width> <width>1343</width>
<height>611</height> <height>907</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>CRDS Scanalyzer</string> <string>CRDS Scanalyzer</string>
</property> </property>
<property name="styleSheet">
<string notr="true">QGroupBox: {font-style: bold; font-size: 8px }</string>
</property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<widget class="QTabWidget" name="graph_tabs"> <layout class="QGridLayout" name="gridLayout">
<property name="geometry"> <item row="0" column="0">
<rect> <widget class="QScrollArea" name="scrollArea">
<x>260</x> <property name="minimumSize">
<y>0</y> <size>
<width>821</width> <width>280</width>
<height>561</height> <height>0</height>
</rect> </size>
</property> </property>
<property name="font"> <property name="maximumSize">
<font> <size>
<pointsize>8</pointsize> <width>280</width>
</font> <height>16777215</height>
</size>
</property> </property>
<property name="currentIndex"> <property name="frameShape">
<number>0</number> <enum>QFrame::StyledPanel</enum>
</property> </property>
<widget class="QWidget" name="raw_data"> <property name="widgetResizable">
<attribute name="title"> <bool>true</bool>
<string>Raw Data</string>
</attribute>
<widget class="RawDataGraph" name="raw_data_graph" native="true">
<property name="geometry">
<rect>
<x>-1</x>
<y>-1</y>
<width>811</width>
<height>531</height>
</rect>
</property> </property>
</widget> <widget class="QWidget" name="scrollAreaWidgetContents">
</widget>
<widget class="QWidget" name="groups">
<attribute name="title">
<string>Groups</string>
</attribute>
<widget class="PeaksGraph" name="groups_graph" native="true">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>811</width> <width>278</width>
<height>531</height> <height>846</height>
</rect> </rect>
</property> </property>
</widget> <layout class="QVBoxLayout" name="verticalLayout_2">
</widget> <item>
<widget class="QWidget" name="time_constant">
<attribute name="title">
<string>Time Constant</string>
</attribute>
<widget class="TimeConstantGraph" name="tau_graph" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>601</width>
<height>531</height>
</rect>
</property>
</widget>
<widget class="QGroupBox" name="groupBox_3">
<property name="geometry">
<rect>
<x>600</x>
<y>10</y>
<width>211</width>
<height>161</height>
</rect>
</property>
<property name="title">
<string>Time constant output</string>
</property>
<widget class="QTextBrowser" name="tau_output">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>191</width>
<height>131</height>
</rect>
</property>
<property name="html">
<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;
&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;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
&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;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</widget>
</widget>
</widget>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="geometry"> <property name="minimumSize">
<rect> <size>
<x>10</x> <width>239</width>
<y>10</y> <height>266</height>
<width>241</width> </size>
<height>231</height> </property>
</rect> <property name="maximumSize">
<size>
<width>239</width>
<height>258</height>
</size>
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<pointsize>8</pointsize> <pointsize>8</pointsize>
<weight>75</weight> <weight>50</weight>
<bold>true</bold> <bold>false</bold>
</font> </font>
</property> </property>
<property name="title"> <property name="title">
<string>Grouping Config</string> <string>GROUPING CONFIG</string>
</property> </property>
<widget class="QStackedWidget" name="grouping_config_area"> <widget class="QStackedWidget" name="grouping_config_area">
<property name="geometry"> <property name="geometry">
@ -135,7 +75,7 @@ p, li { white-space: pre-wrap; }
<x>10</x> <x>10</x>
<y>50</y> <y>50</y>
<width>221</width> <width>221</width>
<height>91</height> <height>101</height>
</rect> </rect>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
@ -155,7 +95,7 @@ p, li { white-space: pre-wrap; }
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Minimum voltage out</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Minimum voltage out&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -164,11 +104,34 @@ p, li { white-space: pre-wrap; }
<property name="decimals"> <property name="decimals">
<number>6</number> <number>6</number>
</property> </property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
<property name="value"> <property name="value">
<double>0.001000000000000</double> <double>0.001000000000000</double>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="spin_max_voltage">
<property name="decimals">
<number>6</number>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
<property name="value">
<double>0.009000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Maximum voltage out</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>
@ -179,7 +142,7 @@ p, li { white-space: pre-wrap; }
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>221</width> <width>221</width>
<height>103</height> <height>108</height>
</rect> </rect>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
@ -195,6 +158,9 @@ p, li { white-space: pre-wrap; }
<property name="decimals"> <property name="decimals">
<number>6</number> <number>6</number>
</property> </property>
<property name="singleStep">
<double>0.000100000000000</double>
</property>
<property name="value"> <property name="value">
<double>0.000600000000000</double> <double>0.000600000000000</double>
</property> </property>
@ -212,6 +178,12 @@ p, li { white-space: pre-wrap; }
<property name="decimals"> <property name="decimals">
<number>6</number> <number>6</number>
</property> </property>
<property name="minimum">
<double>-99.989999999999995</double>
</property>
<property name="singleStep">
<double>0.000100000000000</double>
</property>
<property name="value"> <property name="value">
<double>0.000400000000000</double> <double>0.000400000000000</double>
</property> </property>
@ -229,6 +201,9 @@ p, li { white-space: pre-wrap; }
<property name="decimals"> <property name="decimals">
<number>6</number> <number>6</number>
</property> </property>
<property name="singleStep">
<double>0.000100000000000</double>
</property>
<property name="value"> <property name="value">
<double>0.001200000000000</double> <double>0.001200000000000</double>
</property> </property>
@ -258,7 +233,7 @@ p, li { white-space: pre-wrap; }
<x>10</x> <x>10</x>
<y>20</y> <y>20</y>
<width>221</width> <width>221</width>
<height>19</height> <height>21</height>
</rect> </rect>
</property> </property>
<item> <item>
@ -276,7 +251,7 @@ p, li { white-space: pre-wrap; }
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>10</x>
<y>200</y> <y>210</y>
<width>221</width> <width>221</width>
<height>20</height> <height>20</height>
</rect> </rect>
@ -292,9 +267,9 @@ p, li { white-space: pre-wrap; }
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>10</x>
<y>150</y> <y>160</y>
<width>233</width> <width>230</width>
<height>51</height> <height>52</height>
</rect> </rect>
</property> </property>
<layout class="QFormLayout" name="formLayout_5"> <layout class="QFormLayout" name="formLayout_5">
@ -340,12 +315,11 @@ p, li { white-space: pre-wrap; }
</item> </item>
</layout> </layout>
</widget> </widget>
</widget>
<widget class="QPushButton" name="correlate_button"> <widget class="QPushButton" name="correlate_button">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>50</x> <x>40</x>
<y>250</y> <y>230</y>
<width>151</width> <width>151</width>
<height>28</height> <height>28</height>
</rect> </rect>
@ -362,24 +336,194 @@ p, li { white-space: pre-wrap; }
<string>Correlate</string> <string>Correlate</string>
</property> </property>
</widget> </widget>
<widget class="QGroupBox" name="groupBox_2"> </widget>
<property name="geometry"> </item>
<rect> <item>
<x>10</x> <widget class="QGroupBox" name="groupBox_4">
<y>350</y> <property name="minimumSize">
<width>241</width> <size>
<height>181</height> <width>239</width>
</rect> <height>165</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>239</width>
<height>165</height>
</size>
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<pointsize>8</pointsize> <pointsize>8</pointsize>
<weight>75</weight> <weight>50</weight>
<bold>true</bold> <bold>false</bold>
</font> </font>
</property> </property>
<property name="title"> <property name="title">
<string>Initial Fit Config</string> <string>PEAK ISOLATION CONFIG (timescale: ticks)</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<widget class="QWidget" name="formLayoutWidget_2">
<property name="geometry">
<rect>
<x>9</x>
<y>20</y>
<width>221</width>
<height>108</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Overlapped Peak Width</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spin_peak_overlap">
<property name="maximum">
<number>100000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="value">
<number>2000</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Min added peak height</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Added peak prominence</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="spin_min_peak_height_added">
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-99.989999999999995</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="spin_peak_prominence_added">
<property name="decimals">
<number>6</number>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.010000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Shift over cut-out zone</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spin_shift_over">
<property name="maximum">
<number>100000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="value">
<number>200</number>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QPushButton" name="isolate_button">
<property name="geometry">
<rect>
<x>100</x>
<y>130</y>
<width>131</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>-1</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">* {font-size: 13px}</string>
</property>
<property name="text">
<string>Add + Isolate</string>
</property>
</widget>
<widget class="QPushButton" name="peak_add_button">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<width>81</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>-1</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">* {font-size: 13px}</string>
</property>
<property name="text">
<string>Add</string>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="minimumSize">
<size>
<width>239</width>
<height>351</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>239</width>
<height>351</height>
</size>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="title">
<string>INITIAL FIT CONFIG</string>
</property> </property>
<widget class="QWidget" name="formLayoutWidget_3"> <widget class="QWidget" name="formLayoutWidget_3">
<property name="geometry"> <property name="geometry">
@ -387,7 +531,7 @@ p, li { white-space: pre-wrap; }
<x>10</x> <x>10</x>
<y>90</y> <y>90</y>
<width>221</width> <width>221</width>
<height>83</height> <height>106</height>
</rect> </rect>
</property> </property>
<layout class="QFormLayout" name="formLayout_3"> <layout class="QFormLayout" name="formLayout_3">
@ -399,9 +543,15 @@ p, li { white-space: pre-wrap; }
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_2"> <widget class="QDoubleSpinBox" name="spin_var_tau">
<property name="suffix">
<string/>
</property>
<property name="decimals"> <property name="decimals">
<number>6</number> <number>8</number>
</property>
<property name="value">
<double>0.000002000000000</double>
</property> </property>
</widget> </widget>
</item> </item>
@ -413,7 +563,7 @@ p, li { white-space: pre-wrap; }
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox"> <widget class="QDoubleSpinBox" name="spin_var_y0">
<property name="decimals"> <property name="decimals">
<number>6</number> <number>6</number>
</property> </property>
@ -427,10 +577,30 @@ p, li { white-space: pre-wrap; }
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_3"> <widget class="QDoubleSpinBox" name="spin_var_a">
<property name="decimals"> <property name="decimals">
<number>6</number> <number>6</number>
</property> </property>
<property name="value">
<double>1.000500000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Shift over fit start</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spin_shift_over_fit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="value">
<number>40</number>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -445,12 +615,11 @@ p, li { white-space: pre-wrap; }
</rect> </rect>
</property> </property>
</widget> </widget>
</widget>
<widget class="QPushButton" name="fit_button"> <widget class="QPushButton" name="fit_button">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>60</x> <x>40</x>
<y>540</y> <y>200</y>
<width>151</width> <width>151</width>
<height>28</height> <height>28</height>
</rect> </rect>
@ -467,68 +636,285 @@ p, li { white-space: pre-wrap; }
<string>Fit</string> <string>Fit</string>
</property> </property>
</widget> </widget>
<widget class="QGroupBox" name="groupBox_4"> <widget class="QWidget" name="formLayoutWidget_6">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>10</x>
<y>290</y> <y>260</y>
<width>241</width>
<height>51</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="title">
<string>Peak Isolation Config (timescale: ticks)</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<widget class="QWidget" name="formLayoutWidget_2">
<property name="geometry">
<rect>
<x>9</x>
<y>20</y>
<width>221</width> <width>221</width>
<height>25</height> <height>81</height>
</rect> </rect>
</property> </property>
<layout class="QFormLayout" name="formLayout_2"> <layout class="QFormLayout" name="formLayout_6">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_11">
<property name="text"> <property name="text">
<string>Overlapped Peak Range</string> <string>Min peak height</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Min peak prominence</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Moving average size</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QSpinBox" name="spin_peak_overlap"> <widget class="QDoubleSpinBox" name="spin_min_peakheight_2">
<property name="maximum"> <property name="enabled">
<number>100000</number> <bool>false</bool>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-99.989999999999995</double>
</property> </property>
<property name="singleStep"> <property name="singleStep">
<number>100</number> <double>0.000100000000000</double>
</property> </property>
<property name="value"> <property name="value">
<number>5000</number> <double>0.000400000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="spin_min_peakprominence_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="singleStep">
<double>0.000100000000000</double>
</property>
<property name="value">
<double>0.001200000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spin_moving_average_denom_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="value">
<number>20</number>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QCheckBox" name="check_advanced_peak_detection">
<property name="geometry">
<rect>
<x>10</x>
<y>230</y>
<width>151</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Advanced peak detection</string>
</property>
</widget> </widget>
</widget> </widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="0" column="1">
<widget class="QTabWidget" name="graph_tabs">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="raw_data">
<attribute name="title">
<string>Raw Data</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="RawDataGraph" name="raw_data_graph" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="voltage">
<attribute name="title">
<string>Voltage Data</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="VoltageGraph" name="voltage_graph" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="groups">
<attribute name="title">
<string>Groups (Overlapped)</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="PeaksGraph" name="groups_graph" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="added_peaks">
<attribute name="title">
<string>Added Peaks</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="AddedPeaksGraph" name="added_peaks_graph" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="peak_fits">
<attribute name="title">
<string>Peak Fits</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="FitsGraph" name="peak_fits_graph" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="time_constant">
<attribute name="title">
<string>Time Constant</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<widget class="TimeConstantGraph" name="tau_graph" native="true"/>
</item>
<item row="0" column="1">
<widget class="QScrollArea" name="scrollArea_2">
<property name="minimumSize">
<size>
<width>281</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>281</width>
<height>16777215</height>
</size>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>279</width>
<height>802</height>
</rect>
</property>
<widget class="QGroupBox" name="groupBox_3">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>261</width>
<height>351</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>211</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>MS Shell Dlg 2</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="title">
<string>Time constant output</string>
</property>
<widget class="QTextBrowser" name="tau_output">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>241</width>
<height>261</height>
</rect>
</property>
<property name="html">
<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;
&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;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
&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-size:7.8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QPushButton" name="copyResultsButton">
<property name="geometry">
<rect>
<x>20</x>
<y>290</y>
<width>221</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Copy</string>
</property>
</widget>
<widget class="QPushButton" name="exportPlaintextButton">
<property name="geometry">
<rect>
<x>20</x>
<y>320</y>
<width>221</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Export Text</string>
</property>
</widget>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1091</width> <width>1343</width>
<height>21</height> <height>21</height>
</rect> </rect>
</property> </property>
@ -635,6 +1021,24 @@ p, li { white-space: pre-wrap; }
<header>widgets</header> <header>widgets</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>FitsGraph</class>
<extends>QWidget</extends>
<header>widgets</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AddedPeaksGraph</class>
<extends>QWidget</extends>
<header>widgets</header>
<container>1</container>
</customwidget>
<customwidget>
<class>VoltageGraph</class>
<extends>QWidget</extends>
<header>widgets</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -1,17 +1,32 @@
import numpy as np
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
import matplotlib import matplotlib
matplotlib.use('Qt5Agg') matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure from matplotlib.figure import Figure
from memdb import mem from memdb import mem
from crds_calc import exp_func
class MplCanvas(FigureCanvasQTAgg): class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100): def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi) fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111) self.axes = fig.add_subplot(111)
fig.tight_layout() fig.tight_layout()
self.fig_width = width
self.fig_height = height
self.fig_dpi = dpi
super(MplCanvas, self).__init__(fig) super(MplCanvas, self).__init__(fig)
def reset(self):
self.axes.remove()
self.axes = self.figure.add_subplot(111)
self.figure.tight_layout()
# fig = Figure(figsize=(self.fig_width, self.fig_height), dpi=self.fig_dpi)
# self.axes = fig.add_subplot(111)
# self.figure.clear()
# self.figure.axes.remove()
# fig.tight_layout()
class BaseGraph(QtWidgets.QWidget): class BaseGraph(QtWidgets.QWidget):
""" """
Widget with embedded matplotlib graph & navigation toolbar Widget with embedded matplotlib graph & navigation toolbar
@ -36,22 +51,82 @@ class BaseGraph(QtWidgets.QWidget):
self.canv.axes.plot(mem['x_data'], mem['y_data']) self.canv.axes.plot(mem['x_data'], mem['y_data'])
def plot(self): def plot(self):
try:
self.canv.axes.clear() self.canv.axes.clear()
except AttributeError:
pass
self.plot_data() self.plot_data()
self.canv.draw() self.canv.draw()
print("attempted plot")
def clear(self): def clear(self):
self.canv.axes.clear() self.canv.axes.clear()
self.canv.draw()
class RawDataGraph(BaseGraph): class RawDataGraph(BaseGraph):
pass pass
class VoltageGraph(BaseGraph):
def plot_data(self):
if not mem['v_data'][0] == None:
self.canv.axes.plot(mem['x_data'], mem['v_data'], color='orange')
class PeaksGraph(BaseGraph): class PeaksGraph(BaseGraph):
def plot_data(self): def plot_data(self):
for i in mem['groups_correlated']: for i in mem['groups_correlated']:
self.canv.axes.plot(i) self.canv.axes.plot(i)
class AddedPeaksGraph(BaseGraph):
params = {
'peak_width': None,
'shift_over': None
}
def set_params(self, peak_width, shift_over=0):
self.params['peak_width'] = peak_width
self.params['shift_over'] = shift_over
def plot_data(self):
self.canv.axes.plot(mem['added_peaks'], color='green') # plot added peaks
if not self.params['peak_width'] == None: # plot peak indices
for i in mem['peak_indices']:
self.canv.axes.axvspan(int(i-self.params['peak_width']/2+self.params['shift_over']), int(i+self.params['peak_width']/2+self.params['shift_over']), color='red', alpha=0.4)
class FitsGraph(BaseGraph):
def __init__(self, x):
super(FitsGraph, self).__init__(x)
def plot_data(self):
for g_i in range(len(mem['isolated_peaks'])):
for p_i in range(len(mem['isolated_peaks'][g_i])):
peak = mem['isolated_peaks'][g_i][p_i]
x_data = np.arange(len(peak))
popt = mem['fit_equations'][g_i][p_i]['popt']
self.canv.axes.plot(peak)
self.canv.axes.plot(x_data, exp_func(x_data, *popt), color='red')
# def plot_data(self):
# try:
# self.canv.axes.remove()
# except AttributeError:
# pass
# subplots_stacked = len(mem['isolated_peaks'][0]) # should all be same length
# axes = self.canv.figure.subplots(subplots_stacked, 1, sharex=True)
# for g_i in range(len(mem['isolated_peaks'])):
# for p_i in range(subplots_stacked):
# peak = mem['isolated_peaks'][g_i][p_i]
# axes[p_i].plot(peak)
# x_data = np.arange(len(peak))
# popt = mem['fit_equations'][g_i][p_i]['popt']
# axes[p_i].plot(x_data, exp_func(x_data, *popt), color='red')
# for ax in axs.flat:
# ax.set(xlabel='x-label', ylabel='y-label')
class TimeConstantGraph(BaseGraph): class TimeConstantGraph(BaseGraph):
pass # no modifications thus far pass