Skip to content

Commit 8c5a055

Browse files
committed
NewnCode of ecg
1 parent c85d3c2 commit 8c5a055

File tree

1 file changed

+222
-40
lines changed

1 file changed

+222
-40
lines changed

applications/ecgg.py

Lines changed: 222 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,187 @@
1+
# import sys
2+
# import numpy as np
3+
# import time
4+
# from pylsl import StreamInlet, resolve_stream
5+
# from scipy.signal import butter, lfilter, find_peaks
6+
# import pyqtgraph as pg # For real-time plotting
7+
# from pyqtgraph.Qt import QtWidgets, QtCore # PyQt components for GUI
8+
# import signal # For handling Ctrl+C
9+
# from collections import deque # For creating a ring buffer
10+
11+
# # Initialize global variables
12+
# inlet = None
13+
# data_buffer = np.zeros(2000) # Buffer to hold the last 2000 samples for ECG data
14+
15+
# # Function to design a Butterworth filter
16+
# def butter_filter(cutoff, fs, order=4, btype='low'):
17+
# nyquist = 0.5 * fs # Nyquist frequency
18+
# normal_cutoff = cutoff / nyquist
19+
# b, a = butter(order, normal_cutoff, btype=btype, analog=False)
20+
# return b, a
21+
22+
# # Apply the Butterworth filter to the data
23+
# def apply_filter(data, b, a):
24+
# return lfilter(b, a, data)
25+
26+
# # Function to detect heartbeats using peak detection
27+
# def detect_heartbeats(ecg_data, sampling_rate):
28+
# peaks, _ = find_peaks(ecg_data, distance=sampling_rate * 0.6, prominence=0.5) # Adjust as necessary
29+
# return peaks
30+
31+
# class DataCollector(QtCore.QThread):
32+
# data_ready = QtCore.pyqtSignal(np.ndarray)
33+
34+
# def __init__(self):
35+
# super().__init__()
36+
# self.running = True
37+
# self.sampling_rate = None
38+
39+
# def run(self):
40+
# global inlet
41+
# print("Looking for LSL stream...")
42+
# streams = resolve_stream('name', 'BioAmpDataStream')
43+
44+
# if not streams:
45+
# print("No LSL Stream found! Exiting...")
46+
# sys.exit(0)
47+
48+
# inlet = StreamInlet(streams[0])
49+
# self.sampling_rate = inlet.info().nominal_srate()
50+
# print(f"Detected sampling rate: {self.sampling_rate} Hz")
51+
52+
# # Create and design filters
53+
# low_cutoff = 20.0 # 40 Hz low-pass filter
54+
# self.low_b, self.low_a = butter_filter(low_cutoff, self.sampling_rate, order=4, btype='low')
55+
56+
# while self.running:
57+
# # Pull multiple samples at once
58+
# samples, _ = inlet.pull_chunk(timeout=0.0, max_samples=10) # Pull up to 10 samples
59+
# if samples:
60+
# global data_buffer
61+
# data_buffer = np.roll(data_buffer, -len(samples)) # Shift data left
62+
# data_buffer[-len(samples):] = [sample[0] for sample in samples] # Add new samples to the end
63+
64+
# filtered_data = apply_filter(data_buffer, self.low_b, self.low_a) # Low-pass Filter
65+
# self.data_ready.emit(filtered_data) # Emit the filtered data for plotting
66+
67+
# time.sleep(0.01)
68+
69+
# def stop(self):
70+
# self.running = False
71+
72+
# class ECGApp(QtWidgets.QMainWindow):
73+
# def __init__(self):
74+
# super().__init__()
75+
76+
# # Create a plot widget
77+
# self.plot_widget = pg.PlotWidget(title="ECG Signal")
78+
# self.setCentralWidget(self.plot_widget)
79+
# self.plot_widget.setBackground('w')
80+
81+
# # Create a label to display heart rate
82+
# self.heart_rate_label = QtWidgets.QLabel("Heart rate: - bpm", self)
83+
# self.heart_rate_label.setStyleSheet("font-size: 20px; font-weight: bold;")
84+
# self.heart_rate_label.setAlignment(QtCore.Qt.AlignCenter)
85+
86+
# layout = QtWidgets.QVBoxLayout()
87+
# layout.addWidget(self.plot_widget)
88+
# layout.addWidget(self.heart_rate_label)
89+
90+
# container = QtWidgets.QWidget()
91+
# container.setLayout(layout)
92+
# self.setCentralWidget(container)
93+
94+
# self.ecg_buffer = [] # Buffer to hold the ECG data
95+
# self.r_peak_times = deque(maxlen=15) # Ring buffer for R-peak timestamps within 15 seconds
96+
# self.peak_indices = deque() # Stores R-peak indices for the current 15-second window
97+
98+
# self.data_collector = DataCollector() # Data collector thread
99+
# self.data_collector.data_ready.connect(self.update_plot)
100+
# self.data_collector.start()
101+
102+
# self.time_axis = np.linspace(0, 2000/200, 2000) # Store the x-axis time window
103+
# self.plot_widget.setYRange(0,800) # Set fixed y-axis limits
104+
105+
# # Timer to update heart rate every 15 seconds
106+
# self.timer = QtCore.QTimer(self)
107+
# self.timer.timeout.connect(self.calculate_and_reset_heart_rate)
108+
# self.timer.start(15000) # 15 seconds
109+
110+
# def update_plot(self, ecg_data):
111+
# self.ecg_buffer = ecg_data # Update buffer
112+
# self.plot_widget.clear()
113+
114+
# # Set a fixed window (time axis) to ensure stable plotting
115+
# self.plot_widget.plot(self.time_axis, self.ecg_buffer, pen='#000000') # Plot ECG data with black line
116+
# self.plot_widget.setXRange(self.time_axis[0], self.time_axis[-1], padding=0)
117+
118+
# # Detect heartbeats in real time
119+
# heartbeats = detect_heartbeats(np.array(self.ecg_buffer), self.data_collector.sampling_rate)
120+
# # print(f"Detected R-peaks at indices: {heartbeats}")
121+
122+
# # Append the actual time of detection for each detected peak
123+
# for index in heartbeats:
124+
# # Append the time based on the index and the sampling rate
125+
# self.r_peak_times.append(index / self.data_collector.sampling_rate)
126+
# self.peak_indices.append(index) # Store peak indices for this 10-second window
127+
128+
# # Calculate the x-coordinates (time axis) for the R-peaks
129+
# r_peak_times = self.time_axis[heartbeats]
130+
131+
# # Plot the R-peaks as red circles
132+
# r_peak_scatter = pg.ScatterPlotItem(x=r_peak_times, y=self.ecg_buffer[heartbeats],
133+
# symbol='o', size=10, pen='r', brush='r')
134+
# self.plot_widget.addItem(r_peak_scatter) # Add scatter plot to the ECG plot
135+
136+
# def calculate_and_reset_heart_rate(self):
137+
# if len(self.r_peak_times) > 1:
138+
# # Calculate the time intervals between successive R-peaks
139+
# intervals = np.diff(self.r_peak_times)
140+
141+
# print(f"R-peak times: {self.r_peak_times}") # Log R-peak times
142+
# print(f"Intervals between peaks: {intervals}") # Log intervals
143+
144+
# # Filter intervals that are positive and non-zero to avoid issues
145+
# valid_intervals = intervals[intervals > 0]
146+
147+
# if len(valid_intervals) > 0:
148+
# bpm = 60 / np.mean(valid_intervals) # Mean interval is in seconds, converting to bpm
149+
# self.heart_rate_label.setText(f"Heart rate: {bpm:.2f} bpm")
150+
# else:
151+
# self.heart_rate_label.setText("Heart rate: 0 bpm")
152+
# else:
153+
# self.heart_rate_label.setText("Heart rate: 0 bpm")
154+
155+
# # Clear the deque for the next 15-second window
156+
# self.r_peak_times.clear()
157+
# self.peak_indices.clear()
158+
159+
# def closeEvent(self, event):
160+
# self.data_collector.stop() # Stop the data collector thread on close
161+
# self.data_collector.wait() # Wait for the thread to finish
162+
# event.accept() # Accept the close event
163+
164+
# def signal_handler(sig, frame):
165+
# print("Exiting...")
166+
# QtWidgets.QApplication.quit()
167+
168+
# if __name__ == "__main__":
169+
# signal.signal(signal.SIGINT, signal_handler) # Handle Ctrl+C
170+
171+
# streams = resolve_stream('name', 'BioAmpDataStream')
172+
# if not streams:
173+
# print("No LSL Stream found! Exiting...")
174+
# sys.exit(0)
175+
176+
# app = QtWidgets.QApplication(sys.argv)
177+
178+
# window = ECGApp()
179+
# window.setWindowTitle("Real-Time ECG Monitoring")
180+
# window.resize(800, 600)
181+
# window.show()
182+
183+
# sys.exit(app.exec_())
184+
1185
import sys
2186
import numpy as np
3187
import time
@@ -50,7 +234,7 @@ def run(self):
50234
print(f"Detected sampling rate: {self.sampling_rate} Hz")
51235

