diff --git a/settings/workouts.csv b/settings/workouts.csv index 7afcf49..7cea173 100644 --- a/settings/workouts.csv +++ b/settings/workouts.csv @@ -2,3 +2,4 @@ Name;Description;Sets;Set Pause;Repetitions;Repetition Active;Repetition Pause Critical Force Test;The classical finger flexor critical force test (ff-CF). 7 sec on, 3 sec off for 4 min in total. Pull as hard as you can.;1;5;24;7;3 Max Isometric Finger Strength;Test the maximal isometric finger strength (MIFS). Hang for 5 sec with 120 sec pause (3 sets).;3;120;1;5;0 Warm-up;This can be used as warm-up for the critical force test. Switch hands after each repetition (10 min).;10;35;2;10;5 +Speed test;This is a test;2;120;1;5;0 \ No newline at end of file diff --git a/ui/CalibrationGui.py b/ui/CalibrationGui.py index 54dfd4f..7a4133c 100644 --- a/ui/CalibrationGui.py +++ b/ui/CalibrationGui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'CalibrationGui.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.6.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -119,7 +119,7 @@ def setupUi(self, Form): self.verticalLayout.addWidget(self.line_3) - self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) + self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) self.verticalLayout.addItem(self.verticalSpacer) diff --git a/ui/CompareresultGui.py b/ui/CompareresultGui.py index fd001e5..5831d93 100644 --- a/ui/CompareresultGui.py +++ b/ui/CompareresultGui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'CompareresultGui.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.6.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ diff --git a/ui/DataGui.py b/ui/DataGui.py index b694570..f0d2898 100644 --- a/ui/DataGui.py +++ b/ui/DataGui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'DataGui.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.6.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ diff --git a/ui/MeasurementCtrl.py b/ui/MeasurementCtrl.py index 2ea76c4..c50734b 100644 --- a/ui/MeasurementCtrl.py +++ b/ui/MeasurementCtrl.py @@ -20,6 +20,8 @@ from PySide6.QtWidgets import QWidget from PySide6.QtGui import QAction +from PySide6.QtWidgets import QDialog +from PySide6.QtCore import Qt import pyqtgraph as pg from psychopy import prefs @@ -32,10 +34,12 @@ import time, datetime from ui.MeasurementGui import Ui_Form +from ui.SpeedtestGui import Ui_Dialog from util.repeatedTimer import RepeatedTimer from util.params import Params from util.workouts import WorkoutHandler from util.criticalForce import computeRepetitionMean, computeCriticalForceAndWPrime, computeMaxForce +from util.Speed_test import analyse_measurements, acceleration, RFD, computeSchnellkraftParameter from util.preferencesHandling import loadPreferences class MeasurementCtrl(QWidget): @@ -55,23 +59,27 @@ def __init__(self, weightSensor): self.delayInSamples = round(pref['delayCompensation']/1000 * self.fsMeas) #======================================== - # Init class members + # Init class members1,5 + #======================================== self.running = False self.lookupTable = 0 self.countdown = 0 - self.numMeasSamples = 0 + self.numMeasSamples_1 = 0 + self.numMeasSamples_2 = 0 self.numSets = 0 self.numRepsPerSet = 0 self.measCnt = 0 self.secCnt = 0 self.repCnt = 0 self.setCnt = 1 - self.currentWeight = 0 - self.tare = 0 + self.currentWeight_1 = 0 + self.tare_1 = 0 + self.tare_2 = 0 # Data, that will be stored in result file - self.measDataKg = np.asarray([]) + self.measDataKg_1 = np.asarray([]) + self.measDataKg_2 = np.asarray([]) self.timestamp = 'unknown' self.bodyWeight = form.bodyWeightSpinBox.value() self.criticalForce = 0 @@ -83,7 +91,8 @@ def __init__(self, weightSensor): #======================================== form.startButton.pressed.connect(self.onStartMeasurement) form.stopButton.pressed.connect(self.onStopMeasurement) - form.tareButton.pressed.connect(self.onTareButtonClicked) + form.tareButton_1.pressed.connect(self.onTareButtonClicked_1) + form.tareButton_2.pressed.connect(self.onTareButtonClicked_2) form.bodyWeightSpinBox.valueChanged.connect(self.onBodyWeightChanged) # Action, triggered when measurement is finished @@ -97,11 +106,13 @@ def __init__(self, weightSensor): self.workoutComboBox = form.workoutComboBox self.workoutLabel = form.workoutLabel self.weightSpinBox = form.bodyWeightSpinBox - self.graphicsView = form.graphicsView - self.tareButton = form.tareButton - self.tareLabel = form.tareLabel + self.graphicsView_1 = form.graphicsView + self.tareButton_1 = form.tareButton_1 + self.tareButton_2 = form.tareButton_2 + self.tareLabel_1 = form.tareLabel_1 + self.tareLabel_2 = form.tareLabel_2 - self.graphicsView.setBackground('w') + self.graphicsView_1.setBackground('w') #======================================== # Workout handling @@ -131,15 +142,28 @@ def __init__(self, weightSensor): self.OLD_TIME = time.time() ################################################## + #======================================= + #Initialising speed test window + #======================================= + + self.speedtestWindow = QDialog(self) + self.speedtestUi = Ui_Dialog() + self.speedtestUi.setupUi(self.speedtestWindow) + self.speedtestWindow.setWindowFlags( Qt.Window | Qt.WindowMinimizeButtonHint |Qt.WindowMaximizeButtonHint |Qt.WindowCloseButtonHint) + self.speedtestWindow.setWindowTitle("Speedtest-Auswertung") + def onStartMeasurement(self): if not self.running: self.tareTimer.stop() - self.numMeasSamples = len(self.lookupTable) * self.fsMeas - self.measDataKg = np.zeros(self.numMeasSamples) + self.numMeasSamples_1 = len(self.lookupTable) * self.fsMeas + self.numMeasSamples_2 = len(self.lookupTable) * self.fsMeas + self.measDataKg_1 = np.zeros(self.numMeasSamples_1) + self.measDataKg_2 = np.zeros(self.numMeasSamples_2) self.workoutComboBox.setEnabled(False) self.weightSpinBox.setEnabled(False) - self.tareButton.setEnabled(False) + self.tareButton_1.setEnabled(False) + self.tareButton_2.setEnabled(False) self.measurementTimer = RepeatedTimer(1/self.fsMeas, self.onMeasurementCallback) @@ -160,7 +184,8 @@ def onStopMeasurement(self): self.workoutComboBox.setEnabled(True) self.weightSpinBox.setEnabled(True) - self.tareButton.setEnabled(True) + self.tareButton_1.setEnabled(True) + self.tareButton_2.setEnabled(True) self.tareTimer = RepeatedTimer(1/self.fsMeas, self.onTareVisualization) @@ -170,14 +195,17 @@ def onStopMeasurement(self): def onMeasurementCallback(self): secCnt = self.secCnt - if self.measCnt >= self.numMeasSamples: + if self.measCnt >= self.numMeasSamples_1: # Measurement finished self.measFinished.trigger() else: # Read value from sensor and save it to array - valueKg = self.weightSensor.getValueInKg() - self.tare - self.tareLabel.setText(str(np.around(valueKg, decimals=2)) + 'kg') - self.measDataKg[self.measCnt] = valueKg + valueKg_1 = self.weightSensor.getValueInKg_1() - self.tare_1 + valueKg_2 = self.weightSensor.getValueInKg_2() - self.tare_2 + self.tareLabel_1.setText(str(np.around(valueKg_1, decimals=2)) + 'kg') + self.tareLabel_2.setText(str(np.around(valueKg_2, decimals=2)) + 'kg') + self.measDataKg_1[self.measCnt] = valueKg_1 + self.measDataKg_2[self.measCnt] = valueKg_2 # Workout timer handling if self.measCnt % self.fsMeas == 0: @@ -222,12 +250,17 @@ def onMeasurementCallback(self): self.measCnt += 1 def onTareVisualization(self): - self.currentWeight = self.weightSensor.getValueInKg() - weightString = str(np.around(self.currentWeight - self.tare, decimals=2)) - self.tareLabel.setText(weightString + 'kg') - - def onTareButtonClicked(self): - self.tare = self.currentWeight + self.currentWeight_1 = self.weightSensor.getValueInKg_1() + self.currentWeight_2 = self.weightSensor.getValueInKg_2() + weightString_1 = str(np.around(self.currentWeight_1 - self.tare_1, decimals=2)) + weightString_2 = str(np.around(self.currentWeight_2 - self.tare_2, decimals=2)) + self.tareLabel_1.setText(weightString_1 + 'kg') + self.tareLabel_2.setText(weightString_2 + 'kg') + + def onTareButtonClicked_1(self): + self.tare_1 = self.currentWeight_1 + def onTareButtonClicked_2(self): + self.tare_2 = self.currentWeight_2 def onWorkoutChanged(self, id): self.workoutDescriptionLabel.setText(self.workoutHandler.getWorkoutDescription(id)) @@ -237,7 +270,7 @@ def onWorkoutChanged(self, id): def onBodyWeightChanged(self, value): self.bodyWeight = value - if len(self.measDataKg) > 0: + if len(self.measDataKg_1) > 0: self.computeResultAndPlot() def onCloseApplication(self): @@ -249,27 +282,29 @@ def onCloseApplication(self): #======================================== def computeResultAndPlot(self): # Compute result (force as percentage of body weight) - measDataPercentBw = self.measDataKg / self.bodyWeight * 100 - + measDataPercentBw_1 = self.measDataKg_1 / self.bodyWeight * 100 + measDataPercentBw_2 = self.measDataKg_2 / self.bodyWeight * 100 # Compensate delay between audio clicks and measurement - measDataPercentBw = np.roll(measDataPercentBw, -self.delayInSamples) - measDataPercentBw[-self.delayInSamples:] = 0 + measDataPercentBw_1 = np.roll(measDataPercentBw_1, -self.delayInSamples) + measDataPercentBw_2 = np.roll(measDataPercentBw_2, -self.delayInSamples) + measDataPercentBw_1[-self.delayInSamples:] = 0 + measDataPercentBw_2[-self.delayInSamples:] = 0 # Compute the mean force during repetition active times - repMean = computeRepetitionMean(measDataPercentBw, self.lookupTable, self.fsMeas) + repMean = computeRepetitionMean(measDataPercentBw_1, self.lookupTable, self.fsMeas) # Prepare plot - t = np.linspace(0, len(measDataPercentBw) / self.fsMeas, len(measDataPercentBw)) - self.graphicsView.clear() - self.graphicsView.addLegend().anchor(itemPos=(1,0), parentPos=(1,0), offset=(-10,10)) + t = np.linspace(0, len(measDataPercentBw_1) / self.fsMeas, len(measDataPercentBw_1)) + self.graphicsView_1.clear() + self.graphicsView_1.addLegend().anchor(itemPos=(1,0), parentPos=(1,0), offset=(-10,10)) # Plot raw measurement data - pen = pg.mkPen(color=(150,150,150), width=2) - self.graphicsView.plot(t, measDataPercentBw, name="Raw Data", pen=pen) + pen = pg.mkPen(color=(150,150,150), width=2) + self.graphicsView_1.plot(t, measDataPercentBw_1, name="Raw Data_1", pen=pen) # Plot mean of each repetition block pen = pg.mkPen(color=(80,80,80), width=2) - self.graphicsView.plot(t, repMean, name="Rep. Mean", pen=pen) + self.graphicsView_1.plot(t, repMean, name="Rep. Mean", pen=pen) # Compute the critical force, if necessary cf = 0 @@ -282,16 +317,57 @@ def computeResultAndPlot(self): W = np.around(W, decimals = 2) pen = pg.mkPen(color=(0,180,0), width=2) - self.graphicsView.plot([t[0],t[-1]], [cf,cf], name="CF = " + str(cf) + " %BW | W\' = " + str(W) + " %BWs", pen=pen) + self.graphicsView_1.plot([t[0],t[-1]], [cf,cf], name="CF = " + str(cf) + " %BW | W\' = " + str(W) + " %BWs", pen=pen) + + #=========================== + #speed test + #=========================== + + workout_name = self.workoutHandler.getWorkoutName(self.selectedWorkoutId) + + if workout_name == 'Speed test': + + pen = pg.mkPen(color=(255, 255, 255), width=2) + + if not self.speedtestWindow.isVisible(): + self.speedtestWindow.show() + + self.speedtestUi.graphicsView.clear() + self.speedtestUi.graphicsView_2.clear() + + #showing new Data + self.speedtestUi.graphicsView_2.plot(t, measDataPercentBw_1, name="Raw Data 1", pen=pen) + self.speedtestUi.graphicsView.plot(t, measDataPercentBw_2, name="Raw Data 2", pen=pen) + + self.speedtestUi.graphicsView_2.addLegend().anchor(itemPos=(1,0), parentPos=(1,0), offset=(-10,10)) + self.speedtestUi.graphicsView.addLegend().anchor(itemPos=(1,0), parentPos=(1,0), offset=(-10,10)) + + # Speedtest parameters calculating + peaks1, tp1, starts1, tsp1, acc_1, rfd_1 = computeSchnellkraftParameter(measDataPercentBw_1, self.lookupTable, self.fsMeas, self.bodyWeight) + peaks2, tp2, starts2, tsp2, acc_2, rfd_2 = computeSchnellkraftParameter(measDataPercentBw_2, self.lookupTable, self.fsMeas, self.bodyWeight) + + self.speedtestUi.graphicsView_2.plot(tp1, peaks1,pen=None, symbol='o', symbolBrush=(0,0,255),name="max Power 1 ="+ str(np.round(peaks1, 2)) + " kg") + self.speedtestUi.graphicsView.plot(tp2, peaks2,pen=None, symbol='o', symbolBrush=(0,0,255),name="max Power 2 ="+ str(np.round(peaks2,2)) + " kg") + + self.speedtestUi.graphicsView_2.plot(tsp1, starts1,pen=None, symbol='o', symbolBrush=(0,255,0),name="Start pullingpoint 1 ="+ str(np.round(starts1, 2)) + " %") + self.speedtestUi.graphicsView.plot(tsp2, starts2,pen=None, symbol='o', symbolBrush=(0,255,0),name="Start pullingpoint 2 ="+ str(np.round(starts2, 2)) + " %") + + self.speedtestUi.graphicsView_2.plot([0], [0], pen=None, symbol=None, name="Max. Acceleration = " + str(acc_1) + " m/s²") + self.speedtestUi.graphicsView.plot([0], [0], pen=None, symbol=None, name="Max. Acceleration = " + str(acc_2) + " m/s²") + + self.speedtestUi.graphicsView_2.plot([0], [0], pen=None, symbol=None, name="RFD = " + str(round(rfd_1, 2)) + " kg/s") + self.speedtestUi.graphicsView.plot([0], [0], pen=None, symbol=None, name="RFD = " + str(round(rfd_2, 2)) + " kg/s") + + #============================= - # Plot maximum force + # Plot maximum force pen = pg.mkPen(color=(180,0,0), width=2) mf = computeMaxForce(repMean) - self.graphicsView.plot([t[0],t[-1]], [mf,mf], name="Max. Force = " + str(mf) + " %BW", pen=pen) + self.graphicsView_1.plot([t[0],t[-1]], [mf,mf], name="Max. Force = " + str(mf) + " %BW", pen=pen) - self.graphicsView.showGrid(x=True, y=True) - self.graphicsView.setLabel('left', "%BW") - self.graphicsView.setLabel('bottom', "Time [sec]") + self.graphicsView_1.showGrid(x=True, y=True) + self.graphicsView_1.setLabel('left', "%BW") + self.graphicsView_1.setLabel('bottom', "Time [sec]") # Store result in class member variables self.criticalForce = cf @@ -309,7 +385,8 @@ def getData(self): data['criticalForce'] = float(self.criticalForce) data['wPrime'] = float(self.wPrime) data['maxForce'] = float(self.maxForce) - data['measDataKg'] = self.measDataKg.tolist() + data['measDataKg_1'] = self.measDataKg_1.tolist() + data['measDataKg_2'] = self.measDataKg_2.tolist() return data def setData(self, data, reset=False): @@ -317,12 +394,13 @@ def setData(self, data, reset=False): workoutId = self.workoutHandler.getIdFromName(self.workoutName) self.workoutComboBox.setCurrentIndex(workoutId) self.timestamp = data['timestamp'] - self.measDataKg = np.asarray(data['measDataKg']) + self.measDataKg_1 = np.asarray(data['measDataKg_1']) + self.measDataKg_2 = np.asarray(data['measDataKg_2']) # The order is critical, always do this at the end self.weightSpinBox.setValue(data['weight']) - if not reset and len(self.measDataKg) > 0: + if not reset and len(self.measDataKg_1) > 0: # Since critical force, W' and max force will be re-calculated in # the following function, it's not necessary to load them here. self.computeResultAndPlot() @@ -331,5 +409,5 @@ def setData(self, data, reset=False): self.criticalForce = 0 self.wPrime = 0 self.maxForce = 0 - self.graphicsView.clear() + self.graphicsView_1.clear() diff --git a/ui/MeasurementGui.py b/ui/MeasurementGui.py index d2fdacf..692d88e 100644 --- a/ui/MeasurementGui.py +++ b/ui/MeasurementGui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'MeasurementGui.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.6.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -25,7 +25,7 @@ class Ui_Form(object): def setupUi(self, Form): if not Form.objectName(): Form.setObjectName(u"Form") - Form.resize(542, 488) + Form.resize(595, 488) self.verticalLayout = QVBoxLayout(Form) self.verticalLayout.setObjectName(u"verticalLayout") self.horizontalLayout_2 = QHBoxLayout() @@ -66,6 +66,13 @@ def setupUi(self, Form): self.verticalLayout.addWidget(self.line_3) + self.line_4 = QFrame(Form) + self.line_4.setObjectName(u"line_4") + self.line_4.setFrameShape(QFrame.HLine) + self.line_4.setFrameShadow(QFrame.Sunken) + + self.verticalLayout.addWidget(self.line_4) + self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName(u"horizontalLayout") self.verticalLayout_2 = QVBoxLayout() @@ -110,32 +117,52 @@ def setupUi(self, Form): self.verticalLayout_3 = QVBoxLayout() self.verticalLayout_3.setObjectName(u"verticalLayout_3") self.verticalLayout_3.setSizeConstraint(QLayout.SetDefaultConstraint) - self.tareLabel = QLabel(Form) - self.tareLabel.setObjectName(u"tareLabel") - self.tareLabel.setMinimumSize(QSize(70, 0)) - self.tareLabel.setFont(font) - self.tareLabel.setAlignment(Qt.AlignCenter) + self.horizontalLayout_3 = QHBoxLayout() + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.tareLabel_1 = QLabel(Form) + self.tareLabel_1.setObjectName(u"tareLabel_1") + sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.tareLabel_1.sizePolicy().hasHeightForWidth()) + self.tareLabel_1.setSizePolicy(sizePolicy) + font1 = QFont() + font1.setPointSize(15) + self.tareLabel_1.setFont(font1) + self.tareLabel_1.setAlignment(Qt.AlignCenter) - self.verticalLayout_3.addWidget(self.tareLabel) + self.horizontalLayout_3.addWidget(self.tareLabel_1) - self.tareButton = QPushButton(Form) - self.tareButton.setObjectName(u"tareButton") - self.tareButton.setMinimumSize(QSize(70, 0)) + self.tareLabel_2 = QLabel(Form) + self.tareLabel_2.setObjectName(u"tareLabel_2") + self.tareLabel_2.setFont(font1) + self.tareLabel_2.setAlignment(Qt.AlignCenter) - self.verticalLayout_3.addWidget(self.tareButton) + self.horizontalLayout_3.addWidget(self.tareLabel_2) - self.horizontalLayout.addLayout(self.verticalLayout_3) + self.verticalLayout_3.addLayout(self.horizontalLayout_3) + self.horizontalLayout_4 = QHBoxLayout() + self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") + self.tareButton_1 = QPushButton(Form) + self.tareButton_1.setObjectName(u"tareButton_1") - self.verticalLayout.addLayout(self.horizontalLayout) + self.horizontalLayout_4.addWidget(self.tareButton_1) - self.line_4 = QFrame(Form) - self.line_4.setObjectName(u"line_4") - self.line_4.setFrameShape(QFrame.HLine) - self.line_4.setFrameShadow(QFrame.Sunken) + self.tareButton_2 = QPushButton(Form) + self.tareButton_2.setObjectName(u"tareButton_2") - self.verticalLayout.addWidget(self.line_4) + self.horizontalLayout_4.addWidget(self.tareButton_2) + + + self.verticalLayout_3.addLayout(self.horizontalLayout_4) + + + self.horizontalLayout.addLayout(self.verticalLayout_3) + + + self.verticalLayout.addLayout(self.horizontalLayout) self.graphicsView = PlotWidget(Form) self.graphicsView.setObjectName(u"graphicsView") @@ -156,7 +183,9 @@ def retranslateUi(self, Form): self.startButton.setText(QCoreApplication.translate("Form", u"Start", None)) self.stopButton.setText(QCoreApplication.translate("Form", u"Stop", None)) self.workoutLabel.setText(QCoreApplication.translate("Form", u"Stopped", None)) - self.tareLabel.setText(QCoreApplication.translate("Form", u"0kg", None)) - self.tareButton.setText(QCoreApplication.translate("Form", u"Tare", None)) + self.tareLabel_1.setText(QCoreApplication.translate("Form", u"0kg", None)) + self.tareLabel_2.setText(QCoreApplication.translate("Form", u"0kg", None)) + self.tareButton_1.setText(QCoreApplication.translate("Form", u"Tare 1", None)) + self.tareButton_2.setText(QCoreApplication.translate("Form", u"Tare 2", None)) # retranslateUi diff --git a/ui/MeasurementGui.ui b/ui/MeasurementGui.ui index 33f816d..7717913 100644 --- a/ui/MeasurementGui.ui +++ b/ui/MeasurementGui.ui @@ -6,7 +6,7 @@ 0 0 - 542 + 595 488 @@ -67,6 +67,13 @@ + + + + Qt::Horizontal + + + @@ -128,50 +135,67 @@ QLayout::SetDefaultConstraint - - - - 70 - 0 - - - - - 13 - - - - 0kg - - - Qt::AlignCenter - - + + + + + + 0 + 0 + + + + + 15 + + + + 0kg + + + Qt::AlignCenter + + + + + + + + 15 + + + + 0kg + + + Qt::AlignCenter + + + + - - - - 70 - 0 - - - - Tare - - + + + + + Tare 1 + + + + + + + Tare 2 + + + + - - - - Qt::Horizontal - - - diff --git a/ui/PreferencesGui.py b/ui/PreferencesGui.py index c53473f..fed964d 100644 --- a/ui/PreferencesGui.py +++ b/ui/PreferencesGui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'PreferencesGui.ui' ## -## Created by: Qt User Interface Compiler version 6.4.2 +## Created by: Qt User Interface Compiler version 6.6.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -58,7 +58,7 @@ def setupUi(self, Form): self.verticalLayout.addWidget(self.warningLabel) - self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) + self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) self.verticalLayout.addItem(self.verticalSpacer) diff --git a/ui/SpeedtestGui.py b/ui/SpeedtestGui.py new file mode 100644 index 0000000..9ebae79 --- /dev/null +++ b/ui/SpeedtestGui.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'SpeedtestGui.ui' +## +## Created by: Qt User Interface Compiler version 6.6.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QDialog, QHBoxLayout, QSizePolicy, + QWidget) + +from pyqtgraph import PlotWidget + +class Ui_Dialog(object): + def setupUi(self, Dialog): + if not Dialog.objectName(): + Dialog.setObjectName(u"Dialog") + Dialog.resize(499, 348) + self.horizontalLayout = QHBoxLayout(Dialog) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.graphicsView_2 = PlotWidget(Dialog) + self.graphicsView_2.setObjectName(u"graphicsView_2") + + self.horizontalLayout.addWidget(self.graphicsView_2) + + self.graphicsView = PlotWidget(Dialog) + self.graphicsView.setObjectName(u"graphicsView") + + self.horizontalLayout.addWidget(self.graphicsView) + + + self.retranslateUi(Dialog) + + QMetaObject.connectSlotsByName(Dialog) + # setupUi + + def retranslateUi(self, Dialog): + Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None)) + # retranslateUi + diff --git a/ui/SpeedtestGui.ui b/ui/SpeedtestGui.ui new file mode 100644 index 0000000..6758a9d --- /dev/null +++ b/ui/SpeedtestGui.ui @@ -0,0 +1,34 @@ + + + Dialog + + + + 0 + 0 + 499 + 348 + + + + Dialog + + + + + + + + + + + + + PlotWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/ui/compileUi.bat b/ui/compileUi.bat index ec7f84a..170e3b8 100644 --- a/ui/compileUi.bat +++ b/ui/compileUi.bat @@ -2,4 +2,5 @@ pyside6-uic MeasurementGui.ui -o MeasurementGui.py pyside6-uic DataGui.ui -o DataGui.py pyside6-uic CalibrationGui.ui -o CalibrationGui.py pyside6-uic PreferencesGui.ui -o PreferencesGui.py -pyside6-uic CompareresultGui.ui -o CompareresultGui.py \ No newline at end of file +pyside6-uic CompareresultGui.ui -o CompareresultGui.py +pyside6-uic SpeedtestGui.ui -o SpeedtestGui.py \ No newline at end of file diff --git a/util/Speed_test.py b/util/Speed_test.py new file mode 100644 index 0000000..b58fc8a --- /dev/null +++ b/util/Speed_test.py @@ -0,0 +1,84 @@ +import numpy as np +from ui import MeasurementCtrl + +def find_monotonic_rise_start(force_data): + peak_index = np.argmax(force_data) + for i in range(peak_index - 1, 0, -1): + if force_data[i] < force_data[i - 1]: + start_index = i + break + else: + start_index = 0 + return start_index + +def analyse_measurements(force_data, sampling_rate=10): + force_data = np.array(force_data) + start_idx = find_monotonic_rise_start(force_data) + + start_time = round(start_idx / sampling_rate, 2) + start_value = round(force_data[start_idx], 2) + + return start_value, start_time + +def acceleration(force_max, bodyweight): + acceleration = (force_max * 10)/bodyweight + acceleration_round = round(acceleration, 2) + + return acceleration_round + +def RFD(startofpullingtimepoint, maxforcetimepoint, maxforce, startpullingpointforce): + rfd = (maxforce - startpullingpointforce) / (maxforcetimepoint - startofpullingtimepoint) + + return rfd + +def computeSchnellkraftParameter(measData, lookupTable, sampleRate, bodyweight): + #cpmoute RFD and max. acceleration + t = np.linspace(0, len(measData) / sampleRate, len(measData)) + peak_alltime = np.max(measData) + peak_timepoint_alltime = t[np.argmax(measData)] + max_force_in_kg_alltime = (peak_alltime / 100) * bodyweight + startpointpullingvalue_alltime, startpointpulling_timepoint_alltime = analyse_measurements(measData) + + + #acceleration + max_acceleration = acceleration(max_force_in_kg_alltime, bodyweight) + + #RFD + rfd_value = RFD(startpointpulling_timepoint_alltime, peak_timepoint_alltime, peak_alltime, startpointpullingvalue_alltime) + + #compute Startingpoints and max. peaks + lookupRsmpl = np.repeat(lookupTable, sampleRate) + assert len(measData) == len(lookupRsmpl) + numSamples = len(lookupRsmpl) + + indStart = 0 + + allPeaks = [] + allPeaks_timepoint = [] + allStartingpoints = [] + allStartingpoints_timepoint = [] + + for i in range(1, numSamples): + diff = lookupRsmpl[i] - lookupRsmpl[i-1] + + if diff == 1: + # New Active Time begins + indStart = i + + elif diff == -1: + # Active Time endet → hier auswerten + segment = measData[indStart:i] + # Peak + local_peak = np.max(segment) + local_peak_idx = np.argmax(segment) + allPeaks.append(local_peak) + allPeaks_timepoint.append(t[indStart + local_peak_idx]) + + # Start-Zeitpunkt + sp_val, sp_time = analyse_measurements(segment, sampling_rate=sampleRate) + allStartingpoints.append(sp_val) + sp_time_abs = indStart/sampleRate + sp_time + allStartingpoints_timepoint.append(sp_time_abs) + + return allPeaks, allPeaks_timepoint, allStartingpoints, \ + allStartingpoints_timepoint, max_acceleration, rfd_value diff --git a/util/cedargrove_nau7802.py b/util/cedargrove_nau7802.py index 1634ee6..9fe2d59 100644 --- a/util/cedargrove_nau7802.py +++ b/util/cedargrove_nau7802.py @@ -97,7 +97,7 @@ class CalibrationMode: INTERNAL = 0x0 # Offset Calibration Internal; _CTRL2[1:0] = 0 (chip default) OFFSET = 0x2 # Offset Calibration System; _CTRL2[1:0] = 2 - GAIN = 0x3 # Gain Calibration System; _CTRL2[1:0] = 3 + GAIN = 0x3 # Gain Calibration System; _CTRL2[1:0] = 3 class NAU7802: diff --git a/util/sensor.py b/util/sensor.py index 8d2147e..46026a1 100644 --- a/util/sensor.py +++ b/util/sensor.py @@ -31,7 +31,8 @@ #======================================== class WeightSensor(): def __init__(self): - self.sensorValue = 0 + self.sensorValue_1 = 0 + self.sensorValue_2 = 0 self.scalingFactor = self.calcScalingFactor(Params.calibrationFile.value) # Set environment variable for MCP2221A @@ -44,13 +45,23 @@ def __init__(self): try: import board from util.cedargrove_nau7802 import NAU7802 + from adafruit_tca9548a import TCA9548A + + #Multiplexer initialisieren + multiplexer = TCA9548A(board.I2C()) # Instantiate 24-bit load sensor ADC; two channels, default gain of 128 - self.nau7802 = NAU7802(board.I2C(), address=0x2A, active_channels=2) - self.nau7802.enable(True) - self.nau7802.channel = 1 + self.nau7802_1 = NAU7802(multiplexer[0], address=0x2A, active_channels=2) + self.nau7802_1.enable(True) + self.nau7802_1.channel = 1 self.zero_channel() + #2.Wägezelle + self.nau7802_2 = NAU7802(multiplexer[1], address=0x2A, active_channels=2) + self.nau7802_2.enable(True) + self.nau7802_2.channel = 1 + self.zero_channel_2() + self.connected = True self.stopThreadEvent = Event() @@ -70,10 +81,12 @@ def onNewMeasurement(self, stopEvent): if stopEvent.is_set(): stopEvent.clear() break - elif self.connected and self.nau7802.available: - self.sensorValue = self.nau7802.read() + elif self.connected and self.nau7802_1.available and self.nau7802_2.available: + self.sensorValue_1 = self.nau7802_1.read() + self.sensorValue_2 = self.nau7802_2.read() else: - self.sensorValue = 0 + self.sensorValue_1 = 0 + self.sensorValue_2 = 0 time.sleep(0.001) def zero_channel(self): @@ -84,20 +97,41 @@ def zero_channel(self): msg = QMessageBox() msg.setWindowTitle("Warning") msg.setIcon(QMessageBox.Warning) - msg.setText("Checking sensor...\nRemove any weight, then click \"OK\" to proceed.") + msg.setText("Checking sensor 1 ...\nRemove any weight, then click \"OK\" to proceed.") msg.exec_() - self.nau7802.calibrate("INTERNAL") - self.nau7802.calibrate("OFFSET") + self.nau7802_1.calibrate("INTERNAL") + self.nau7802_1.calibrate("OFFSET") for _ in range(100): - self.nau7802.read() # Read 100 samples to establish zero offset + self.nau7802_1.read() # Read 100 samples to establish zero offset + + def zero_channel_2(self): + """Initiate internal calibration for current channel; return raw zero + offset value. Use when scale is started, a new channel is selected, or to + adjust for measurement drift. Remove weight and tare from load cell before + executing.""" + msg = QMessageBox() + msg.setWindowTitle("Warning") + msg.setIcon(QMessageBox.Warning) + msg.setText("Checking sensor 2 ...\nRemove any weight, then click \"OK\" to proceed.") + msg.exec_() + + self.nau7802_2.calibrate("INTERNAL") + self.nau7802_2.calibrate("OFFSET") + + for _ in range(100): + self.nau7802_2.read() # Read 100 samples to establish zero offset - def getValueInKg(self): - return self.sensorValue * self.scalingFactor + def getValueInKg_1(self): + return self.sensorValue_1 * self.scalingFactor + def getValueInKg_2(self): + return self.sensorValue_2 * self.scalingFactor - def getRawValue(self): - return self.sensorValue + def getRawValue_1(self): + return self.sensorValue_1 + def getRawValue_2(self): + return self.sensorValue_2 def isConnected(self): return self.connected