-
Notifications
You must be signed in to change notification settings - Fork 39
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
Conversation
|
||
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); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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"}) + ",", |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reformatted links for polaris
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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!
Finally getting a chance to run through this PR today with some internet. |
There was a problem hiding this 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.
@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? |
That modal window is so slick. @keyfer is going to lose his mind. |
There was a problem hiding this 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 ;)
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:
Raw Data: