Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8,457 changes: 3,899 additions & 4,558 deletions package-lock.json

Large diffs are not rendered by default.

45 changes: 23 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,27 @@
"start": "next start",
"lint": "next lint",
"export": "next export",
"distDir": "out"
"distDir": "out",
"generate-manifest": "ts-node scripts/generateManifest.mjs"
},
"dependencies": {
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-hover-card": "^1.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.0.7",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@types/p5": "^1.7.6",
"@types/w3c-web-serial": "^1.0.6",
"class-variance-authority": "^0.7.0",
Expand All @@ -38,20 +39,20 @@
"fft-js": "^0.0.12",
"file-saver": "^2.0.5",
"framer-motion": "^11.5.4",
"glob": "10.5.0",
"html2canvas": "^1.4.1",
"jszip": "^3.10.1",
"lucide-react": "^0.460.0",
"next": "^15.3.3",
"next": "^16.0.3",
"next-pwa": "^5.6.0",
"next-themes": "^0.3.0",
"react": "^18",
"react-dom": "^18",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-icons": "^5.3.0",
"smoothie": "^1.36.1",
"sonner": "^1.5.0",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"tailwindcss-textshadow": "^2.1.3",
"uuid": "^11.0.3",
"webgl-plot": "^0.7.2"
},
Expand Down
Binary file removed src/app/favicon.ico
Binary file not shown.
6 changes: 2 additions & 4 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<link rel="manifest" href={`${basePath}/manifest.json`} /> {/* ✅ Dynamic manifest */}
</head>
<head><link rel="manifest" href={`${basePath}/manifest.json`} /></head>
<body
className={cn(
lobsterTwo.variable,
Expand All @@ -70,4 +68,4 @@ export default function RootLayout({
</body>
</html>
);
}
}
8 changes: 4 additions & 4 deletions src/app/muscle-strength/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { useTheme } from "next-themes";

const MuscleStrength = () => {
const [isDisplay, setIsDisplay] = useState<boolean>(true); // Display state
const sampingrateref = useRef<number>(250);
const samplingrateref = useRef<number>(250);
const [isFilterPopoverOpen, setIsFilterPopoverOpen] = useState(false);
const connectedDeviceRef = useRef<any | null>(null); // UseRef for device tracking
const canvasContainerRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -644,7 +644,7 @@ const MuscleStrength = () => {
forceUpdate(); // Trigger re-render
};
useEffect(() => {
dataPointCountRef.current = (sampingrateref.current * timeBase);
dataPointCountRef.current = (samplingrateref.current * timeBase);
}, [timeBase]);
const zoomRef = useRef(Zoom);

Expand Down Expand Up @@ -675,10 +675,10 @@ const MuscleStrength = () => {
);

notchFilters.forEach((filter) => {
filter.setbits(sampingrateref.current);
filter.setbits(samplingrateref.current);
});
EXGFilters.forEach((filter) => {
filter.setbits("12", sampingrateref.current);
filter.setbits("12", samplingrateref.current);
});
function processSample(dataView: DataView): void {
if (dataView.byteLength !== SINGLE_SAMPLE_LEN) {
Expand Down
25 changes: 16 additions & 9 deletions src/app/npg-lite/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const NPG_Ble = () => {
const [recordingElapsedTime, setRecordingElapsedTime] = useState<number>(0); // State to store the recording duration
const [customTimeInput, setCustomTimeInput] = useState<string>(""); // State to store the custom stop time input
const existingRecordRef = useRef<any | undefined>(undefined);
const sampingrateref = useRef<number>(500);
const samplingrateref = useRef<number>(500);
const recordingStartTimeRef = useRef<number>(0);
const endTimeRef = useRef<number | null>(null); // Ref to store the end time of the recording
const canvasElementCountRef = useRef<number>(1);
Expand Down Expand Up @@ -105,6 +105,10 @@ const NPG_Ble = () => {
return; // Exit if the ref is null
}

// Ensure dataPointCount is calculated from current sampling rate and timeBase
const dpCount = samplingrateref.current * timeBase;
dataPointCountRef.current = dpCount;

currentSweepPos.current = new Array(numChannels).fill(0);
sweepPositions.current = new Array(numChannels).fill(0);

Expand Down Expand Up @@ -137,7 +141,7 @@ const NPG_Ble = () => {
const opacityDarkMinor = "0.05";
const opacityLightMajor = "0.4";
const opacityLightMinor = "0.1";
const distanceminor = sampingrateref.current * 0.04;
const distanceminor = samplingrateref.current * 0.04;
const numGridLines = (500 * 4) / distanceminor;

for (let j = 1; j < numGridLines; j++) {
Expand Down Expand Up @@ -188,10 +192,10 @@ const NPG_Ble = () => {
wglp.gScaleY = Zoom;


const line = new WebglLine(getLineColor(channelNumber, theme), dataPointCountRef.current);
const line = new WebglLine(getLineColor(channelNumber, theme), dpCount);
wglp.gOffsetY = 0;
line.offsetY = 0;
line.lineSpaceX(-1, 2 / dataPointCountRef.current);
line.lineSpaceX(-1, 2 / dpCount);

wglp.addLine(line);
newLines.push(line);
Expand Down Expand Up @@ -314,7 +318,7 @@ const NPG_Ble = () => {
forceUpdate(); // Trigger re-render
};
useEffect(() => {
dataPointCountRef.current = (sampingrateref.current * timeBase);
dataPointCountRef.current = (samplingrateref.current * timeBase);
}, [timeBase]);
const zoomRef = useRef(Zoom);

Expand All @@ -338,13 +342,13 @@ const NPG_Ble = () => {
const exgFiltersRef = useRef(Array.from({ length: maxCanvasElementCountRef.current }, () => new EXGFilter()));
const pointoneFilterRef = useRef(Array.from({ length: maxCanvasElementCountRef.current }, () => new HighPassFilter()));
notchFiltersRef.current.forEach((filter) => {
filter.setbits(sampingrateref.current);
filter.setbits(samplingrateref.current);
});
exgFiltersRef.current.forEach((filter) => {
filter.setbits("12", sampingrateref.current);
filter.setbits("12", samplingrateref.current);
});
pointoneFilterRef.current.forEach((filter) => {
filter.setSamplingRate(sampingrateref.current);
filter.setSamplingRate(samplingrateref.current);
});

// Inside your component
Expand Down Expand Up @@ -786,7 +790,6 @@ const NPG_Ble = () => {

const toggleChannel = (channelIndex: number) => {
setSelectedChannels((prevSelected) => {
setManuallySelected(true);
const updatedChannels = prevSelected.includes(channelIndex)
? prevSelected.filter((ch) => ch !== channelIndex)
: [...prevSelected, channelIndex];
Expand All @@ -799,6 +802,8 @@ const NPG_Ble = () => {

return sortedChannels;
});

setManuallySelected(true);
};


Expand Down Expand Up @@ -1509,6 +1514,7 @@ const NPG_Ble = () => {
<div className="relative w-[28rem] flex items-center rounded-lg py-2 border border-gray-300 dark:border-gray-600">
{/* Button for setting Time Base to 1 */}
<button
type="button"
className="text-gray-700 dark:text-gray-400 mx-1 px-2 py-1 border rounded hover:bg-gray-200 dark:hover:bg-gray-700"
onClick={() => setTimeBase(1)}
>
Expand All @@ -1527,6 +1533,7 @@ const NPG_Ble = () => {
/>
{/* Button for setting Time Base to 10 */}
<button
type="button"
className="text-gray-700 dark:text-gray-400 mx-2 px-2 py-1 border rounded hover:bg-gray-200 dark:hover:bg-gray-700"
onClick={() => setTimeBase(10)}
>
Expand Down
10 changes: 5 additions & 5 deletions src/components/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const Canvas = forwardRef(
ref
) => {
const { theme } = useTheme();
let previousCounter: number | null = null; // Variable to store the previous counter value for loss detection
const previousCounterRef = useRef<number | null>(null); // Variable to store the previous counter value for loss detection
const canvasContainerRef = useRef<HTMLDivElement>(null);
const [numChannels, setNumChannels] = useState<number>(selectedChannels.length);
const dataPointCountRef = useRef<number>(2000); // To track the calculated value
Expand Down Expand Up @@ -165,17 +165,17 @@ const Canvas = forwardRef(
processIncomingData(data);
updatePlots(data, Zoom);
}
if (previousCounter !== null) {
if (previousCounterRef.current !== null) {
// If there was a previous counter value
const expectedCounter: number = (previousCounter + 1) % 256; // Calculate the expected counter value
const expectedCounter: number = (previousCounterRef.current + 1) % 256; // Calculate the expected counter value
if (data[0] !== expectedCounter) {
// Check for data loss by comparing the current counter with the expected counter
console.warn(
`Data loss detected in canvas! Previous counter: ${previousCounter}, Current counter: ${data[0]}`
`Data loss detected in canvas! Previous counter: ${previousCounterRef.current}, Current counter: ${data[0]}`
);
}
}
previousCounter = data[0]; // Update the previous counter with the current counter
previousCounterRef.current = data[0]; // Update the previous counter with the current counter
},
}),
[Zoom, numChannels, timeBase]
Expand Down
60 changes: 38 additions & 22 deletions src/components/Connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const Connection: React.FC<ConnectionProps> = ({
const existingRecordRef = useRef<any | undefined>(undefined);
const devicenameref = useRef<string>("");
const [deviceReady, setDeviceReady] = useState(false);
const sampingrateref = useRef<number>(0);
const samplingrateref = useRef<number>(0);
const [open, setOpen] = useState(false);
const [openfft, setOpenfft] = useState(false);
const [isPauseState, setIsPauseState] = useState(false);
Expand Down Expand Up @@ -280,8 +280,8 @@ const Connection: React.FC<ConnectionProps> = ({


const toggleChannel = (channelIndex: number) => {
// Mark as manually selected and update selectedChannels
setSelectedChannels((prevSelected) => {
setManuallySelected(true);
const updatedChannels = prevSelected.includes(channelIndex)
? prevSelected.filter((ch) => ch !== channelIndex)
: [...prevSelected, channelIndex];
Expand All @@ -292,24 +292,29 @@ const Connection: React.FC<ConnectionProps> = ({
sortedChannels.push(1);
}

// Retrieve saved devices from localStorage
const savedPorts = JSON.parse(localStorage.getItem('savedDevices') || '[]');
const portInfo = portRef.current?.getInfo();

if (portInfo) {
const deviceIndex = savedPorts.findIndex(
(saved: SavedDevice) => saved.deviceName === devicenameref.current
);
// Retrieve saved devices from localStorage and persist selection
try {
const savedPorts = JSON.parse(localStorage.getItem('savedDevices') || '[]');
const portInfo = portRef.current?.getInfo();

if (deviceIndex !== -1) {
savedPorts[deviceIndex].selectedChannels = sortedChannels;
localStorage.setItem('savedDevices', JSON.stringify(savedPorts));
if (portInfo) {
const deviceIndex = savedPorts.findIndex(
(saved: SavedDevice) => saved.deviceName === devicenameref.current
);

if (deviceIndex !== -1) {
savedPorts[deviceIndex].selectedChannels = sortedChannels;
localStorage.setItem('savedDevices', JSON.stringify(savedPorts));
}
}
} catch (e) {
console.warn('Failed to persist selected channels:', e);
}

return sortedChannels;
});

setManuallySelected(true);
};

// Handle right arrow click (reset count and disable button if needed)
Expand Down Expand Up @@ -353,15 +358,24 @@ const Connection: React.FC<ConnectionProps> = ({
});
}
};
const setCanvasCountInWorker = (canvasCount: number) => {
const setCanvasCountInWorker = useCallback((canvasCount: number) => {
if (!workerRef.current) {
initializeWorker();
}
setCanvasCount(selectedChannels.length)
// Update parent canvasCount only when it differs to avoid unnecessary rerenders
const newCount = selectedChannels.length;
if (typeof setCanvasCount === 'function' && newCount !== canvasCount) {
setCanvasCount(newCount);
}

// Send canvasCount independently to the worker
workerRef.current?.postMessage({ action: 'setCanvasCount', canvasCount: canvasElementCountRef.current });
};
setCanvasCountInWorker(canvasElementCountRef.current);
}, [selectedChannels, setCanvasCount]);

// Run the canvas count sync after render (and when selectedChannels change)
useEffect(() => {
setCanvasCountInWorker(canvasElementCountRef.current);
}, [setCanvasCountInWorker]);

const setSelectedChannelsInWorker = (selectedChannels: number[]) => {
if (!workerRef.current) {
Expand Down Expand Up @@ -412,8 +426,10 @@ const Connection: React.FC<ConnectionProps> = ({

if (zipBlob) {
saveAs(zipBlob, 'ChordsWeb.zip');
toast.success("Data successfully downloaded as ZIP.");
} else if (error) {
console.error(error);
toast.error(`Error while creating ZIP: ${error}`);
}
};
}
Expand Down Expand Up @@ -559,7 +575,7 @@ const Connection: React.FC<ConnectionProps> = ({

if (sampling_rate) {
setCurrentSamplingRate(sampling_rate);
sampingrateref.current = sampling_rate;
samplingrateref.current = sampling_rate;
}

return {
Expand Down Expand Up @@ -1155,7 +1171,7 @@ const Connection: React.FC<ConnectionProps> = ({
maxCanvasElementCountRef.current = 3;
setSelectedChannel(1);
setCurrentSamplingRate(500);
sampingrateref.current = 500;
samplingrateref.current = 500;
setInterval(() => {
if (samplesReceivedRef.current === 0) {
disconnect();
Expand Down Expand Up @@ -1287,13 +1303,13 @@ const Connection: React.FC<ConnectionProps> = ({
const EXGFilters = Array.from({ length: maxCanvasElementCountRef.current }, () => new EXGFilter());
const pointoneFilter = Array.from({ length: maxCanvasElementCountRef.current }, () => new HighPassFilter());
notchFilters.forEach((filter) => {
filter.setbits(sampingrateref.current); // Set the bits value for all instances
filter.setbits(samplingrateref.current); // Set the bits value for all instances
});
pointoneFilter.forEach((filter) => {
filter.setSamplingRate(sampingrateref.current); // Set the bits value for all instances
filter.setSamplingRate(samplingrateref.current); // Set the bits value for all instances
});
EXGFilters.forEach((filter) => {
filter.setbits(detectedBitsRef.current.toString(), sampingrateref.current); // Set the bits value for all instances
filter.setbits(detectedBitsRef.current.toString(), samplingrateref.current); // Set the bits value for all instances
});
try {
while (isDeviceConnectedRef.current) {
Expand Down
Loading
Loading