52236
# Create and design filters
53-
low_cutoff = 20.0 # 40 Hz low-pass filter
237+
low_cutoff = 20.0 # 20 Hz low-pass filter
54238
self.low_b, self.low_a = butter_filter(low_cutoff, self.sampling_rate, order=4, btype='low')
55239

56240
while self.running:
@@ -91,21 +275,22 @@ def __init__(self):
91275
container.setLayout(layout)
92276
self.setCentralWidget(container)
93277

94-
self.ecg_buffer = [] # Buffer to hold the ECG data
95-
self.r_peak_times = deque(maxlen=15) # Ring buffer for R-peak timestamps within 15 seconds
96-
self.peak_indices = deque() # Stores R-peak indices for the current 15-second window
278+
self.ecg_buffer = []
279+
self.r_peak_times = deque(maxlen=10) # Deque to store recent R-peak times
280+
self.peak_intervals = deque(maxlen=9) # Deque to store intervals between R-peaks
97281

98282
self.data_collector = DataCollector() # Data collector thread
99283
self.data_collector.data_ready.connect(self.update_plot)
100284
self.data_collector.start()
101285

102286
self.time_axis = np.linspace(0, 2000/200, 2000) # Store the x-axis time window
103-
self.plot_widget.setYRange(0,800) # Set fixed y-axis limits
287+
self.plot_widget.setYRange(0,1000) # Set fixed y-axis limits
288+
289+
# Time to start heart rate calculation after 10 seconds
290+
self.start_time = time.time()
291+
self.initial_period = 10 # First 10 seconds to gather enough R-peaks
104292

