12
12
class ECGMonitor (QMainWindow ):
13
13
def __init__ (self , invert = False ): # Add the invert parameter
14
14
super ().__init__ ()
15
- self .invert = invert # Store the invert flag
15
+ self .invert = invert
16
16
17
- # Set up GUI window
18
- self .setWindowTitle ("Real-Time ECG Monitor" )
17
+ self .setWindowTitle ("Real-Time ECG Monitor" ) # Set up GUI window
19
18
self .setGeometry (100 , 100 , 800 , 600 )
20
19
21
20
self .plot_widget = PlotWidget (self )
@@ -54,11 +53,9 @@ def __init__(self, invert=False): # Add the invert parameter
54
53
self .heart_rate = None
55
54
self .current_index = 0 # Index for overwriting data
56
55
57
- # Low-pass filter coefficients
58
- self .b , self .a = butter (4 , 20.0 / (0.5 * self .sampling_rate ), btype = 'low' )
56
+ self .b , self .a = butter (4 , 20.0 / (0.5 * self .sampling_rate ), btype = 'low' ) # Low-pass filter coefficients
59
57
60
- # Timer for updating the plot
61
- self .timer = pg .QtCore .QTimer ()
58
+ self .timer = pg .QtCore .QTimer () # Timer for updating the plot
62
59
self .timer .timeout .connect (self .update_plot )
63
60
self .timer .start (10 )
64
61
@@ -71,9 +68,11 @@ def __init__(self, invert=False): # Add the invert parameter
71
68
# Set fixed x-axis range
72
69
self .plot_widget .setXRange (0 , 10 ) # 10 seconds
73
70
74
- # Initialize the plot curves
75
71
self .ecg_curve = self .plot_widget .plot (self .time_data , self .ecg_data , pen = pg .mkPen ('k' , width = 1 ))
76
72
self .r_peak_curve = self .plot_widget .plot ([], [], pen = None , symbol = 'o' , symbolBrush = 'r' , symbolSize = 10 ) # R-peaks in red
73
+
74
+ self .moving_average_window_size = 5 # Initialize moving average buffer
75
+ self .heart_rate_history = [] # Buffer to store heart rates for moving average
77
76
78
77
def update_plot (self ):
79
78
samples , _ = self .inlet .pull_chunk (timeout = 0.0 , max_samples = 30 )
@@ -83,14 +82,12 @@ def update_plot(self):
83
82
self .ecg_data [self .current_index ] = sample [0 ]
84
83
self .current_index = (self .current_index + 1 ) % self .buffer_size # Circular increment
85
84
86
- # Filter the signal
87
- filtered_ecg = filtfilt (self .b , self .a , self .ecg_data )
85
+ filtered_ecg = filtfilt (self .b , self .a , self .ecg_data ) # Filter the signal
88
86
89
87
# Invert the signal if the invert flag is set
90
88
if self .invert :
91
89
filtered_ecg = - filtered_ecg
92
90
93
- # Update the plot data
94
91
self .ecg_curve .setData (self .time_data , filtered_ecg ) # Use current buffer for plotting
95
92
96
93
# Detect R-peaks and update heart rate
@@ -109,24 +106,30 @@ def calculate_heart_rate(self):
109
106
if len (rr_intervals ) > 0 :
110
107
avg_rr = np .mean (rr_intervals ) # Average RR interval
111
108
self .heart_rate = 60.0 / avg_rr # Convert to heart rate (BPM)
112
- self .heart_rate_label .setText (f"Heart Rate: { int (self .heart_rate )} BPM" )
109
+ self .heart_rate_history .append (self .heart_rate ) # Update moving average
110
+ if len (self .heart_rate_history ) > self .moving_average_window_size :
111
+ self .heart_rate_history .pop (0 ) # Remove the oldest heart rate
112
+
113
+ # Calculate the moving average heart rate
114
+ moving_average_hr = np .mean (self .heart_rate_history )
115
+
116
+ # Update heart rate label with moving average & convert into int
117
+ self .heart_rate_label .setText (f"Heart Rate: { int (moving_average_hr )} BPM" )
113
118
else :
114
- self .heart_rate_label .setText ("Heart Rate: Calculating..." ) # If fewer than 10 R-peaks are detected, show "Calculating"
119
+ self .heart_rate_label .setText ("Heart Rate: Calculating..." )
115
120
116
121
def plot_r_peaks (self , filtered_ecg ):
117
- # Extract the time of detected R-peaks
118
- r_peak_times = self .time_data [self .r_peaks ]
122
+ r_peak_times = self .time_data [self .r_peaks ] # Extract the time of detected R-peaks
119
123
r_peak_values = filtered_ecg [self .r_peaks ]
120
124
self .r_peak_curve .setData (r_peak_times , r_peak_values ) # Plot R-peaks as red dots
121
125
122
126
if __name__ == "__main__" :
123
- # Set up argument parser
124
127
parser = argparse .ArgumentParser (description = "Real-Time ECG Monitor" )
125
128
parser .add_argument ("--invert" , action = "store_true" , help = "Invert the ECG signal plot" )
126
129
127
130
args = parser .parse_args ()
128
131
129
132
app = QApplication (sys .argv )
130
- window = ECGMonitor (invert = args .invert ) # Pass the invert flag to ECGMonitor
133
+ window = ECGMonitor (invert = args .invert ) # Pass the invert flag
131
134
window .show ()
132
- sys .exit (app .exec_ ())
135
+ sys .exit (app .exec_ ())
0 commit comments