/// <summary> /// The enumerator implementation /// </summary> public override IEnumerator <View> GetEnumerator() { IViewResult result; using (var logWriter = new StreamWriter(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "compexp_log_" + settings.SubjectName + DateTime.Now.ToString("MM dd yyyy H mm ss") + ".txt"))) using (var dataWriter = new StreamWriter(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "compexp_" + settings.SubjectName + DateTime.Now.ToString("MM dd yyyy H mm ss") + ".csv"))) { for (int i = 0; i < 2; i++) { yield return(new ChoiceView(new string[] { "Ready for Study Phase" }, out result)); //Present half the stimuli for study for (int j = 0 + i * (presentation.Count / 2); j < (presentation.Count / 2) * (i + 1); j++) { yield return(new TextView(presentation[j], this.settings.PresentationTime, GUIUtils.Constants.DISPLAY_FONT_LARGE)); yield return(new RestView(this.settings.RestTime)); } //Begin the practice phase yield return(new ChoiceView(new string[] { "Start EEG Recording" }, out result) { Text = "Click When Ready" }); var connected = true; // assume it's connected using (var invoker = new SingleThreadedInvoker()) using (var connectionListener = new EEGDataListener(invoker, s => connected = true, null, s => connected = false)) { // listen for a broken connection this.dataSource.AddListener(connectionListener); foreach (var view in this.GetViews(invoker, logWriter, dataWriter, i)) { if (connected) { yield return(view); } else { GUIUtils.Alert("Lost connection to headset!"); break; } } this.dataSource.RemoveListener(connectionListener); } } } }
//Generates the views by calling RunTrial private IEnumerable <View> GetViews(ISynchronizeInvoke invoker, StreamWriter logWriter, StreamWriter dataWriter, int round) { //Get a block of stimuli //var blocks = this.GetBlocks(this.class1, new Random()) // .Zip(this.GetBlocks(this.class2, new Random()), (b1, b2) => new[] { new { stimuli = b1, cls = 1 }, new { stimuli = b2, cls = 2 } }) // .SelectMany(x => x); //int blockCount = 1; var currentTrialEntries = new List <EEGDataEntry>(); //To do: Save the date/time earlier and use it for both this and the dataWriter. Put it in GetEnumerator and pass to GetViews using (var artifactListener = new EEGDataListener(invoker, null, data => { foreach (var entry in data) { if (entry.HasStimulusMarker()) { lock (currentTrialEntries) { currentTrialEntries.Add(entry); } } } }, null)) { this.dataSource.AddListener(artifactListener); //Display each block of stimuli for (int j = 0 + round * (settings.NumBlocks); j < (settings.NumBlocks) * (round + 1); j++) { logWriter.WriteLine("Current Class: {0}, Block Number: {1}", (j % 2 + 1), j); //yield return new TextView("Current Class: " + block.cls, 2500, GUIUtils.Constants.DISPLAY_FONT_LARGE); IViewResult result; yield return(new ChoiceView(new string[] { "Ready for next block" }, out result)); int limit = blocks[j].Count; for (int k = 0; k < limit; k++) { foreach (var view in RunTrial(blocks[j].RemoveRandom(), (j % 2 + 1), dataWriter, logWriter, currentTrialEntries)) { yield return(view); } } //blockCount++; } logWriter.WriteLine("Phase {0} Concluded.", round + 1); } }
//Generates the views by calling RunTrial private IEnumerable <View> GetCompViews(ISynchronizeInvoke invoker, StreamWriter logWriter, StreamWriter dataWriter) { var currentCompTrialEntries = new List <EEGDataEntry>(); using (var compartifactListener = new EEGDataListener(invoker, null, data => { foreach (var entry in data) { if (entry.HasStimulusMarker()) { lock (currentCompTrialEntries) { currentCompTrialEntries.Add(entry); } } } }, null)) { this.dataSource.AddListener(compartifactListener); //Display each block of stimuli for (int j = 0; j < settings.NumBlocks * 2; j++) { logWriter.WriteLine("Current Class: {0}, Block Number: {1}", (j % 2 + 1), j); //yield return new TextView("Current Class: " + block.cls, 2500, GUIUtils.Constants.DISPLAY_FONT_LARGE); IViewResult result; yield return(new ChoiceView(new string[] { "Ready for next block" }, out result)); int limit = blocks[j].Count; for (int k = 0; k < limit; k++) { foreach (var view in RunCompTrial(blocks[j].RemoveRandom(), (j % 2 + 1), dataWriter, logWriter, currentCompTrialEntries)) { yield return(view); } } } logWriter.WriteLine("Training Phase Concluded."); } }
/// <summary> /// Yields the experiment as a sequence of views /// </summary> public override IEnumerator <View> GetEnumerator() { // wait to begin yield return(new ChoiceView(new string[] { "Click anywhere to begin" })); var connected = new VolatileBool(true); // assume it's connected using (var invoker = new SingleThreadedInvoker()) using (var connectionListener = new EEGDataListener(invoker, s => connected.value = true, null, s => connected.value = false)) using (var logger = this.LogExperimentAndGetLogger()) // log the experiment using (var trialLogger = this.GetTrialWriter()) // logs each trial { // create the runtime var runtime = new Runtime(this) { Classifiers = this.ClassificationSchemes.Select(cs => new ClassifierManager(cs)).ToIArray(), Logger = logger, TrialLogger = trialLogger, }; // listen for a broken connection this.DataSource.AddListener(connectionListener); foreach (var view in this.GetViews(runtime)) { if (connected.value) { yield return(view); } else { GUIUtils.Alert(runtime.LogLine("Lost connection to headset!")); break; } } this.DataSource.RemoveListener(connectionListener); } }
/// <summary> /// The enumerator implementation /// </summary> public override IEnumerator <View> GetEnumerator() { IViewResult result; Random numgen = new Random(); RandomizedQueue <StudyTestPair> study = new RandomizedQueue <StudyTestPair>(); RandomizedQueue <StudyTestPair> quiz = new RandomizedQueue <StudyTestPair>(); RandomizedQueue <StudyTestPair> done = new RandomizedQueue <StudyTestPair>(); string filename = "adapt_data_" + settings.SubjectName + "_" + DateTime.Now.ToString("MM dd yyyy H mm ss") + ".csv"; using (var logWriter = new StreamWriter(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "adapt_log_" + settings.SubjectName + "_" + DateTime.Now.ToString("MM dd yyyy H mm ss") + ".txt"))) //If using MATLAB reference, the datawriter path must match the location of your MATLAB code using (var dataWriter = new StreamWriter(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), filename))) { yield return(new ChoiceView(new string[] { "Ready for Training Study Phase" }, out result)); //Present competition stimuli for study for (int j = 0; j < presentation.Count; j++) { yield return(new TextView(presentation[j], this.settings.PresentationTime, GUIUtils.Constants.DISPLAY_FONT_LARGE)); yield return(new RestView(this.settings.RestTime)); } //Begin the practice phase yield return(new ChoiceView(new string[] { "Start Training EEG Recording" }, out result) { Text = "Click When Ready" }); var compconnected = true; // assume it's connected using (var compinvoker = new SingleThreadedInvoker()) using (var compconnectionListener = new EEGDataListener(compinvoker, s => compconnected = true, null, s => compconnected = false)) { // listen for a broken connection this.dataSource.AddListener(compconnectionListener); foreach (var view in this.GetCompViews(compinvoker, logWriter, dataWriter)) { if (compconnected) { yield return(view); } else { GUIUtils.Alert("Lost connection to headset!"); break; } } this.dataSource.RemoveListener(compconnectionListener); } //Check that the person has sufficient training data if (numArt1 > 24 || numArt2 > 24) { yield return(new TextView("Error: Weeping Angel", settings.InstructionTime, GUIUtils.Constants.DISPLAY_FONT_LARGE)); } //MATLAB REFERENCE //matlab.Execute("cd c:\\Users\\Nicole\\Documents\\Matlab\\Thesis\\Adapt"); //matlab.Execute("classifier = wekacomptrain('"+ filename + "');"); yield return(new ChoiceView(new string[] { "Start Study Phase" }, out result) { Text = "Click When Ready" }); while (pres.Count > 0) { var stimulus = pres.RemoveRandom(); yield return(new TextView(stimulus.test + "\n" + stimulus.answer, this.settings.PresentationTime, GUIUtils.Constants.DISPLAY_FONT_LARGE)); yield return(new RestView(this.settings.RestTime)); quiz.Add(stimulus); } yield return(new ChoiceView(new string[] { "Start Test Phase" }, out result) { Text = "Click When Ready" }); var connected = true; // assume it's connected using (var invoker = new SingleThreadedInvoker()) using (var connectionListener = new EEGDataListener(invoker, s => connected = true, null, s => connected = false)) { // listen for a broken connection this.dataSource.AddListener(connectionListener); foreach (var view in this.GetViews(invoker, logWriter, dataWriter, study, quiz, done, numgen)) { if (connected) { yield return(view); } else { GUIUtils.Alert("Lost connection to headset!"); break; } } this.dataSource.RemoveListener(connectionListener); } } }
private IEnumerable <View> GetViews(ISynchronizeInvoke invoker, StreamWriter logWriter, StreamWriter dataWriter, RandomizedQueue <StudyTestPair> study, RandomizedQueue <StudyTestPair> quiz, RandomizedQueue <StudyTestPair> done, Random numgen) { var currentTrialEntries = new List <EEGDataEntry>(); using (var artifactListener = new EEGDataListener(invoker, null, data => { foreach (var entry in data) { if (entry.HasStimulusMarker()) { lock (currentTrialEntries) { currentTrialEntries.Add(entry); } } } }, null)) { this.dataSource.AddListener(artifactListener); for (int index = 0; index < settings.NumRounds; index++) { double rand = numgen.NextDouble(); StudyTestPair stim; if (rand < .39) { if (!study.IsEmpty()) { stim = study.RemoveRandom(); quiz.Add(stim); logWriter.WriteLine("5"); logWriter.WriteLine(stim.test + "\\n" + stim.answer); yield return(new RestView(this.settings.BlinkTime)); yield return(new TextView(stim.test + "\n" + stim.answer, this.settings.PresentationTime, GUIUtils.Constants.DISPLAY_FONT_LARGE)); } else if (!quiz.IsEmpty()) { stim = quiz.RemoveRandom(); logWriter.WriteLine("7"); logWriter.WriteLine(stim.test); logWriter.WriteLine(stim.answer); foreach (var view in RunTrial(index, stim, dataWriter, logWriter, currentTrialEntries, study, quiz, done)) { yield return(view); } } else { stim = done.RemoveRandom(); logWriter.WriteLine("7"); logWriter.WriteLine(stim.test); logWriter.WriteLine(stim.answer); foreach (var view in RunTrial(index, stim, dataWriter, logWriter, currentTrialEntries, study, quiz, done)) { yield return(view); } } } else if (rand < .99) { if (!quiz.IsEmpty()) { stim = quiz.RemoveRandom(); logWriter.WriteLine("7"); logWriter.WriteLine(stim.test); logWriter.WriteLine(stim.answer); foreach (var view in RunTrial(index, stim, dataWriter, logWriter, currentTrialEntries, study, quiz, done)) { yield return(view); } } else if (!study.IsEmpty()) { stim = study.RemoveRandom(); quiz.Add(stim); logWriter.WriteLine("5"); logWriter.WriteLine(stim.test + "\\n" + stim.answer); yield return(new RestView(this.settings.BlinkTime)); yield return(new TextView(stim.test + "\n" + stim.answer, this.settings.PresentationTime, GUIUtils.Constants.DISPLAY_FONT_LARGE)); } else { stim = done.RemoveRandom(); logWriter.WriteLine("7"); logWriter.WriteLine(stim.test); logWriter.WriteLine(stim.answer); foreach (var view in RunTrial(index, stim, dataWriter, logWriter, currentTrialEntries, study, quiz, done)) { yield return(view); } } } else { if (!done.IsEmpty()) { stim = done.RemoveRandom(); logWriter.WriteLine("7"); logWriter.WriteLine(stim.test); logWriter.WriteLine(stim.answer); foreach (var view in RunTrial(index, stim, dataWriter, logWriter, currentTrialEntries, study, quiz, done)) { yield return(view); } } else if (!quiz.IsEmpty()) { stim = quiz.RemoveRandom(); logWriter.WriteLine("7"); logWriter.WriteLine(stim.test); logWriter.WriteLine(stim.answer); foreach (var view in RunTrial(index, stim, dataWriter, logWriter, currentTrialEntries, study, quiz, done)) { yield return(view); } } else { stim = study.RemoveRandom(); quiz.Add(stim); logWriter.WriteLine("5"); logWriter.WriteLine(stim.test + "\\n" + stim.answer); yield return(new RestView(this.settings.BlinkTime)); yield return(new TextView(stim.test + "\n" + stim.answer, this.settings.PresentationTime, GUIUtils.Constants.DISPLAY_FONT_LARGE)); } } } } }
private void BuildView() { this.SuspendLayout(); var tabs = new CustomTabControl() { Dock = DockStyle.Fill }; tabs.DisplayStyleProvider = new TabStyleVisualStudioProvider(tabs) { ShowTabCloser = true }; tabs.TabClosing += (sender, args) => ((CustomTab)args.TabPage).RaiseClosingSafe(args); var startTab = new CustomTab() { Text = "Classifiers " }; // the ending space is necessary for some reason startTab.Closing += (sender, args) => { args.Cancel = true; if (GUIUtils.IsUserSure("Reset classifiers?")) { this.classifierTabs.Clear(); this.Controls.Remove(tabs); tabs.Dispose(); this.BuildView(); this.OnSizeChanged(EventArgs.Empty); } }; // classifier list var classifierList = new CheckedListBox() { Dock = DockStyle.Fill, CheckOnClick = true }; classifierList.AddContextMenu(); Action <ClassificationScheme> addClassifier = (scheme) => { // get unique name if necessary string baseName = string.IsNullOrWhiteSpace(scheme.Settings.Name) ? "new classifier" : scheme.Settings.Name; if (!this.classifierTabs.Select(ct => ct.Text).Contains(baseName)) { scheme.Settings.Name = baseName; } else { int i = 1; while (this.classifierTabs .Select(ct => ct.Text.ToLower()) .Contains(string.Format("{0} {1}", baseName, i))) { i++; } scheme.Settings.Name = string.Format("{0} {1}", baseName, i); } // create the tab var classifierTab = new ClassificationSchemeTab(scheme); classifierTab.TextChanged += (sender, args) => classifierList.Invalidate(); classifierTab.Closing += (sender, args) => { this.classifierTabs.Remove(classifierTab); classifierList.Items.Remove(classifierTab); }; this.classifierTabs.Add(classifierTab); tabs.TabPages.Add(classifierTab); classifierList.Items.Add(classifierTab, true); }; this.getSelectedClassifiers = () => classifierList.CheckedItems.Cast <ClassificationSchemeTab>().Select(cst => cst.ClassificationScheme).ToIArray(); // buttons var buttonTable = GUIUtils.CreateButtonTable(Direction.Horizontal, DockStyle.Bottom, GUIUtils.CreateFlatButton("New", (b) => { var classifier = classifierList.Items.Count > 0 ? ((ClassificationSchemeTab)(classifierList.SelectedItem ?? classifierList.Items[0])).ClassificationScheme : new ClassificationScheme(); classifier.Settings.Name = string.Empty; addClassifier(classifier); }, startTab.ToolTip, "Create a new classifier"), GUIUtils.CreateFlatButton("Load", (b) => { if (this.openDialog.ShowDialog() != DialogResult.OK) { return; } ClassificationScheme scheme; foreach (var path in this.openDialog.FileNames) { if (Utils.TryDeserializeFile(this.openDialog.FileName, out scheme)) { addClassifier(scheme); } else { GUIUtils.Alert("Failed to load classifier info from " + path, MessageBoxIcon.Error); } } }, startTab.ToolTip, "Load a previously saved classifier settings file")); // artifact detection config var artifactDetectionPanel = new ConfigurationPanel(this.artifactDetection); artifactDetectionPanel.PropertyChanged += args => this.artifactDetection.SetProperty(args.Property, args.Getter()); // artifact detection label var artifactDetectionLabel = new Label() { Dock = DockStyle.Bottom, TextAlign = ContentAlignment.MiddleCenter, Visible = false }; IEnumerable <EEGDataEntry> empty = new EEGDataEntry[0], entries = empty; var listener = new EEGDataListener(GUIUtils.GUIInvoker, source => artifactDetectionLabel.Visible = true, data => { if (!this.artifactDetection.UseArtifactDetection) { artifactDetectionLabel.Visible = false; entries = empty; return; } artifactDetectionLabel.Visible = true; entries = entries.Concat(data); if (data.LastItem().TimeStamp - entries.First().TimeStamp >= 500) { if (this.artifactDetection.HasMotionArtifact(entries)) { artifactDetectionLabel.Text = "Motion artifact detected!"; artifactDetectionLabel.BackColor = Color.Red; artifactDetectionLabel.ForeColor = Color.White; if (this.artifactDetection.Beep) { GUIUtils.GUIInvoker.BeginInvoke(SystemSounds.Beep.Play); } } else { artifactDetectionLabel.Text = "No artifacts detected"; artifactDetectionLabel.BackColor = Color.Green; artifactDetectionLabel.ForeColor = Color.Black; } entries = empty; } }, source => artifactDetectionLabel.Visible = false); // avoid using the gui invoker before the handle has been created this.HandleCreated += (sender, args) => EmotivDataSource.Instance.AddListener(listener); artifactDetectionLabel.Disposed += (sender, args) => { EmotivDataSource.Instance.RemoveListener(listener); listener.Dispose(); }; // right half var rightPanel = new Panel() { Dock = DockStyle.Fill }; rightPanel.Controls.Add(classifierList); rightPanel.Controls.Add(buttonTable); // left half var leftPanel = new Panel() { Dock = DockStyle.Fill }; leftPanel.Controls.Add(artifactDetectionPanel); leftPanel.Controls.Add(artifactDetectionLabel); var cols = GUIUtils.CreateTable(new double[] { .5, .5 }, Direction.Horizontal); cols.Controls.Add(rightPanel, 0, 0); cols.Controls.Add(leftPanel, 1, 0); startTab.Controls.Add(cols); tabs.TabPages.Add(startTab); this.Controls.Add(tabs); this.ResumeLayout(false); }