105-
# Timer to update heart rate every 15 seconds
106-
self.timer = QtCore.QTimer(self)
107-
self.timer.timeout.connect(self.calculate_and_reset_heart_rate)
108-
self.timer.start(15000) # 15 seconds
293+
self.total_interval_sum = 0 # To track the sum of intervals for efficient heart rate calculation
109294

110295
def update_plot(self, ecg_data):
111296
self.ecg_buffer = ecg_data # Update buffer
@@ -115,47 +300,44 @@ def update_plot(self, ecg_data):
115300
self.plot_widget.plot(self.time_axis, self.ecg_buffer, pen='#000000') # Plot ECG data with black line
116301
self.plot_widget.setXRange(self.time_axis[0], self.time_axis[-1], padding=0)
117302

118-
# Detect heartbeats in real time
119303
heartbeats = detect_heartbeats(np.array(self.ecg_buffer), self.data_collector.sampling_rate)
120-
# print(f"Detected R-peaks at indices: {heartbeats}")
121304

122-
# Append the actual time of detection for each detected peak
123305
for index in heartbeats:
124-
# Append the time based on the index and the sampling rate
125-
self.r_peak_times.append(index / self.data_collector.sampling_rate)
126-
self.peak_indices.append(index) # Store peak indices for this 10-second window
306+
r_time = index / self.data_collector.sampling_rate
307+
self.r_peak_times.append(r_time)
127308

