private void ProcessChannel_Worker(object sender, DoWorkEventArgs e) { List<eventTime> eventList = new List<eventTime>(); //holds list of potential new Events for this channel workerArguments args = (workerArguments)e.Argument; double[] d = args.data; //channel data to be analyzed int N = d.Length; int degree = args.trendDegree; if (degree >= 0) //then perform polynomial detrending { bw.ReportProgress(0, "detrending with " + degree.ToString("0") + " degree polynomial"); removeTrend(d, degree); } double maxD = d[0]; double minD = maxD; foreach (double v in d) if (v > maxD) maxD = v; else if (v < minD) minD = v; int filterN = args.filterLength; double[] V = new double[filterN]; double c1 = 12D / (double)(filterN * (filterN - 1) * (filterN + 1)); double offset = ((double)filterN - 1D) / 2D; for (int i = 0; i < filterN; i++) V[i] = c1 * ((double)i - offset); List<double> filtered = new List<double>(64); bool inEvent = false; int eventLength = 0; double sign = 1D; double threshold = args.threshold; int minimumLength = args.minLength; bw.ReportProgress(0, "detecting with " + filterN.ToString("0") + "pt filter; th = " + threshold.ToString("0.00") + "; minLen = " + minimumLength.ToString("0")); int eventCount = 0; for (int i = 0; i < N; i++) { if (bw.CancellationPending) { e.Cancel = true; return; } double s = 0; for (int j = 0; j < filterN; j++) { int index = i + j - filterN / 2; if (index < 0) //handle start-up s += V[j] * d[0]; //repeat first value to its left else if (index >= N) //handle end s += V[j] * d[N - 1]; //repeat last value to its right else //usual case s += V[j] * d[index]; } if (Math.Abs(s) > threshold) //above threshold? { if (!inEvent) //found beginning of new event { sign = s > 0D ? 1D : -1D; eventLength = 0; inEvent = true; } filtered.Add(s - sign * threshold); eventLength++; } else //below threshold if (inEvent) //are we just exiting an event? { if (eventLength > minimumLength) //event counts only if longer than minimum length { eventTime ev = new eventTime(); //create eventTime for each detected signal ev.channelItem = args.channelItem; ev.serialNumber = ++eventCount; ev.channelNumber = args.channelNumber; ev.startTime = i - eventLength; //starting index in data ev.length = eventLength; ev.sign = sign; ev.filterSignal = filtered; ev.trendDegree = args.trendDegree; ev.filterLength = args.filterLength; ev.threshold = args.threshold; ev.minimumLength = args.minLength; filtered = new List<double>(64); //need new filtered array for signal eventList.Add(ev); lf.registerPKEvent(); } else filtered.Clear(); inEvent = false; } } //now we do a fit on each of the detected signals double samplingRate = args.samplingRate; double t0 = (double)filterN / (2D * samplingRate); //fixed initial estimate of t0 int nEvents = eventList.Count; if (nEvents != 0) if (nEvents == 1) eventList[0].foundFit = fitSignal(d, 0, eventList[0], d.Length, samplingRate, minD, maxD); else { eventList[0].foundFit = fitSignal(d, 0, eventList[0], eventList[1].startTime, samplingRate, minD, maxD); for (int i = 1; i < eventList.Count - 1; i++) { if (bw.CancellationPending) { e.Cancel = true; return; } //look for cancellation bw.ReportProgress((int)((100D * i) / eventList.Count), ""); eventList[i].foundFit = fitSignal(d, eventList[i - 1].endTime, eventList[i], eventList[i + 1].startTime, samplingRate, minD, maxD); } eventList[eventList.Count - 1].foundFit = fitSignal(d, eventList[eventList.Count - 2].endTime, eventList[eventList.Count - 1], d.Length, samplingRate, minD, maxD); } e.Result = eventList; }
static void Main(string[] args) { List<eventTime> eventList = new List<eventTime>(); Console.Write("Degree of fit desired: "); int degree = Convert.ToInt32(Console.ReadLine()); BDFEDFFileReader bdf = new BDFEDFFileReader(new FileStream(@"C:\\Users\Jim\Desktop\PK detector data\S9998-AP-20150210-1205_PKcubes.bdf", FileMode.Open, FileAccess.Read)); Console.WriteLine("Opened BDF file"); double[] d = bdf.readAllChannelData(5); int N = d.Length; double n = (double)N; Console.WriteLine(d[0].ToString() + " " + d[1].ToString() + " " + d[100].ToString()); removeTrend(d, degree); Console.WriteLine(d[0].ToString() + " " + d[1].ToString() + " " + d[100].ToString()); double[] V = new double[filterN]; double c1 = 12D / (double)(filterN * (filterN - 1) * (filterN + 1)); double offset = ((double)filterN - 1D) / 2D; for (int i = 0; i < filterN; i++) V[i] = c1 * ((double)i - offset); List<double> filtered = new List<double>(64); byte[] marker = new byte[N]; bool inEvent = false; int eventLength = 0; double sign = 1D; for (int i = 0; i < N; i++) { double s = 0; for (int j = 0; j < filterN; j++) { int index = i + j - filterN / 2; if (index < 0) //handle start-up s += V[j] * d[0]; //repeat first value to its left else if (index >= N) //handle end s += V[j] * d[N - 1]; //repeat last value to its right else //usual case s += V[j] * d[index]; } if (Math.Abs(s) > threshold) //above threshold? { if (!inEvent) //found beginning of new event { sign = s > 0D ? 1D : -1D; eventLength = 0; inEvent = true; } filtered.Add(s - sign * threshold); eventLength++; } else //below threshold if (inEvent) //are we just exiting an event? { if (eventLength > minimumLength) //event counts only if longer than minimum length { eventTime e = new eventTime(); e.time = i - eventLength; e.length = eventLength; e.sign = sign; e.filteredSignal = filtered; filtered = new List<double>(64); //need new filtered array eventList.Add(e); } else filtered.Clear(); inEvent = false; } } int dataLength; double t; eventTime et0; eventTime et1; double t0 = (double)filterN / (2D * SR); for (int i = 0; i < eventList.Count - 1; i++) { et0 = eventList[i]; et1 = eventList[i + 1]; dataLength = Math.Min(et1.time - et0.time, 16000); double max = double.MinValue; for (int p = et0.time; p < et0.time + et0.length; p++) max = Math.Max(max, Math.Abs(d[p])); et0.A = et0.sign * max; //correct sign of displacement; could be max sign*Abs(displacement) et0.C = d[et0.time]; //estimate of initial offset et0.B = et0.C; //current actual "baseline" et0.a = 4D; //typical alpha et0.b = 0.04; //typical beta t = t0; //half filterN / SR if (et0.foundFit = fitSignal(d, et0.time, dataLength, ref et0.A, ref et0.B, ref et0.C, ref et0.a, ref et0.b, ref t)) et0.time += (int)(t * SR); Console.WriteLine(); Console.WriteLine(et0.time.ToString("0") + " (" + LM.Result.ToString() + ", " + LM.Iterations.ToString("0") + ", " + LM.ChiSquare.ToString("0.0") + ", " + LM.normalizedStandardErrorOfFit.ToString("0.00") + "): "); Console.WriteLine(et0.A.ToString("0.0") + " " + et0.B.ToString("0.0") + " " + et0.C.ToString("0.0") + " " + et0.a.ToString("0.000") + " " + et0.b.ToString("0.00000") + " " + t.ToString("0.000") + " "); NVector Sp = LM.parameterStandardError; Console.WriteLine(Sp[0].ToString("0.00") + " " + Sp[1].ToString("0.00") + " " + Sp[2].ToString("0.00") + " " + Sp[3].ToString("0.0000") + " " + Sp[4].ToString("0.000000") + " " + Sp[5].ToString("0.0000") + " "); } et0 = eventList[eventList.Count - 1]; dataLength = Math.Min(N - et0.time, 16000); et0.A = et0.sign * 5000D; //correct sign of displacement; could be max sign*Abs(displacement) et0.C = d[et0.time]; //estimate of initial offset et0.B = et0.C; et0.a = 4D; et0.b = 0.05; t = 0.25; if (et0.foundFit = fitSignal(d, et0.time, dataLength, ref et0.A, ref et0.B, ref et0.C, ref et0.a, ref et0.b, ref t)) et0.time += (int)(t * SR); Console.WriteLine(et0.time.ToString("0") + " (" + LM.Result.ToString() + ", " + LM.Iterations.ToString("0") + ", " + LM.ChiSquare.ToString("0.0") + "): " + et0.A.ToString("0.0") + " " + et0.B.ToString("0.0") + " " + et0.C.ToString("0.0") + " " + et0.a.ToString("0.000") + " " + et0.b.ToString("0.00000") + " " + t.ToString("0.000") + " "); Console.WriteLine("Total events found = " + eventList.Count.ToString("0")); ConsoleKeyInfo cki = Console.ReadKey(); }
private static bool fitSignal(double[] d, int beforeTime, eventTime current, int afterTime, double samplingRate, double minD, double maxD) { LevenbergMarquardt LM = new LevenbergMarquardt(func, Jfunc, new NVector(new double[] { minD - maxD, 2 * minD, 2 * minD, 0.25, 0.005, -0.25 }), new NVector(new double[] { maxD - minD, 2 * maxD, 2 * maxD, 40, 0.1, 0.5 }), null, new double[] { 0.0001, 0.00001, 0.00001, 0.01 }, LevenbergMarquardt.UpdateType.Marquardt); //set up LM processor, parameters and limits //determine subset of data around the detection signal int start = Math.Max(0, Math.Min(current.startTime - (beforeTime + (int)(deadtimeSecsAfter * samplingRate)), (int)(maxSecsBefore * samplingRate))); //up to 5 seconds before double newTOffset = (double)start / samplingRate; int dataLength = start + Math.Max(current.filterLength, Math.Min(afterTime - current.filterLength - current.startTime, (int)(maxSecsAfter * samplingRate))); //up to 40 seconds after start = current.startTime - start; dataLength = Math.Min(dataLength, d.Length - start); //watch for overrun past end of data array double max = double.MinValue; for (int v = current.startTime; v < current.startTime + current.length; v++) max = Math.Max(max, Math.Abs(d[v])); //Get signal max for initial estimate of A NVector t = new NVector(dataLength); for (int ti = 0; ti < dataLength; ti++) t[ti] = (double)ti / samplingRate - newTOffset; //create independent variable array NVector y = new NVector(dataLength); for (int i = 0; i < dataLength; i++) y[i] = d[start + i]; //create dependent variable array NVector p = LM.Calculate( new NVector(new double[] { current.sign * max, /* A */ d[current.startTime], /* B */ d[current.startTime], /* C */ 4D, /* alpha */ 0.04, /* beta */ 0D }), /* t0 */ t, y); //fit signal using Levenberg-Marquardt algorithm current.A = p[0]; //parse estimated parameters out current.B = p[1]; current.C = p[2]; current.a = p[3]; current.b = p[4]; if (LM.Result > 0) current.t0 += (int)(p[5] * samplingRate); //offset starting time by new t0, only if fit found current.chiSquare = LM.ChiSquare; //remember Chi square return LM.Result > 0; }