public void Execute(object sender, DoWorkEventArgs e) { bw = (BackgroundWorker)sender; bw.ReportProgress(0, "Starting FMConverter"); CCIUtilities.Log.writeToLog("Starting FMConverter on records in " + directory); /***** Read electrode file *****/ ElectrodeInputFileStream etrFile = new ElectrodeInputFileStream( new FileStream(Path.Combine(directory, eventHeader.ElectrodeFile), FileMode.Open, FileAccess.Read)); /***** Open FILMAN file *****/ SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = "Save as FILMAN file ..."; dlg.AddExtension = true; dlg.DefaultExt = ".fmn"; // Default file extension dlg.Filter = "FILMAN Files (.fmn)|*.fmn"; // Filter files by extension dlg.FileName=Path.GetFileNameWithoutExtension(eventHeader.BDFFile); Nullable<bool> result = dlg.ShowDialog(); if (result == null || !(bool)result) { e.Result = new int[] { 0, 0 }; return; } samplingRate = BDF.NSamp / BDF.RecordDuration; offsetInPts = Convert.ToInt32(offset * samplingRate); newRecordLength = Convert.ToInt32(Math.Ceiling(length * samplingRate / (float)decimation)); FMStream = new FILMANOutputStream( File.Open(dlg.FileName, FileMode.Create, FileAccess.ReadWrite), GV.Count + 2, EDE.ancillarySize, channels.Count, newRecordLength, FILMANFileStream.FILMANFileStream.Format.Real); log = new LogFile(dlg.FileName + ".log.xml"); FMStream.IS = Convert.ToInt32( (double)samplingRate/(double)decimation); bigBuff = new float[BDF.NumberOfChannels - 1, FMStream.ND]; //have to dimension to BDF rather than FMStream //in case we need for reference calculations /***** Create FILMAN header records *****/ FMStream.GVNames(0, "Channel"); FMStream.GVNames(1, "Montage"); int i = 2; foreach (GVEntry gv in GV) FMStream.GVNames(i++, gv.Name); //generate group variable names for (i = 0; i < FMStream.NC; i++) //generate channel labels { string s = BDF.channelLabel(channels[i]); ElectrodeFileStream.ElectrodeRecord p; if (etrFile.etrPositions.TryGetValue(s, out p)) //add electrode location information, if available FMStream.ChannelNames(i, s.PadRight(16, ' ') + p.projectPhiTheta().ToString("0")); else FMStream.ChannelNames(i, s); } FMStream.Description(0, eventHeader.Title + " Date: " + eventHeader.Date + " " + eventHeader.Time); FMStream.Description(1, "File: " + Path.Combine(directory, Path.GetFileNameWithoutExtension(eventHeader.BDFFile))); StringBuilder sb = new StringBuilder("Subject: " + eventHeader.Subject.ToString()); if (eventHeader.Agent != 0) sb.Append(" Agent: " + eventHeader.Agent); sb.Append(" Tech:"); foreach (string s in eventHeader.Technician) sb.Append(" " + s); FMStream.Description(2, sb.ToString()); sb = new StringBuilder("Event="+EDE.Name); sb.Append(" Offset=" + offset.ToString("0.00")); sb.Append(" Length=" + length.ToString("0.00")); if (referenceGroups == null || referenceGroups.Count == 0) sb.Append(" No reference"); else if (referenceGroups.Count == 1) { sb.Append(" Single ref group with"); if (referenceGroups[0].Count >= FMStream.NC) if (referenceChannels[0].Count == BDF.NumberOfChannels) sb.Append(" common average ref"); else if (referenceChannels[0].Count == 1) sb.Append(" ref channel " + referenceChannels[0][0].ToString("0") + "=" + BDF.channelLabel(referenceChannels[0][0])); else sb.Append(" multiple ref channels=" + referenceChannels[0].Count.ToString("0")); } else // complex reference expression { sb.Append(" Multiple reference groups=" + referenceGroups.Count.ToString("0")); } FMStream.Description(3, sb.ToString()); sb = new StringBuilder("#Group vars=" + GV.Count.ToString("0")); if (anc) sb.Append(" Ancillary=" + FMStream.NA.ToString("0")); sb.Append(" #Channels=" + FMStream.NC.ToString("0")); sb.Append(" #Samples=" + FMStream.ND.ToString("0")); sb.Append(" Samp rate=" + FMStream.IS.ToString("0")); FMStream.Description(4, sb.ToString()); FMStream.Description(5, BDF.LocalRecordingId); FMStream.writeHeader(); log.registerHeader(this); /***** Open Event file for reading *****/ EventFactory.Instance(eventHeader.Events); // set up the factory EventFileReader EventFR = new EventFileReader( new FileStream(Path.Combine(directory, eventHeader.EventFile), FileMode.Open, FileAccess.Read)); BDFLoc stp = BDF.LocationFactory.New(); if (!EDE.intrinsic) if (risingEdge) threshold = EDE.channelMin + (EDE.channelMax - EDE.channelMin) * threshold; else threshold = EDE.channelMax - (EDE.channelMax - EDE.channelMin) * threshold; nominalT = BDF.LocationFactory.New(); //nominal Event time based on Event.Time actualT = BDF.LocationFactory.New(); //actual Event time in Status channel //Note: these should be the same if the two clocks run the same rate (BioSemi DAQ and computer) /***** MAIN LOOP *****/ foreach (InputEvent ie in EventFR) //Loop through Event file { bw.ReportProgress(0, "Processing event " + ie.Index.ToString("0")); //Report progress if (ie.Name == EDE.Name) // Event match found in Event file { if (findEvent(ref stp, ie)) createFILMANRecord(stp, ie); //Create FILMAN recordset around this found point } } e.Result = new int[] { FMStream.NR, FMStream.NR / FMStream.NC }; FMStream.Close(); EventFR.Close(); log.Close(); }
public void Execute(object sender, DoWorkEventArgs e) { bw = (BackgroundWorker)sender; bw.ReportProgress(0, "Starting ASC conversion"); CCIUtilities.Log.writeToLog("Started ASC conversion on records in " + headerFileName); /***** Read electrode file *****/ ElectrodeInputFileStream etrFile = new ElectrodeInputFileStream( new FileStream(System.IO.Path.Combine(directory, head.ElectrodeFile), FileMode.Open, FileAccess.Read)); /***** Open new FILMAN file *****/ SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = "Save as FILMAN file ..."; dlg.AddExtension = true; dlg.DefaultExt = ".fmn"; // Default file extension dlg.Filter = "FILMAN Files (.fmn)|*.fmn"; // Filter files by extension dlg.FileName = headerFileName; dlg.InitialDirectory = ASCtoFMConverter.Properties.Settings.Default.LastDataset; //Use dataset default, but don't save if new location bool? result = dlg.ShowDialog(); if (result == null || !(bool)result) { bw.ReportProgress(0, "Conversion cancelled before FM file created."); e.Cancel = true; return; } int newRecordLength = Convert.ToInt32(Math.Ceiling(FMRecLength * samplingRate / (double)decimation)); int FMRecordLengthInBDF = Convert.ToInt32(FMRecLength * samplingRate); int GVCount = 6 + GVCopyAcross.Count; bool PKCounterExists = false; foreach (EpisodeDescription ed in specs) { PKDetectorEventCounterDescription pkd = ed.PKCounter; if (pkd != null) { PKCounterExists = true; pkd.assignedGVNumber = GVCount; } } GVCount += PKCounterExists ? 3 : 0; //NOTE: because we have to have the number of GVs (and Channels) available when we create the output stream, // we have to separate the enumeration of GVs from the naming of GVs; this could be avoided by using lists // rather than arrays for GVNames in FILMANOuputStream and having an actual Header entity for FILMAN files; // one would then enter the GVNames (and Channel names) into the list before creating the ouput stream and have // the constructor use the counts to get NG and NC; NA could also be an array that must be created before as a // byte array, but this might be awkward FMStream = new FILMANOutputStream( File.Open(dlg.FileName, FileMode.Create, FileAccess.ReadWrite), GVCount, 0, channels.Count, newRecordLength, FILMANFileStream.FILMANFileStream.Format.Real); log = new LogFile(dlg.FileName + ".log.xml"); FMStream.IS = Convert.ToInt32((double)samplingRate / (double)decimation); bigBuff = new double[bdf.NumberOfChannels - 1, FMStream.ND]; //have to dimension to BDF rather than FMStream //in case we need for reference calculations /***** Create FILMAN header records *****/ //First GV names: //six for the standard generated FMStream.GVNames(0, "Channel"); FMStream.GVNames(1, "Montage"); FMStream.GVNames(2, "NewGroupVariable"); FMStream.GVNames(3, "EpisodeNumber"); FMStream.GVNames(4, "EpisodeRecordNumber"); FMStream.GVNames(5, "SecondsFromStart"); //then the copied-across GVs for (int n = 0; n < GVCopyAcross.Count; n++) FMStream.GVNames(n + 6, GVCopyAcross[n].Name); //and last, the GVs from the counters if (PKCounterExists) //if there are any PK counters, we'll need their GVs { FMStream.GVNames(GVCount - 3, "PK-rate"); FMStream.GVNames(GVCount - 2, "PK-velocity"); FMStream.GVNames(GVCount - 1, "PK-accel"); } for (int j = 0; j < FMStream.NC; j++) //generate channel labels { string s = bdf.channelLabel(channels[j]); ElectrodeFileStream.ElectrodeRecord p; if (etrFile.etrPositions.TryGetValue(s, out p)) FMStream.ChannelNames(j, s.PadRight(16, ' ') + p.projectPhiTheta().ToString("0")); //add electrode location information, if available else FMStream.ChannelNames(j, s); } FMStream.Description(0, head.Title + " Date: " + head.Date + " " + head.Time + " File: " + System.IO.Path.Combine(directory, System.IO.Path.GetFileNameWithoutExtension(head.BDFFile))); StringBuilder sb = new StringBuilder("Subject: " + head.Subject.ToString()); if (head.Agent != 0) sb.Append(" Agent: " + head.Agent); sb.Append(" Tech:"); foreach (string s in head.Technician) sb.Append(" " + s); FMStream.Description(1, sb.ToString()); sb.Clear(); sb = sb.Append(specs[0].ToString()); for (int j = 1; j < specs.Length; j++) sb.Append("/ " + specs[j].ToString()); string str = sb.ToString(); int sl = str.Length; int k; if (sl < 72) { FMStream.Description(2, str); k = 3; } else { FMStream.Description(2, str.Substring(0, 72)); if (sl < 144) { FMStream.Description(3, str.Substring(72)); k = 4; } else { FMStream.Description(3, str.Substring(72, 72)); k = 5; if (sl < 216) FMStream.Description(4, str.Substring(144)); else FMStream.Description(4, str.Substring(144, 72)); } } sb.Clear(); if (referenceGroups == null || referenceGroups.Count == 0) sb.Append("No reference"); else if (referenceGroups.Count == 1) { sb.Append("Single ref group with"); if (referenceGroups[0].Count >= FMStream.NC) if (referenceChannels[0].Count == bdf.NumberOfChannels) sb.Append(" common average ref"); else if (referenceChannels[0].Count == 1) sb.Append(" ref channel " + referenceChannels[0][0].ToString("0") + "=" + bdf.channelLabel(referenceChannels[0][0])); else sb.Append(" multiple ref channels=" + referenceChannels[0].Count.ToString("0")); } else // complex reference expression { sb.Append(" Multiple reference groups=" + referenceGroups.Count.ToString("0")); } FMStream.Description(k++, sb.ToString()); if (k < 6) FMStream.Description(k, bdf.LocalRecordingId); FMStream.writeHeader(); log.registerHeader(this); EventFactory.Instance(ED); int epiNo = 0; //found episode counter //read in list of Events bw.ReportProgress(0, "Reading Events, synchronizing clocks, and calculating Event offsets from BDF file"); List<InputEvent> EventList = new List<InputEvent>(); EventFileReader efr=new EventFileReader( new FileStream(System.IO.Path.Combine(directory, head.EventFile), FileMode.Open, FileAccess.Read)); InputEvent.LinkEventsToDataset(head, bdf); //link InputEvents to this specific dataset foreach (InputEvent ie in efr) { EventList.Add(ie); } IEnumerator<InputEvent> EventEnumerator = EventList.GetEnumerator(); //Enumerator for stepping through Event file //******** Synchronize clocks //Need to synchronize clocks by setting the BDF.zeroTime value //zeroTime is the time, according to the Event file clock, of the beginning of the BDF file (BioSemi clock) if (ignoreStatus && offsetToFirstEvent < 0D) //cannot use Status markers to synchronize clocks, so //use raw Event clock times as actual offsets from beginning of BDF file; in other words force all Events to be BDF-based bdf.setZeroTime(0D); //this keeps it from throwing an error when "synchronizing" else { //Need to find a covered (intrisic or extrinsic) Event to use as an indicial Event bool found = false; EventEnumerator.Reset(); if (syncToFirst || ignoreStatus) while (!found && EventEnumerator.MoveNext()) { if (EventEnumerator.Current.EDE.IsCovered) //have we found a covered Event? { if (ignoreStatus) { bdf.setZeroTime(EventEnumerator.Current.Time - offsetToFirstEvent); found = true; } else found = bdf.setZeroTime(EventEnumerator.Current); } } else //sync to "middle" Event { int midRecord = bdf.NumberOfRecords / 2; BDFLocFactory fac = new BDFLocFactory(bdf); BDFLoc loc = fac.New(); loc.Rec = midRecord; bdf.read(midRecord); uint v1 = head.Mask & (uint)bdf.getStatusSample(loc); uint v2; InputEvent IE; while ((v2 = (uint)bdf.getStatusSample(++loc) & head.Mask) == v1) ; //find next Status mark after mid point of file; v2 = GC while (!found && EventEnumerator.MoveNext()) { IE = EventEnumerator.Current; if (IE.GC == v2) { bdf.setZeroTime(IE.Time - loc.ToSecs()); found = true; } } } if (!found) { log.Close(); FMStream.Close(); throw (new Exception("No valid synchronizing (covered) Event found; use manual synchronization")); } } Log.writeToLog("\tinto FM file " + dlg.FileName); foreach(InputEvent ie in EventList) //modify relativeTime field depending on type of Event ie.setRelativeTime(); EventList = EventList.OrderBy(ev => ev.relativeTime).ToList(); //re-sort: minor order changes may occur //Loop through each episode specification, // then through the Event file to find any regions satisfying the specification, // then create FILMAN records for each of these regions //******** Episode specification loop for (int i = 0; i < specs.Length; i++) { EpisodeDescription currentEpisode = specs[i]; FMStream.record.GV[2] = currentEpisode.GVValue; //set epispec GV value if (currentEpisode.Exclude != null) { //Here we complete the ExclusionDescription for the given Episode specification //by finding all the segemnts that must be excluded and //calculating their From to To BDFPoints; this is done for each specification to //permit different exclusion criteria for each EpisodeDescription ExclusionDescription ed = currentEpisode.Exclude; EventEnumerator.Reset(); EventDictionaryEntry startEDE = ed.startEvent; bool t = ed.endEvent != null && ed.endEvent.GetType() == typeof(EventDictionaryEntry); EventDictionaryEntry endEDE = null; if(t) endEDE = (EventDictionaryEntry)ed.endEvent; while(EventEnumerator.MoveNext()) { InputEvent ev = EventEnumerator.Current; if (ev.Name == startEDE.Name) { BDFPoint b = new BDFPoint(bdf).FromSecs(bdf.timeFromBeginningOfFileTo(ev)); ed.From.Add(b); if (t) while (EventEnumerator.MoveNext()) { ev = EventEnumerator.Current; if (ev.Name == endEDE.Name) { ed.To.Add(new BDFPoint(bdf).FromSecs(bdf.timeFromBeginningOfFileTo(ev))); break; } } else ed.To.Add(b); } } } // From here we loop through Event file until an Event is found that matches the // current startEvent in spec[i]; from that point a matching endEvent is sought; // episode is then processed; note that this implies that overlapping episodes are not // generally permitted (in a given specification) except when caused by offsets. //************* Event file loop EventEnumerator.Reset(); bool found; do { if (bw.CancellationPending) //look for cancellation first { bw.ReportProgress(0, "Conversion canceled with " + FMStream.NR.ToString("0") + " records in " + (FMStream.NR / FMStream.NC).ToString("0") + " recordsets generated."); FMStream.Close(); log.Close(); e.Cancel = true; return; } InputEvent startEvent = null; InputEvent endEvent = null; double startTime; double endTime = 0; found = findNextMark(currentEpisode.Start, EventEnumerator, true, out startTime, out startEvent) && findNextMark(currentEpisode.End, EventEnumerator, false, out endTime, out endEvent); if (found || startTime >= 0D && specs[i].useEOF) { //use EOF //***************** FILMAN record loop startTime += currentEpisode.Start._offset; endTime += currentEpisode.End._offset; bw.ReportProgress(0, "Found episode " + (++epiNo).ToString("0") + " from " + startTime.ToString("0.000") + " to " + endTime.ToString("0.000")); int maxNumberOfFMRecs = (int)Math.Floor((endTime - startTime) / FMRecLength); log.openFoundEpisode(epiNo, startTime, endTime, maxNumberOfFMRecs); BDFPoint startBDFPoint = new BDFPoint(bdf); startBDFPoint.FromSecs(startTime); BDFPoint endBDFPoint = new BDFPoint(startBDFPoint); /***** Get group variables for this record *****/ FMStream.record.GV[3] = epiNo; if (startEvent != null) //exclude BOF { int GrVar = 6; //Load up group variables, based on the start Event foreach (GVEntry gve in GVCopyAcross) { int j = startEvent.GetIntValueForGVName(gve.Name); FMStream.record.GV[GrVar++] = j < 0 ? 0 : j; //use zero to indicate "No value" } } /***** Process each FILMAN record *****/ int actualNumberOfFMRecs = 0; for (int rec = 1; rec <= maxNumberOfFMRecs; rec++) { endBDFPoint += FMRecordLengthInBDF; //update end point if (currentEpisode.Exclude == null || !currentEpisode.Exclude.IsExcluded(startBDFPoint, endBDFPoint)) //is not excluded: { FMStream.record.GV[4] = ++actualNumberOfFMRecs; //Record number in this episode FMStream.record.GV[5] = Convert.ToInt32(Math.Ceiling(startBDFPoint.ToSecs())); //Approximate seconds since start of BDF file //calculate start and end times for this record: use BDFPoint values to assure accuracy; //avoids problem if FMRecordLength is not exactly represented in double startTime = startBDFPoint.ToSecs(); endTime = endBDFPoint.ToSecs(); //*****Count PK Events in this record***** if (PKCounterExists) { for (int j = GVCount - 3; j < GVCount; j++) FMStream.record.GV[j] = 0; //zero out as default if(currentEpisode.PKCounter!=null) { PKDetectorEventCounterDescription pkd = currentEpisode.PKCounter; double[] v = pkd.countMatchingEvents(startTime, startTime + FMRecLength, EventList); FMStream.record.GV[GVCount - 3] = Convert.ToInt32(1000D * v[0]); //make per thousand to nave useful integer FMStream.record.GV[GVCount - 2] = Convert.ToInt32(v[1]); FMStream.record.GV[GVCount - 1] = Convert.ToInt32(v[2]); } } createFILMANRecord(startBDFPoint, endBDFPoint); } startBDFPoint = endBDFPoint; //move start point forward } log.closeFoundEpisode(actualNumberOfFMRecs); } } while (found && !(currentEpisode.Start.MatchesType("Beginning of file") == true)); //next Event, if any } //next spec e.Result = new int[] { FMStream.NR, FMStream.NR / FMStream.NC }; FMStream.Close(); log.Close(); Log.writeToLog("Completed ASC conversion with " + FMStream.NR.ToString("0") + " FM records created"); }
public void Execute(object sender, DoWorkEventArgs e) { bw = (BackgroundWorker)sender; bw.ReportProgress(0, "Starting FMConverter"); CCIUtilities.Log.writeToLog("Starting FMConverter on records in " + Path.Combine(directory, FileName)); /***** Open FILMAN file *****/ SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = "Save as FILMAN file ..."; dlg.AddExtension = true; dlg.DefaultExt = ".fmn"; // Default file extension dlg.Filter = "FILMAN Files (.fmn)|*.fmn"; // Filter files by extension dlg.FileName = FileName; bool? result = dlg.ShowDialog(); if (result == null || !(bool)result) { e.Result = new int[] { 0, 0 }; return; } newRecordLengthPts = oldRecordLengthPts / decimation; FMStream = new FILMANOutputStream( File.Open(dlg.FileName, FileMode.Create, FileAccess.ReadWrite), 3, 0, channels.Count, newRecordLengthPts, FILMANFileStream.FILMANFileStream.Format.Real); log = new LogFile(dlg.FileName + ".log.xml", GVMapElements); FMStream.IS = Convert.ToInt32((double)newRecordLengthPts / newRecordLengthSec); //rounding method bigBuff = new float[edfPlus.NumberOfChannels - 1, FMStream.ND]; //have to dimension to BDF rather than FMStream //in case we need for reference calculations /***** Create FILMAN header records *****/ FMStream.GVNames(0, "Channel"); FMStream.GVNames(1, "Montage"); FMStream.GVNames(2, GVName); for (int i = 0; i < FMStream.NC; i++) //copy across channel labels { string s = edfPlus.channelLabel(channels[i]); FMStream.ChannelNames(i, s); } FMStream.Description(0, " Date: " + edfPlus.timeOfRecording().ToShortDateString() + " Time: " + edfPlus.timeOfRecording().ToShortTimeString()); FMStream.Description(1, "Based on file: " + Path.Combine(directory, FileName)); FMStream.Description(2, edfPlus.LocalSubjectId); StringBuilder sb = new StringBuilder(" Offset=" + offset.ToString("0.00")); sb.Append(" Length=" + newRecordLengthSec.ToString("0.000")); if (referenceGroups == null || referenceGroups.Count == 0) sb.Append(" No reference"); else if (referenceGroups.Count == 1) { sb.Append(" Single ref group with"); if (referenceGroups[0].Count >= FMStream.NC) if (referenceChannels[0].Count == edfPlus.NumberOfChannels) sb.Append(" common average ref"); else if (referenceChannels[0].Count == 1) sb.Append(" ref channel " + referenceChannels[0][0].ToString("0") + "=" + edfPlus.channelLabel(referenceChannels[0][0])); else sb.Append(" multiple ref channels=" + referenceChannels[0].Count.ToString("0")); } else // complex reference expression { sb.Append(" Multiple reference groups=" + referenceGroups.Count.ToString("0")); } FMStream.Description(3, sb.ToString()); sb = new StringBuilder("#Group var=" + GVName); sb.Append(" #Channels=" + FMStream.NC.ToString("0")); sb.Append(" #Samples=" + FMStream.ND.ToString("0")); sb.Append(" Samp rate=" + FMStream.IS.ToString("0")); FMStream.Description(4, sb.ToString()); FMStream.Description(5, edfPlus.LocalRecordingId); FMStream.writeHeader(); log.registerHeader(this); BDFLoc stp = edfPlus.LocationFactory.New(); BDFLoc end = edfPlus.LocationFactory.New(); /***** MAIN LOOP *****/ for (int ev = 0; ev < Events.Count; ev++) //Loop through Events list { EventMark currentEvent = Events[ev]; //check to make sure we haven't deleted this event type from GV map GVMapElement gv = currentEvent.GV; if (GVMapElements.Contains(gv)) { stp.FromSecs(currentEvent.Time + offset); currentGVValue = gv.Value; //map to integer if (ev < (Events.Count - 1)) end.FromSecs(Events[ev + 1].Time + offset); else end.EOF(); bw.ReportProgress(0, "Processing event at " + currentEvent.Time.ToString("0.000")); //Report progress int n = 0; while (createFILMANRecord(ref stp, end)) n++; /*Create FILMAN recordset around this found point*/ gv.RecordCount += n; log.registerEvent(currentEvent, offset, n); } } int recs = FMStream.NR / FMStream.NC; e.Result = new int[] { FMStream.NR, recs }; FMStream.Close(); CCIUtilities.Log.writeToLog("Ending FMConverter, producing " + recs.ToString("0") + " records in file " + dlg.FileName); log.registerSummary(GVMapElements, recs); log.Close(); }