Skip to content

Commit ba9977b

Browse files
committed
Added 0.1 filter
1 parent f3e8928 commit ba9977b

File tree

3 files changed

+117
-42
lines changed

3 files changed

+117
-42
lines changed

src/app/npg-lite/page.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { saveAs } from "file-saver";
1111
import { WebglPlot, ColorRGBA, WebglLine } from "webgl-plot";
1212
import Navbar from "@/components/Navbar";
1313
import { Button } from "@/components/ui/button";
14-
import { EXGFilter, Notch } from '@/components/filters';
14+
import { EXGFilter, Notch, HighPassFilter } from '@/components/filters';
1515
import {
1616
Popover,
1717
PopoverContent,
@@ -98,6 +98,7 @@ const NPG_Ble = () => {
9898
setIsDisplay(newPauseState);
9999
pauseRef.current = newPauseState;
100100
};
101+
const samplesReceivedRef = useRef(0);
101102
const createCanvasElements = () => {
102103
const container = canvasContainerRef.current;
103104
if (!container) {
@@ -332,7 +333,6 @@ const NPG_Ble = () => {
332333

333334

334335
let prevSampleCounter: number | null = null;
335-
let samplesReceived = 0;
336336
let channelData: number[] = [];
337337
const notchFilters = Array.from(
338338
{ length: maxCanvasElementCountRef.current },
@@ -342,14 +342,20 @@ const NPG_Ble = () => {
342342
{ length: maxCanvasElementCountRef.current },
343343
() => new EXGFilter()
344344
);
345+
const pointoneFilter = Array.from(
346+
{ length: maxCanvasElementCountRef.current },
347+
() => new HighPassFilter()
348+
);
345349

346350
notchFilters.forEach((filter) => {
347351
filter.setbits(sampingrateref.current);
348352
});
349353
EXGFilters.forEach((filter) => {
350354
filter.setbits("12", sampingrateref.current);
351355
});
352-
356+
pointoneFilter.forEach((filter) => {
357+
filter.setSamplingRate(sampingrateref.current);
358+
});
353359

354360
// Inside your component
355361
const processSample = useCallback((dataView: DataView): void => {
@@ -376,7 +382,7 @@ const NPG_Ble = () => {
376382
const sample = dataView.getInt16(1 + (channel * 2), false);
377383
channelData.push(
378384
notchFilters[channel].process(
379-
EXGFilters[channel].process(sample, appliedEXGFiltersRef.current[channel]),
385+
EXGFilters[channel].process(pointoneFilter[channel].process(sample), appliedEXGFiltersRef.current[channel]),
380386
appliedFiltersRef.current[channel]
381387
)
382388
);
@@ -410,7 +416,7 @@ const NPG_Ble = () => {
410416
}
411417

412418
channelData = [];
413-
samplesReceived++;
419+
samplesReceivedRef.current += 1;
414420
}, [
415421
canvasElementCountRef.current, selectedChannels, timeBase
416422
]);
@@ -470,7 +476,13 @@ const NPG_Ble = () => {
470476
await dataChar.startNotifications();
471477
dataChar.addEventListener("characteristicvaluechanged", handleNotification);
472478
setIsConnected(true);
473-
479+
setInterval(() => {
480+
if (samplesReceivedRef.current === 0) {
481+
disconnect();
482+
window.location.reload();
483+
}
484+
samplesReceivedRef.current = 0;
485+
}, 1000);
474486
} catch (error) {
475487
console.log("Error: " + (error instanceof Error ? error.message : error));
476488
setIsLoading(false);

src/components/Connection.tsx

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import React, { useState, useRef, useCallback, useEffect } from "react";
33
import { Button } from "./ui/button";
44
import { Input } from "./ui/input";
5-
import { EXGFilter, Notch } from './filters';
5+
import { EXGFilter, Notch,HighPassFilter } from './filters';
66
import { useTheme } from "next-themes";
77
import { useRouter } from "next/navigation"; // Import useRouter
88
import { getCustomColor, lightThemeColors } from './Colors';
@@ -1014,20 +1014,24 @@ const Connection: React.FC<ConnectionProps> = ({
10141014
}
10151015
// Add these to your component's refs
10161016
const notchFiltersRef = useRef<Notch[]>([]);
1017+
const onehighRef = useRef<HighPassFilter[]>([]);
10171018
const EXGFiltersRef = useRef<EXGFilter[]>([]);
10181019
const connectedDeviceRef = useRef<any | null>(null); // UseRef for device tracking
10191020

1020-
// Initialize filters when device connects or channel count changes
1021-
useEffect(() => {
1022-
if (maxCanvasElementCountRef.current > 0) {
1023-
notchFiltersRef.current = Array.from({ length: maxCanvasElementCountRef.current }, () => new Notch());
1024-
EXGFiltersRef.current = Array.from({ length: maxCanvasElementCountRef.current }, () => new EXGFilter());
1025-
1026-
// Set initial filter parameters
1027-
notchFiltersRef.current.forEach(filter => filter.setbits(500));
1028-
EXGFiltersRef.current.forEach(filter => filter.setbits("12", 500));
1029-
}
1030-
}, [maxCanvasElementCountRef.current]);
1021+
// // Initialize filters when device connects or channel count changes
1022+
// useEffect(() => {
1023+
// if (maxCanvasElementCountRef.current > 0) {
1024+
// notchFiltersRef.current = Array.from({ length: maxCanvasElementCountRef.current }, () => new Notch());
1025+
// EXGFiltersRef.current = Array.from({ length: maxCanvasElementCountRef.current }, () => new EXGFilter());
1026+
// onehighRef.current = Array.from({ length: maxCanvasElementCountRef.current }, () => new HighPassFilter());
1027+
1028+
// // Set initial filter parameters
1029+
// // onehighRef.current.forEach(filter => filter.setbits(500));
1030+
// onehighRef.current.forEach(filter => filter.setSamplingRate(500));
1031+
// notchFiltersRef.current.forEach(filter => filter.setbits(500));
1032+
// EXGFiltersRef.current.forEach(filter => filter.setbits("12", 500));
1033+
// }
1034+
// }, [maxCanvasElementCountRef.current]);
10311035
const processSample = useCallback((dataView: DataView): void => {
10321036
if (dataView.byteLength !== SINGLE_SAMPLE_LEN) {
10331037
console.log("Unexpected sample length: " + dataView.byteLength);
@@ -1052,7 +1056,7 @@ const Connection: React.FC<ConnectionProps> = ({
10521056
channelData.push(
10531057
notchFiltersRef.current[channel].process(
10541058
EXGFiltersRef.current[channel].process(
1055-
sample,
1059+
onehighRef.current[channel].process(sample),
10561060
appliedEXGFiltersRef.current[channel] || 0
10571061
),
10581062
appliedFiltersRef.current[channel] || 0
@@ -1132,6 +1136,8 @@ const Connection: React.FC<ConnectionProps> = ({
11321136
// Initialize filters
11331137
notchFiltersRef.current = Array.from({ length: 3 }, () => new Notch());
11341138
EXGFiltersRef.current = Array.from({ length: 3 }, () => new EXGFilter());
1139+
onehighRef.current = Array.from({ length: 3 }, () => new HighPassFilter());
1140+
onehighRef.current.forEach(filter => filter.setSamplingRate(500));
11351141
notchFiltersRef.current.forEach(filter => filter.setbits(500));
11361142
EXGFiltersRef.current.forEach(filter => filter.setbits("12", 500));
11371143
const service = await server.getPrimaryService(SERVICE_UUID);
@@ -1158,7 +1164,6 @@ const Connection: React.FC<ConnectionProps> = ({
11581164
sampingrateref.current = 500;
11591165
setInterval(() => {
11601166
if (samplesReceivedRef.current === 0) {
1161-
console.log("hello");
11621167
disconnect();
11631168
window.location.reload();
11641169
}
@@ -1286,9 +1291,13 @@ const Connection: React.FC<ConnectionProps> = ({
12861291
let previousCounter: number | null = null; // Variable to store the previous counter value for loss detection
12871292
const notchFilters = Array.from({ length: maxCanvasElementCountRef.current }, () => new Notch());
12881293
const EXGFilters = Array.from({ length: maxCanvasElementCountRef.current }, () => new EXGFilter());
1294+
const pointoneFilter = Array.from({ length: maxCanvasElementCountRef.current }, () => new HighPassFilter());
12891295
notchFilters.forEach((filter) => {
12901296
filter.setbits(sampingrateref.current); // Set the bits value for all instances
12911297
});
1298+
pointoneFilter.forEach((filter) => {
1299+
filter.setSamplingRate(sampingrateref.current); // Set the bits value for all instances
1300+
});
12921301
EXGFilters.forEach((filter) => {
12931302
filter.setbits(detectedBitsRef.current.toString(), sampingrateref.current); // Set the bits value for all instances
12941303
});
@@ -1341,7 +1350,7 @@ const Connection: React.FC<ConnectionProps> = ({
13411350
channelData.push(
13421351
notchFilters[channel].process(
13431352
EXGFilters[channel].process(
1344-
value,
1353+
pointoneFilter[channel].process(value),
13451354
appliedEXGFiltersRef.current[channel]
13461355
),
13471356
appliedFiltersRef.current[channel]

src/components/filters.tsx

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,60 @@
99
// Note:
1010
// filter_gen.py provides C/C++ type functions which we have converted to TS
1111

12+
export class HighPassFilter {
13+
// State variables for the filter
14+
private z1: number;
15+
private z2: number;
16+
private x1: number;
17+
private currentSamplingRate: number;
18+
19+
constructor() {
20+
this.z1 = 0;
21+
this.z2 = 0;
22+
this.x1 = 0;
23+
this.currentSamplingRate = 0;
24+
}
25+
26+
// Set the sampling rate (250 or 500 Hz)
27+
setSamplingRate(samplingRate: number): void {
28+
this.currentSamplingRate = samplingRate;
29+
}
30+
31+
// Process the input sample through the appropriate high-pass filter
32+
process(input: number): number {
33+
let output = input;
34+
35+
switch (this.currentSamplingRate) {
36+
case 250:
37+
// High-Pass Butterworth IIR digital filter for 250Hz
38+
// Sampling rate: 250.0 Hz, frequency: 1.0 Hz.
39+
{
40+
this.x1 = output - (-1.99644570 * this.z1) - (0.99645200 * this.z2);
41+
output = (0.99822443 * this.x1) + (-1.99644885 * this.z1) + (0.99822443 * this.z2);
42+
this.z2 = this.z1;
43+
this.z1 = this.x1;
44+
}
45+
break;
46+
47+
case 500:
48+
// High-Pass Butterworth IIR digital filter for 500Hz
49+
// Sampling rate: 500.0 Hz, frequency: 1.0 Hz.
50+
{
51+
this.x1 = output - (-1.99822285 * this.z1) - (0.99822443 * this.z2);
52+
output = (0.99911182 * this.x1) + (-1.99822364 * this.z1) + (0.99911182 * this.z2);
53+
this.z2 = this.z1;
54+
this.z1 = this.x1;
55+
}
56+
break;
57+
58+
default:
59+
throw new Error(`Unsupported sampling rate: ${this.currentSamplingRate}. Only 250Hz and 500Hz are supported.`);
60+
}
61+
62+
return output;
63+
}
64+
}
65+
1266
//Notch Filter 50Hz/60Hz
1367
export class EXGFilter {
1468
// Properties to hold the state of the filter
@@ -21,7 +75,7 @@ export class EXGFilter {
2175
private bits: string | null;
2276
private bitsPoints: number;
2377
private yScale: number;
24-
private currentSamplingRate:number;
78+
private currentSamplingRate: number;
2579

2680

2781
constructor() {
@@ -33,9 +87,9 @@ export class EXGFilter {
3387
this.x3 = 0;
3488
this.x4 = 0;
3589
this.bits = null;
36-
this.bitsPoints=0;
37-
this.yScale=0;
38-
this.currentSamplingRate=0;
90+
this.bitsPoints = 0;
91+
this.yScale = 0;
92+
this.currentSamplingRate = 0;
3993
}
4094
//bits-
4195
//1.500
@@ -46,45 +100,45 @@ export class EXGFilter {
46100
//3.EEG
47101
//4.EMG
48102
// function to apply the
49-
setbits(bits: string,currentSamplingRate:number): void {
50-
this.currentSamplingRate=currentSamplingRate;
103+
setbits(bits: string, currentSamplingRate: number): void {
104+
this.currentSamplingRate = currentSamplingRate;
51105
this.bits = bits;
52-
this.bitsPoints = Math.pow(2,parseInt(bits)
106+
this.bitsPoints = Math.pow(2, parseInt(bits)
53107
); // Adjust according to your ADC resolution
54108
this.yScale = 2 / this.bitsPoints;
55109
}
56110

57111
process(input: number, type: number): number {
58-
if(!type) return (input - this.bitsPoints / 2) * this.yScale;
112+
if (!type) return input * this.yScale;
59113
let output = input;
60-
let chData=0;
114+
let chData = 0;
61115
switch (this.currentSamplingRate) {
62116
//bitsrate 500Hz
63-
case 500:
117+
case 500:
64118
switch (type) {
65119
case 1: // ECG Sampling rate: 500.0 Hz, frequency: 30.0 Hz.
66120
// Filter is order 2, implemented as second-order sections (biquads).
67121
this.x1 = output - (-1.47548044 * this.z1) - (0.58691951 * this.z2);
68122
output = 0.02785977 * this.x1 + 0.05571953 * this.z1 + 0.02785977 * this.z2;
69123
this.z2 = this.z1;
70124
this.z1 = this.x1;
71-
chData = (output - this.bitsPoints / 2) * this.yScale;
125+
chData = output * this.yScale;
72126
break;
73127
case 2: // EOG Sampling rate: 500.0 Hz, frequency: 10.0 Hz.
74128
// Filter is order 2, implemented as second-order sections (biquads).
75129
this.x2 = output - (-1.82269493 * this.z1) - (0.83718165 * this.z2);
76130
output = 0.00362168 * this.x2 + 0.00724336 * this.z1 + 0.00362168 * this.z2;
77131
this.z2 = this.z1;
78132
this.z1 = this.x2;
79-
chData = (output - this.bitsPoints / 2) * this.yScale;
133+
chData = output * this.yScale;
80134
break;
81135
case 3: // EEG Sampling rate: 500.0 Hz, frequency: 45.0 Hz.
82136
// Filter is order 2, implemented as second-order sections (biquads).
83137
this.x3 = output - (-0.51930341 * this.z1) - (0.21965398 * this.z2);
84138
output = 0.17508764 * this.x3 + 0.35017529 * this.z1 + 0.17508764 * this.z2;
85139
this.z2 = this.z1;
86140
this.z1 = this.x3;
87-
chData = (output - this.bitsPoints / 2) * this.yScale;
141+
chData = output * this.yScale;
88142
break;
89143
case 4: // EMG Sampling rate: 500.0 Hz, frequency: 70.0 Hz.
90144
// Filter is order 2, implemented as second-order sections (biquads).
@@ -107,7 +161,7 @@ export class EXGFilter {
107161
output = 0.09131490 * this.x1 + 0.18262980 * this.z1 + 0.09131490 * this.z2;
108162
this.z2 = this.z1;
109163
this.z1 = this.x1;
110-
chData = (output - this.bitsPoints / 2) * this.yScale;
164+
chData = output * this.yScale;
111165
break;
112166

113167
case 2: // EOG Sampling rate: 250.0 Hz, frequency: 10.0 Hz.
@@ -116,7 +170,7 @@ export class EXGFilter {
116170
output = 0.01335920 * this.x2 + 0.02671840 * this.z1 + 0.01335920 * this.z2;
117171
this.z2 = this.z1;
118172
this.z1 = this.x2;
119-
chData = (output - this.bitsPoints / 2) * this.yScale;
173+
chData = output * this.yScale;
120174
break;
121175

122176
case 3: // EEG Sampling rate: 250.0 Hz, frequency: 45.0 Hz.
@@ -125,7 +179,7 @@ export class EXGFilter {
125179
output = 0.17508764 * this.x3 + 0.35017529 * this.z1 + 0.17508764 * this.z2;
126180
this.z2 = this.z1;
127181
this.z1 = this.x3;
128-
chData = (output - this.bitsPoints / 2) * this.yScale;
182+
chData = output * this.yScale;
129183
break;
130184

131185
case 4: // EMG Sampling rate: 250.0 Hz, frequency: 70.0 Hz.
@@ -156,7 +210,7 @@ export class Notch {
156210
private z2_2: number;
157211
private x_1: number;
158212
private x_2: number;
159-
private currentSamplingRate:number;
213+
private currentSamplingRate: number;
160214

161215

162216
constructor() {
@@ -167,17 +221,17 @@ export class Notch {
167221
this.z2_2 = 0;
168222
this.x_1 = 0;
169223
this.x_2 = 0;
170-
this.currentSamplingRate=0;
224+
this.currentSamplingRate = 0;
171225

172226
}
173227

174-
setbits(currentSamplingRate:number): void {
175-
this.currentSamplingRate=currentSamplingRate;
228+
setbits(currentSamplingRate: number): void {
229+
this.currentSamplingRate = currentSamplingRate;
176230
}
177231

178232
// Method to apply the filter
179233
process(input: number, type: number): number {
180-
if(!type) return input;
234+
if (!type) return input;
181235
let output = input;
182236
switch (this.currentSamplingRate) {
183237
case 500: // 500Hz

0 commit comments

Comments
 (0)