Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions settings/workouts.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions ui/CalibrationGui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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!
################################################################################
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion ui/CompareresultGui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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!
################################################################################
Expand Down
2 changes: 1 addition & 1 deletion ui/DataGui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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!
################################################################################
Expand Down
170 changes: 124 additions & 46 deletions ui/MeasurementCtrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -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)

Expand All @@ -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:
Expand Down Expand Up @@ -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))
Expand All @@ -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):
Expand All @@ -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
Expand All @@ -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
Expand All @@ -309,20 +385,22 @@ 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):
self.workoutName = data['workout']
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()
Expand All @@ -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()

Loading