Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recording Data in all three modules! #77

Merged
merged 21 commits into from
Dec 24, 2019
Merged

Recording Data in all three modules! #77

merged 21 commits into from
Dec 24, 2019

Conversation

kylemath
Copy link
Owner

@kylemath kylemath commented Dec 23, 2019

This addresses the goals of #14
We have a data save button at the end of each page, this uses the current page settings and pipe to save a csv file with each row showing a processed segment of data, the next row shows the next segment.
The output files have unique names for the different modules, and have a column at the start with a unique time code.
The files also have a header saying what is in each row
Record button is only active once connected, and once pressed pops up a window explaining that it is recording and a csv file will download when done

TODO: right now it records for 50 epochs. Currently epochs are 4 seconds long, but user can change that. We probably want to preset a time, or have the user select it from a menu, in minutes, and then convert that to number of epochs depending on the epoch length
.
Frequency spectra:

Screen Shot 2019-12-22 at 11 38 37 PM

Screen Shot 2019-12-22 at 11 38 44 PM

Raw Data:
Screen Shot 2019-12-23 at 12 21 49 AM
Screen Shot 2019-12-23 at 12 21 37 AM

@kylemath kylemath requested a review from korymath December 23, 2019 07:32

export function PageSwitcher() {

const [introData, setIntroData] = useState(emptyChannelData)
const [rawData, setRawData] = useState(emptyChannelData);
const [spectraData, setSpectraData] = useState(emptyChannelData);
const [bandsData, setBandsData] = useState(emptyChannelData);
const [recordPop, setRecordPop] = useState(false);
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the pop up when recording (modal window)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work getting a modal window pop'ped.

@@ -51,6 +62,24 @@ export function PageSwitcher() {
{ label: translations.types.bands, value: translations.types.bands }
];

function buildPipe(value) {
Copy link
Owner Author

@kylemath kylemath Dec 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not get the case by case pipe building working in this pull request, but I did move this to a function and have it commented out for now so we can try again in a different branch/PR

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

case by case pipe building working

Is the assumption that this will significantly improve performance?
Is there an associated issue for this fix?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes assumption is it will take less memory
#79

@@ -72,6 +101,106 @@ export function PageSwitcher() {
}
}

function saveToCSV(value) {
const numSamplesToSave = 50;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what we will need to adjust depending on time desired and epoch length

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sampling rate will always be 256Hz unless it is a Muse 1 in which case it will be 220Hz.

So, time desired would be

recording_time_length = num_samples / sample_rate ... right?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it isn't the sampling rate of the EEG, it is the interval between consecutive epochs of data in neurosity pipes

next(x) {
dataToSave.push(
"Timestamp (ms),",
generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return "ch0_" + f + "ms"}) + ",",
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tediously creating the headers for each csv types

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was for sure the hardest part and you have done great work making it useful for the user. Laudable efforts!

break;
case translations.types.spectra:
//take one sample to get header info
localObservable$ = window.multicastSpectra$.pipe(
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the first two we subscribe and take 1 sample to get some info for the header


localObservable$.subscribe({
next(x) {
dataToSave.push(Date.now() + "," + Object.values(x).join(",") + "\n");
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

push values with commas between them to file

dataToSave,
{type: "text/plain;charset=utf-8"}
);
saveAs(blob, value + "_Recording.csv");
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

custom names for each module

// Build the data source from the data source
// Build the data source
console.log("Starting to build the data pipes from the data source...");
buildPipe(selected);
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now we call this function here instead of building each one

@@ -168,7 +294,92 @@ export function PageSwitcher() {
}
}

return (
function renderRecord() {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the record button and popup when it is pressed

if (selected === translations.types.intro) {
console.log('No record on intro')
return null
} else if (selected === translations.types.raw) {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if recording raw data there is a suggested setting explained for the user

@@ -299,23 +299,23 @@ export function EEGEdu(channels) {
<Card.Section>
<p>
{specificTranslations.credits1}
<a href="http://learn.neurotechedu.com/">NeurotechEdu. </a>
<Link url="http://learn.neurotechedu.com/">NeurotechEdu. </Link>
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reformatted links for polaris

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there other links that we will need to change to conform?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not yet

).map(function(each_element) {
return Number(each_element.toFixed(0));
});
export function generateXTics(srate, duration, reverse = true) {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a flag so this could generate increasing or decreasing tics, which we use in the graph as decreasing (default so the old code still works) but in the csv as increasing so false flag is used

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice default value to a function, and good use of the export function to make it useable elsewhere!

@korymath
Copy link
Collaborator

Finally getting a chance to run through this PR today with some internet.

Copy link
Collaborator

@korymath korymath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Nice work on building out the save functionality.

@korymath
Copy link
Collaborator

@kylemath Things look good to me and work for each tab. Nice work.

The modal window covers the ploted data, so we lose the neurofeedback aspect of it. Also, I wonder if it is going to be covered,.... should we plause plotting during saving? Or how should that play out?

@korymath
Copy link
Collaborator

That modal window is so slick. @keyfer is going to lose his mind.

Copy link
Owner Author

@kylemath kylemath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, so lets merge this one and then focus on how long we record for and what gets shown on the screen when that happens in a new PR, it will likely be different for different module, and a whole new module can now be made by just changing what is on the screen during recording ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants