Skip to content

Commit

Permalink
Saving tracking data
Browse files Browse the repository at this point in the history
  • Loading branch information
kieranabrennan committed Dec 28, 2023
1 parent a45d360 commit fcefaf4
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 19 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/venv
/__pycache__
.DS_Store
.DS_Store
/data/*
19 changes: 11 additions & 8 deletions BeatTracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@
import matplotlib.pyplot as plt
import numpy as np
import neurokit2 as nk
from PySide6.QtCore import QObject, Signal, Slot
from PySide6.QtCore import QObject

'''
BeatTracker class
Tracks a rolling ecg signal history and calculate number of beats in a time window
'''
class BeatTracker(QObject):
accuracyCalcuated = Signal(float)

def __init__(self):
super().__init__()
self.ECG_HIST_SIZE = 2400 # Number of samples to keep in the history
self.ecg_hist = np.full(self.ECG_HIST_SIZE, np.nan)
self.ecg_times = np.full(self.ECG_HIST_SIZE, np.nan)
self.beat_count = None

self.beat_count_measured = None
self.beat_count_entered = None

def update_ecg_history(self, t, ecg):
self.ecg_hist = np.roll(self.ecg_hist, -1)
Expand All @@ -24,17 +29,15 @@ def update_ecg_history(self, t, ecg):

def get_beat_count_from_wind(self, start_time, end_time):
wind_values, wind_times = self.get_ecg_wind(start_time, end_time)
self.beat_count = self.get_beat_count(wind_values, 130)
self.beat_count_measured = self.get_beat_count(wind_values, 130)
# Show the start time error to 3 dp
print(f"Start time error: {start_time-wind_times[0]:.3f} s")
print(f"End time error: {end_time-wind_times[-1]:.3f} s")
print(f"Number of R peaks: {self.beat_count:.0f}")
print(f"Number of R peaks: {self.beat_count_measured:.0f}")

self.plot_graph(wind_values, wind_times)

def get_accuracy_score(self, beat_count_entered):
accuracy = 1 - abs(self.beat_count - beat_count_entered)/(0.5*(self.beat_count + beat_count_entered))
self.accuracyCalcuated.emit(accuracy)
return self.beat_count_measured

def plot_graph(self, wind_values, wind_times):
plt.figure()
Expand Down
15 changes: 6 additions & 9 deletions Controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def __init__(self):
self.view.show()

self.model.sensorConnected.connect(self.sensorConnectedHandler)
self.model.beat_tracker.accuracyCalcuated.connect(self.accuracyCalculatedHandler)

self.state = ControlState.INITIALISING

Expand All @@ -42,14 +41,10 @@ def __init__(self):
def sensorConnectedHandler(self):
self.setStateReadyAfterDelay()

@Slot(float)
def accuracyCalculatedHandler(self, accuracy):
print(f"Accuracy: {accuracy:.3f}")

@Slot()
def countdownFinished(self):
self.state = ControlState.RECORDING_INPUT
self.model.beat_tracker.get_beat_count_from_wind(self.record_start_time, time.time_ns()/1.0e9)
self.state = ControlState.RECORDING_INPUT
self.model.getBeatCountMeasured(self.record_start_time, time.time_ns()/1.0e9)
self.view.control_recording_input()

def configureSeriesTimer(self):
Expand Down Expand Up @@ -81,10 +76,12 @@ def buttonPressed(self):
pass
elif self.state == ControlState.RECORDING_INPUT:
self.state = ControlState.RECORDING_CONFIDENCE
self.model.beat_tracker.get_accuracy_score(self.view.controls_widget.beat_count_input.value())
accuracy = self.model.getBeatCountAccuracy(self.view.controls_widget.beat_count_input.value())
print(f"Accuracy: {accuracy:.3f}")
self.view.control_recording_confidence()
elif self.state == ControlState.RECORDING_CONFIDENCE:

self.model.setBeatCountConfidence(self.view.controls_widget.confidence_scale.value())
self.model.saveBeatTrackingData()
self.state = ControlState.FINISHED
elif self.state == ControlState.FINISHED:
self.state = ControlState.READY_TO_START
Expand Down
43 changes: 42 additions & 1 deletion Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from BeatTracker import BeatTracker
from PySide6.QtCore import QObject, Signal
from bleak import BleakScanner
from datetime import datetime
import json
import os

class Model(QObject):
sensorConnected = Signal()
Expand All @@ -11,7 +14,12 @@ def __init__(self):
super().__init__()
self.polar_sensor = None
self.beat_tracker = BeatTracker()


self.beat_count_measured = None
self.beat_count_entered = None
self.beat_count_accuracy = None
self.beat_count_confidence = None

def set_polar_sensor(self, device):
self.polar_sensor = PolarH10(device)

Expand Down Expand Up @@ -55,6 +63,39 @@ async def update_ecg(self):
while not self.polar_sensor.ecg_queue_is_empty():
self.beat_tracker.update_ecg_history(*self.polar_sensor.dequeue_ecg())

def getBeatCountMeasured(self, start_time, end_time):
self.beat_count_measured = self.beat_tracker.get_beat_count_from_wind(start_time, end_time)
return self.beat_count_measured

def getBeatCountAccuracy(self, beat_count_entered):
self.beat_count_entered = beat_count_entered
self.beat_count_accuracy = 1 - abs(self.beat_count_measured - self.beat_count_entered)/(0.5*(self.beat_count_measured + self.beat_count_entered))
return self.beat_count_accuracy

def setBeatCountConfidence(self, beat_count_confidence):
self.beat_count_confidence = beat_count_confidence

def saveBeatTrackingData(self):
data = {
"Beat count measured": self.beat_count_measured,
"Beat count entered": self.beat_count_entered,
"Beat count accuracy": self.beat_count_accuracy,
"Beat count confidence": self.beat_count_confidence
}

data_folder = "data"
if not os.path.exists(data_folder):
os.makedirs(data_folder)

timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
filename = f"beat_tracking_data_{timestamp}.json"
filepath = os.path.join(data_folder, filename)

with open(filepath, "w") as file:
json.dump(data, file, indent=4)

print(f"Data saved to {filepath}")




Expand Down
3 changes: 3 additions & 0 deletions View.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,6 @@ def initUI(self):
max_label.setFixedWidth(200) # Set a fixed width
max_label.setAlignment(Qt.AlignLeft)
slider_layout.addWidget(max_label)

def value(self):
return self.slider.value()
6 changes: 6 additions & 0 deletions data/beat_tracking_data_20231228-130137.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"Beat count measured": 10,
"Beat count entered": 6,
"Beat count accuracy": 0.5,
"Beat count confidence": 5
}
6 changes: 6 additions & 0 deletions data/beat_tracking_data_20231228-130457.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"Beat count measured": 10,
"Beat count entered": 5,
"Beat count accuracy": 0.33333333333333337,
"Beat count confidence": 5
}

0 comments on commit fcefaf4

Please sign in to comment.