128-
# Calculate the x-coordinates (time axis) for the R-peaks
129-
r_peak_times = self.time_axis[heartbeats]
309+
if len(self.r_peak_times) > 1:
310+
# Calculate the interval between consecutive R-peaks
311+
new_interval = self.r_peak_times[-1] - self.r_peak_times[-2]
130312

131-
# Plot the R-peaks as red circles
132-
r_peak_scatter = pg.ScatterPlotItem(x=r_peak_times, y=self.ecg_buffer[heartbeats],
133-
symbol='o', size=10, pen='r', brush='r')
134-
self.plot_widget.addItem(r_peak_scatter) # Add scatter plot to the ECG plot
135-
136-
def calculate_and_reset_heart_rate(self):
137-
if len(self.r_peak_times) > 1:
138-
# Calculate the time intervals between successive R-peaks
139-
intervals = np.diff(self.r_peak_times)
140-
141-
print(f"R-peak times: {self.r_peak_times}") # Log R-peak times
142-
print(f"Intervals between peaks: {intervals}") # Log intervals
313+
if len(self.peak_intervals) == self.peak_intervals.maxlen:
314+
# Remove the oldest interval from the sum
315+
oldest_interval = self.peak_intervals.popleft()
316+
self.total_interval_sum -= oldest_interval #Minus the oldest
143317

144-
# Filter intervals that are positive and non-zero to avoid issues
145-
valid_intervals = intervals[intervals > 0]
318+
# Add the new interval to the deque and update the sum
319+
self.peak_intervals.append(new_interval)
320+
self.total_interval_sum += new_interval # Plus the new
146321

147-
if len(valid_intervals) > 0:
148-
bpm = 60 / np.mean(valid_intervals) # Mean interval is in seconds, converting to bpm
149-
self.heart_rate_label.setText(f"Heart rate: {bpm:.2f} bpm")
150-
else:
151-
self.heart_rate_label.setText("Heart rate: 0 bpm")
322+
# Plot detected R-peaks
323+
r_peak_times = self.time_axis[heartbeats]
324+
r_peak_scatter = pg.ScatterPlotItem(x=r_peak_times, y=self.ecg_buffer[heartbeats],
325+
symbol='o', size=10, pen='r', brush='r')
326+
self.plot_widget.addItem(r_peak_scatter)
327+
328+
# Start heart rate calculation after 10 seconds
329+
if time.time() - self.start_time >= self.initial_period:
330+
self.update_heart_rate()
331+
332+
def update_heart_rate(self):
333+
if len(self.peak_intervals) > 0:
334+
# Efficiently calculate the heart rate using the sum of intervals
335+
avg_interval = self.total_interval_sum / len(self.peak_intervals)
336+
bpm = 60 / avg_interval # Convert to beats per minute
337+
self.heart_rate_label.setText(f"Heart rate: {bpm:.2f} bpm")
152338
else:
153339
self.heart_rate_label.setText("Heart rate: 0 bpm")
154340

155-
# Clear the deque for the next 15-second window
156-
self.r_peak_times.clear()
157-
self.peak_indices.clear()
158-
159341
def closeEvent(self, event):
160342
self.data_collector.stop() # Stop the data collector thread on close
161343
self.data_collector.wait() # Wait for the thread to finish

0 commit comments

Comments
 (0)