//Copy constructor public BDFPoint(BDFPoint pt) { this._rec = pt._rec; this._pt = pt._pt; this._recSize = pt._recSize; this._sec = pt._sec; this._st = pt._st; }
public double Read(int channel, BDFPoint p) { int value; if (isBDF) { BDFEDFRecord.i24 val3; accessor.Read<BDFEDFRecord.i24>(3L * (recordSetLength * p.Rec + recordLength * channel + p.Pt), out val3); value = BDFEDFRecord.convert34(val3.b1, val3.b2, val3.b3); } else {//is EDF file BDFEDFRecord.i16 val2; accessor.Read<BDFEDFRecord.i16>(2L * (recordSetLength * p.Rec + recordLength * channel + p.Pt), out val2); value = BDFEDFRecord.convert24(val2.b1, val2.b2); } return _BDFEDFHeader.Gain(channel) * (double)value + _BDFEDFHeader.Offset(channel); }
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"); }
private bool createFILMANRecord(BDFPoint startingPt, BDFPoint endPt) { if (startingPt.Rec < 0) return false; //start of record outside of file coverage; so skip it if (endPt.Rec >= bdf.NumberOfRecords) return false; //end of record outside of file coverage /***** Read correct portion of BDF file and decimate *****/ int pt = 0; int j = 0; //set to avoid compiler complaining about uninitialized variable! int k = 0; int p = 0; for (int rec = startingPt.Rec; rec <= endPt.Rec; rec++) { if (bdf.read(rec) == null) throw new Exception("Unable to read BDF record #" + rec.ToString("0")); if (rec == startingPt.Rec) j = startingPt.Pt; else j = p - bdf.NSamp; // calculate point offset at beginning of new record if (rec == endPt.Rec) k = endPt.Pt; else k = bdf.NSamp; for (p = j; p < k; p += decimation, pt++) for (int c = 0; c < bdf.NumberOfChannels - 1; c++) bigBuff[c, pt] = bdf.getSample(c, p); } //NOTE: after this point bigBuff containes all channels in BDF file, // includes all BDF records that contribute to this output record, // but has been decimated to include only those points that will actually be written out!!! // This is necessary because referencing channels may not be actually included in the recordSet. /***** Update bigBuff to referenced data *****/ calculateReferencedData(); /***** Write out channel after loading appropriate data *****/ for (int iChan = 0; iChan < FMStream.NC; iChan++) { int channel = channels[iChan]; // translate channel numbers double ave = 0.0; double beta = 0.0; double fn = (double)FMStream.ND; if (radinOffset) //calculate Radin offset for this channel, based on a segment of the data specified by radinLow and radinHigh { for (int i = radinLow; i < radinHigh; i++) ave += bigBuff[channel, i]; ave = ave / (double)(radinHigh - radinLow); } if (removeOffsets || removeTrends) //calculate average for this channel; this will always be true if removeTrends true { for (int i = 0; i < FMStream.ND; i++) ave += bigBuff[channel, i]; ave = ave / fn; } double t = 0D; if (removeTrends) //calculate linear trend for this channel; see Bloomfield p. 115 //NOTE: this technique works only for "centered" data: if there are N points, covering NT seconds, it is assumed that // these points are located at (2i-N-1)T/2 seconds, for i = 1 to N; in other words, the samples are in the center of // each sample time and are symetrically distributed about a central zero time in the record. Then one can separately // calculate the mean and the slope and apply them together to remove a linear trend. This doesn't work for quadratic // or higher order trend removal however. { t = (fn - 1.0D) / 2.0D; fn *= fn * fn - 1D; for (int i = 0; i < FMStream.ND; i++) beta += bigBuff[channel, i] * ((double)i - t); beta = 12.0D * beta / fn; } for (int i = 0; i < FMStream.ND; i++) FMStream.record[i] = bigBuff[channel, i] - (ave + beta * ((double)i - t)); FMStream.write(); //Channel number group variable taken care of here } return true; }
//adds pts points to current location stp public static BDFPoint operator +(BDFPoint pt, int pts) { BDFPoint stp = new BDFPoint(pt); stp.Pt += pts; //set property to get record correction return stp; }
public bool lessThanOrEqual(BDFPoint pt) { return this._rec < pt._rec || this._rec == pt._rec && this._pt <= pt._pt; }
public bool greaterThanOrEqual(BDFPoint pt) { return this._rec > pt._rec || this._rec == pt._rec && this._pt >= pt._pt; }
public bool equal(BDFPoint pt) { return this._rec == pt._rec && this._pt == pt._pt; }
public long distanceInPts(BDFPoint p) { if (_recSize != p._recSize) throw new Exception("BDFPoint.distanceInPts: record sizes not equal"); long d = (_rec - p._rec) * _recSize; d += _pt - p._pt; return d < 0 ? -d : d; }
public void Read(int channel, BDFPoint start, int length, ref double[] outArray) { BDFPoint p = new BDFPoint(start); for (int i = 0; i < length; i++) outArray[i] = Read(channel, p++); }
/// <summary> /// Determine if two sements between start1 and end1 and /// start2 and end2 overlap; we assume that start is less than end for both /// </summary> /// <param name="start1"></param> /// <param name="end1"></param> /// <param name="start2"></param> /// <param name="end2"></param> /// <returns>true if overlap present, otherwise false</returns> static bool Overlap(BDFPoint start1, BDFPoint end1, BDFPoint start2, BDFPoint end2) { return end2.greaterThan(start1) && end1.greaterThan(start2); }
public bool IsExcluded(BDFPoint start, BDFPoint end) { for (int i = 0; i < From.Count; i++) if (Overlap(From[i], To[i], start, end)) return true; return false; }