Skip to content

Commit f40f3b9

Browse files
authored
Merge pull request #90 from Ritika8081/fft-plot
Fix issue of snapshot duplicating channel 1's data.
2 parents 94f4271 + 9cf09b1 commit f40f3b9

File tree

4 files changed

+66
-47
lines changed

4 files changed

+66
-47
lines changed

.github/workflows/deploy.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ name: Deploy Next.js site to Pages
77
on:
88
# Runs on pushes targeting the default branch
99
push:
10-
branches: ["*"]
10+
branches-ignore :
11+
- "**dev**"
12+
pull_request:
13+
branches-ignore :
14+
- "**dev**"
1115

1216
# Allows you to run this workflow manually from the Actions tab
1317
workflow_dispatch:

README.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@ Chords is an application based on Web Serial connection, you can connect [Compat
2323
1. Connect the Arduino to your computer using a USB cable.
2424
2. Open the Arduino IDE and flash the provided firmware onto the Arduino.
2525
3. Open Chords in a web browser.
26-
4. Click the "Connect" button to establish a connection with the Arduino and stream.
27-
5. Click the "Zoom" button to zoom in on data visualization.
28-
6. Click the "Play/Pause" to control data flow and navigate frames with forward/backward buttons.
29-
7. Click the "Record" button to record data.
30-
8. Click the "download" button to download the recorded data.
31-
9. Click the "Delete" button to delete recorded data.
32-
10. Click the "Plus/Minus" button to increase/decrease channel.
33-
11. Click "Filter" button for EMG, ECG, EOG and EEG filters with muscle, heart, eye and brain icons or master buttons for all channels. You can apply 50Hz or 60Hz filter to individual or all channel.
34-
12. Click the "Disconnect" button to terminate the connection with the Arduino and stop the data stream.
26+
4. Click the "Visualize Now" button to navigate to the applications page, where you'll find three options: "Chords Visualizer", "Serial Wizard", and "FFT Plot".
27+
5. Select "Chords Visualizer" to connect with the Arduino and begin streaming data.
28+
7. Click the "Settings" icon to open the popover, where you'll find a "Zoom" slider to adjust the view as needed.
29+
8. Click the "Play/Pause" to control data flow and navigate frames with forward/backward buttons.
30+
9. Click the "Record" button to record data.
31+
10. Click the "download" button to download the recorded data.
32+
11. Click the "Delete" button to delete recorded data.
33+
12. Click the "Settings" icon to select from (up to 16) available channels of your development board. Use "Select All" to choose all channels simultaneously, and "Reset" to revert to the previous channel selection.
34+
13. Click the "Filter" button to select filters for EMG, ECG, EOG, and EEG for muscle, heart, eye, and brain data, respectively. Click on individual icons or master buttons at the top to apply a filter for all channels. You can also apply 50Hz or 60Hz notch filter to individual or all channels.
35+
14. Click the "Disconnect" button to terminate the connection with your development board and stop the data stream.
3536

3637
## Technologies Used
3738

@@ -81,6 +82,8 @@ Chords is an application based on Web Serial connection, you can connect [Compat
8182

8283
- [X] **Serial Wizard** : We are introducing the Serial Plotter and Monitor, a powerful tool that enables you to visualize data graphically, display raw data in text format, and even use both modes simultaneously for a more comprehensive analysis. In the past,we had no better option to run both plotter and monitor at the same time and high-speed plotting that hindered real-time data visualization. To address these challenges, we've created a user-friendly, smooth-performing Serial Plotter & Monitor with multi-mode support, providing a more responsive and efficient data analysis experience.
8384

85+
- [X] **FFT Visualizer** : We've added FFT analysis and EEG band spectrum plotting to enhance real-time brain signal visualization. These features let you monitor EEG frequency bands—Delta, Theta, Alpha, Beta, and Gamma—for deeper insights into mental states. The “FFT Visualizer” shows filtered EEG signals and offers two modes: Band Power for real-time EEG strength and Beta Candle, a unique focus-level indicator where a brighter candle means higher beta activity. You can also download EEG data as CSV for further analysis.
86+
8487
## Contributors
8588

8689
Thank you for contributing to our project! Your support is invaluable in creating & enhancing Chords-Web and making it even better. 😊
101 KB
Loading

src/components/Canvas.tsx

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -86,39 +86,57 @@ const Canvas = forwardRef(
8686
const prevCanvasCountRef = useRef<number>(canvasCount);
8787

8888
const processIncomingData = (incomingData: number[]) => {
89-
if (pauseRef.current) return; // Skip processing if paused
89+
// Ensure we have valid references
90+
if (!selectedChannelsRef.current || !array3DRef.current) return;
9091

9192
const currentBuffer = array3DRef.current[activeBufferIndexRef.current];
93+
if (!currentBuffer) return;
9294

9395
// Handle canvas count changes and reset buffers
9496
if (prevCanvasCountRef.current !== canvasCount) {
9597
for (let bufferIndex = 0; bufferIndex < 6; bufferIndex++) {
96-
array3DRef.current[bufferIndex] = Array.from({ length: canvasCount }, () => []);
98+
array3DRef.current[bufferIndex] = Array.from(
99+
{ length: selectedChannelsRef.current.length },
100+
() => []
101+
);
97102
snapShotRef.current[bufferIndex] = false;
98103
}
99104
prevCanvasCountRef.current = canvasCount;
100105
}
101106

102-
// Process incoming data for each canvas
103-
currentBuffer.forEach((buffer, i) => {
104-
if (buffer.length >= dataPointCountRef.current ||
105-
(!pauseRef.current && buffer.length < dataPointCountRef.current)) {
107+
// Process incoming data for each selected channel
108+
selectedChannelsRef.current.forEach((channelNumber, i) => {
109+
// Ensure currentBuffer[i] exists
110+
if (!currentBuffer[i]) {
106111
currentBuffer[i] = [];
107112
}
108-
currentBuffer[i].push(incomingData[i + 1]);
113+
114+
// Clear buffer if needed
115+
if (currentBuffer[i].length >= dataPointCountRef.current ||
116+
(!pauseRef.current && currentBuffer[i].length < dataPointCountRef.current)) {
117+
currentBuffer[i] = [];
118+
}
119+
120+
// Safely access incoming data
121+
if (channelNumber >= 0 && channelNumber < incomingData.length) {
122+
currentBuffer[i].push(incomingData[channelNumber]);
123+
} else {
124+
console.warn(`Invalid channel number ${channelNumber} or missing data`);
125+
currentBuffer[i].push(0); // Push default value if data is missing
126+
}
109127
});
110128

111129
// Update snapshot and buffer index when data is ready
112-
if (currentBuffer[0].length >= dataPointCountRef.current) {
130+
if (currentBuffer[0] && currentBuffer[0].length >= dataPointCountRef.current) {
113131
snapShotRef.current[activeBufferIndexRef.current] = true;
114132
activeBufferIndexRef.current = (activeBufferIndexRef.current + 1) % 6;
115133
snapShotRef.current[activeBufferIndexRef.current] = false;
116134
}
117135

118136
// Update data indices for referencing past buffers
119137
dataIndicesRef.current = Array.from(
120-
{ length: 6 },
121-
(_, i) => (activeBufferIndexRef.current - i + 6) % 6
138+
{ length: 5 },
139+
(_, i) => (activeBufferIndexRef.current - i + 5) % 6
122140
);
123141
};
124142

@@ -367,43 +385,37 @@ const Canvas = forwardRef(
367385

368386

369387
const updatePlotSnapshot = (currentSnapshot: number) => {
370-
for (let i = 0; i < canvasCount; i++) {
371-
wglPlots.forEach((wglp, index) => {
372-
if (wglp) {
373-
try {
374-
wglp.gScaleY = Zoom; // Adjust the zoom value
375-
} catch (error) {
376-
console.error(
377-
`Error setting gScaleY for WebglPlot instance at index ${index}:`,
378-
error
379-
);
380-
}
381-
} else {
382-
console.warn(`WebglPlot instance at index ${index} is undefined.`);
388+
const currentSelectedChannels = selectedChannelsRef.current;
389+
390+
currentSelectedChannels.forEach((channelNumber, i) => {
391+
const wglp = wglPlots[i];
392+
if (wglp) {
393+
try {
394+
wglp.gScaleY = Zoom;
395+
} catch (error) {
396+
console.error(`Error setting gScaleY for WebglPlot instance at index ${i}:`, error);
383397
}
384-
});
385-
if (
386-
array3DRef.current &&
398+
}
399+
400+
if (array3DRef.current &&
387401
dataIndicesRef.current &&
388402
dataIndicesRef.current[currentSnapshot] !== undefined &&
389-
array3DRef.current[dataIndicesRef.current[currentSnapshot]] !== undefined
390-
) {
391-
const yArray = new Float32Array(array3DRef.current[dataIndicesRef.current[currentSnapshot]][i]);
392-
// Check if the line exists
403+
array3DRef.current[dataIndicesRef.current[currentSnapshot]] &&
404+
array3DRef.current[dataIndicesRef.current[currentSnapshot]][i]) {
405+
406+
const channelData = array3DRef.current[dataIndicesRef.current[currentSnapshot]][i];
407+
const yArray = new Float32Array(channelData);
408+
393409
const line = linesRef.current[i];
394410
if (line) {
395-
line.shiftAdd(yArray); // Efficiently add new points
411+
line.shiftAdd(yArray);
396412
} else {
397413
console.error(`Line at index ${i} is undefined or null.`);
398414
}
399-
400-
} else {
401-
console.warn("One of the references is undefined or invalid");
402415
}
416+
});
403417

404-
405-
}
406-
wglPlots.forEach((wglp) => wglp.update()); // Redraw the plots
418+
wglPlots.forEach((wglp) => wglp.update());
407419
};
408420

409421
useEffect(() => {

0 commit comments

Comments
 (0)