/********************************************************************** * trinomial_mult - multiplies a series of trinomials together and returns * the coefficients of the resulting polynomial. * * The multiplication has the following form: * * (x^2 + b[0]x + c[0])*(x^2 + b[1]x + c[1])*...*(x^2 + b[n-1]x + c[n-1]) * * The b[i] and c[i] coefficients are assumed to be complex and are passed * to the function as a pointers to arrays of doubles of length 2n. The real * part of the coefficients are stored in the even numbered elements of the * array and the imaginary parts are stored in the odd numbered elements. * * The resulting polynomial has the following form: * * x^2n + a[0]*x^2n-1 + a[1]*x^2n-2 + ... +a[2n-2]*x + a[2n-1] * * The a[i] coefficients can in general be complex but should in most cases * turn out to be real. The a[i] coefficients are returned by the function as * a pointer to an array of doubles of length 4n. The real and imaginary * parts are stored, respectively, in the even and odd elements of the array. * Storage for the array is allocated by the function and should be freed by * the calling program when no longer needed. * * Function arguments: * * n - The number of trinomials to multiply * b - Pointer to an array of doubles of length 2n. * c - Pointer to an array of doubles of length 2n. */ private static rawType[] trinomial_mult(int n, rawType[] b, rawType[] c) { int i, j; rawType[] a = new rawType[4 * n]; a[2] = c[0]; a[3] = c[1]; a[0] = b[0]; a[1] = b[1]; for (i = 1; i < n; ++i) { a[2 * (2 * i + 1)] += c[2 * i] * a[2 * (2 * i - 1)] - c[2 * i + 1] * a[2 * (2 * i - 1) + 1]; a[2 * (2 * i + 1) + 1] += c[2 * i] * a[2 * (2 * i - 1) + 1] + c[2 * i + 1] * a[2 * (2 * i - 1)]; for (j = 2 * i; j > 1; --j) { a[2 * j] += b[2 * i] * a[2 * (j - 1)] - b[2 * i + 1] * a[2 * (j - 1) + 1] + c[2 * i] * a[2 * (j - 2)] - c[2 * i + 1] * a[2 * (j - 2) + 1]; a[2 * j + 1] += b[2 * i] * a[2 * (j - 1) + 1] + b[2 * i + 1] * a[2 * (j - 1)] + c[2 * i] * a[2 * (j - 2) + 1] + c[2 * i + 1] * a[2 * (j - 2)]; } a[2] += b[2 * i] * a[0] - b[2 * i + 1] * a[1] + c[2 * i]; a[3] += b[2 * i] * a[1] + b[2 * i + 1] * a[0] + c[2 * i + 1]; a[0] += b[2 * i]; a[1] += b[2 * i + 1]; } return(a); }
/********************************************************************** * sf_bwbp - calculates the scaling factor for a butterworth bandpass filter. * The scaling factor is what the c coefficients must be multiplied by so * that the filter response has a maximum value of 1. */ private static rawType sf_bwbp(int n, rawType f1f, rawType f2f) { int k; // loop variables rawType ctt; // cotangent of theta rawType sfr, sfi; // real and imaginary parts of the scaling factor rawType parg; // pole angle rawType sparg; // sine of pole angle rawType cparg; // cosine of pole angle rawType a, b, c; // workspace variables ctt = (rawType)(1.0 / Math.Tan(Math.PI * (f2f - f1f) / 2.0)); sfr = (rawType)1.0; sfi = (rawType)0.0; for (k = 0; k < n; ++k) { parg = (rawType)(Math.PI * (rawType)(2 * k + 1) / (rawType)(2 * n)); sparg = (rawType)(ctt + Math.Sin(parg)); cparg = (rawType)(Math.Cos(parg)); a = (sfr + sfi) * (sparg - cparg); b = sfr * sparg; c = -sfi * cparg; sfr = b - c; sfi = a - b - c; } return((rawType)(1.0 / sfr)); }
//note that this only needs to filter the channels on this particular device public SALPA3(int length_sams, int asym_sams, int blank_sams, int ahead_sams, int forcepeg_sams, rawType railLow, rawType railHigh, int numElectrodes, int bufferLength, rawType[] thresh) { //MB defaults: this.length_sams = length_sams; // 75; this.asym_sams = asym_sams; // 10; this.blank_sams = blank_sams; // 75;//HACK try 20 this.ahead_sams = ahead_sams; // 5; //this.period_sams = period_sams;// 0; //this.delay_sams = 0; this.forcepeg_sams = forcepeg_sams;//10; this.thresh = thresh; this.numElectrodes = numElectrodes; this.railHigh = railHigh; this.railLow = railLow; this.numSamples = bufferLength; this.PRE = 2 * length_sams; this.POST = 2 * length_sams + 1 + ahead_sams; fitters = new LocalFit[numElectrodes]; for (int i = 0; i < numElectrodes; i++) { fitters[i] = new LocalFit(thresh[i], length_sams, blank_sams, ahead_sams, asym_sams, railHigh, railLow, bufferLength, forcepeg_sams); } }
private void calc_X3() { X3 = 0; for (int t = -tau; t <= tau; t++) { int t3 = t * t * t; rawType y = source[t0 + t]; X3 += ((double)t3 * y); } }
private bool ispegged(rawType r) { bool tmp = (r < railLow) || (r > railHigh); if (tmp) { tmp = true; } return(tmp); }
protected override void updateThreshold(rawType[] data, int channel) { /* After band pass filtering as for BandFlt, LimAda splits the data stream into 10 ms windows, * and determines the 2nd and 30th percentiles of the distribution of voltages found in each * such window. Call these voltages V.02 and V.30. (Note that both are usually negative because * of the filtering, which sets V.50 ~ <V> ~ 0.) It then performs two tests: * • Is the ratio of V.02 over V.30 less than 5? * • Is the absolute value of V.30 (significantly) non-zero? * The first test makes sure that there was no actual spike in the window; the second test * makes sure that the data in the window was not blanked out (e.g. by Rawsrv or Salpa). * If both tests are passed, the windows is considered ‘clean’, and V.02 is used to update the * current noise threshold estimate. Spikes are detected whenever the absolute value of the * voltage exceeds the current threshold, which is the output of passing the absolute values of * V.02 from all ‘clean’ windows through a low-pass filter with a time constant of 100 windows * (1 second if all are clean). This algorithm adapts rapidly to changing noise situations, while * not desensitizing during bursts. */ for (int j = 0; j < numChunks; ++j) { //Copy chunk into tempData, sort for (int k = 0; k < chunkSize; ++k) { tempData[k] = data[j * chunkSize + k]; } Array.Sort(tempData); VLo = tempData[VLoIdx]; VHi = tempData[VHiIdx]; if (VLo / VHi < 5.0 && VHi != 0.0) { //Low pass filter threshold if (VLo < 0) { tempThreshold = -alpha * VLo + (1 - alpha) * limAdaPrevious[channel]; } else { //tempThreshold = alpha * VLo + (1 - alpha) * limAdaPrevious[channel]; tempThreshold = limAdaPrevious[channel] + alpha * (VLo - limAdaPrevious[channel]); } } else { tempThreshold = limAdaPrevious[channel]; } finalThreshold = tempThreshold * thresholdMultiplier * 0.494F; //The 0.494 is to make it commensurate with RMS noise //Copy threshold to thrAda for (int k = 0; k < chunkSize; ++k) { threshold[channel, j *chunkSize + k] = finalThreshold; } limAdaPrevious[channel] = tempThreshold; } }
//functions to actually calculate the curve #region X0-3 functions private void calc_X012() { X0 = X1 = X2 = 0; for (int t = -tau; t <= tau; t++) { int t2 = t * t; rawType y = source[t0 + t]; X0 += y; X1 += ((double)t * y); X2 += ((double)t2 * y); } }
public RMSThresholdFixed(int spikeBufferLengthIn, int numChannelsIn, int downsampleIn, int spikeWaveformLength, int numPostIn, int numPreIn, rawType threshMult, int detectionDeadTime, int minSpikeWidth, int maxSpikeWidth , double maxSpikeAmp, double minSpikeSlope, int spikeIntegrationTime, double deviceRefresh, int threshPolarity) : base(spikeBufferLengthIn, numChannelsIn, downsampleIn, spikeWaveformLength, numPostIn, numPreIn, threshMult, detectionDeadTime, minSpikeWidth, maxSpikeWidth, maxSpikeAmp, minSpikeSlope, threshPolarity) { numUpdatesForTrain = (int)Math.Round(10 / deviceRefresh); // ten seconds worth of data used in training threshold = new rawType[1, numChannels]; numUpdates = new int[numChannels]; RMSList = new double[numChannels, numUpdatesForTrain]; ChanThresh = new double[numUpdatesForTrain]; ThreshSorted = new double[(int)Math.Floor((double)(numUpdatesForTrain - 9 * numUpdatesForTrain / 10))]; }
/********************************************************************** * dcof_bwbp - calculates the d coefficients for a butterworth bandpass * filter. The coefficients are returned as an array of doubles. */ public static rawType[] dcof_bwbp(int n, rawType f1f, rawType f2f) { int k; // loop variables rawType theta; // M_PI * (f2f - f1f) / 2.0 rawType cp; // cosine of phi rawType st; // sine of theta rawType ct; // cosine of theta rawType s2t; // sine of 2*theta rawType c2t; // cosine 0f 2*theta rawType[] rcof; // z^-2 coefficients rawType[] tcof; // z^-1 coefficients rawType[] dcof; // dk coefficients rawType parg; // pole angle rawType sparg; // sine of pole angle rawType cparg; // cosine of pole angle rawType a; // workspace variables cp = (rawType)(Math.Cos(Math.PI * (f2f + f1f) / 2.0)); theta = (rawType)(Math.PI * (f2f - f1f) / 2.0); st = (rawType)(Math.Sin(theta)); ct = (rawType)(Math.Cos(theta)); s2t = (rawType)(2.0 * st * ct); // sine of 2*theta c2t = (rawType)(2.0 * ct * ct - 1.0); // cosine of 2*theta rcof = new rawType[2 * n]; tcof = new rawType[2 * n]; for (k = 0; k < n; ++k) { parg = (rawType)(Math.PI * (rawType)(2 * k + 1) / (rawType)(2 * n)); sparg = (rawType)(Math.Sin(parg)); cparg = (rawType)(Math.Cos(parg)); a = (rawType)(1.0 + s2t * sparg); rcof[2 * k] = c2t / a; rcof[2 * k + 1] = s2t * cparg / a; tcof[2 * k] = (rawType)(-2.0 * cp * (ct + st * sparg) / a); tcof[2 * k + 1] = (rawType)(-2.0 * cp * st * cparg / a); } dcof = trinomial_mult(n, tcof, rcof); dcof[1] = dcof[0]; dcof[0] = (rawType)1.0; for (k = 3; k <= 2 * n; ++k) { dcof[k] = dcof[2 * k - 2]; } return(dcof); }
public void mein(string[] args)//filter data { if (args.Length < 2) { System.Console.Out.WriteLine("Not enough input arguments."); return; } StreamReader sr = new StreamReader(args[0]); StreamWriter sw = new StreamWriter(args[1]); string in_line = sr.ReadLine(); string out_line; rawType[] data; //Continue to read until you reach end of file //filter data from ascii file one line at a time while (in_line != null) { //convert string to raw //System.Console.Out.WriteLine(in_line); string[] words = in_line.Split(' '); //System.Console.Out.WriteLine("here?"); data = new rawType[words.Length - 1]; //System.Console.Out.WriteLine("here?"); for (int i = 0; i < words.Length - 1; i++)//-1 for the last one { //System.Console.Out.WriteLine(":"+ words[i]); data[i] = (rawType)Convert.ToDouble(words[i]); } //filter data filterData(data); //convert raw to string out_line = ""; for (int i = 0; i < data.Length; i++) { out_line += Convert.ToString(data[i]) + " "; } //output processed data in string form to output file. sw.WriteLine(out_line); in_line = sr.ReadLine(); } //close file streams sw.Close(); sr.Close(); }
public void Reset(int order, rawType samplingRate, rawType lowCut, rawType highCut, int bufferSize) { lock (this) { dcof = ButterworthFilter.dcof_bwbp(order, (rawType)(lowCut / (samplingRate * 0.5)), (rawType)(highCut / (samplingRate * 0.5))); ccof = ButterworthFilter.ccof_bwbp(order, (rawType)(lowCut / (samplingRate * 0.5)), (rawType)(highCut / (samplingRate * 0.5))); //for (int i = 0; i < dcof.Length; ++i) // dcof[i] = -dcof[i]; //Since you always subtract dcof lastInput = new rawType[ccof.Length - 1]; lastInput.Initialize(); lastOutput = new rawType[ccof.Length - 1]; lastOutput.Initialize(); oldData = new rawType[bufferSize]; } }
internal void calcThreshForOneBlock(rawType[] data, int channel, int idx) { rawType tempData = 0; for (int j = 0; j < spikeBufferLength / downsample; ++j) { double dd = data[j * downsample] * data[j * downsample]; if (dd > 0) // Don't include blanked samples { tempData += dd; //Square data } } tempData /= (spikeBufferLength / downsample); rawType thresholdTemp = (rawType)(Math.Sqrt(tempData) * _thresholdMultiplier); RMSList[channel, idx] = thresholdTemp; threshold[0, channel] = (threshold[0, channel] * (numUpdates[channel])) / (numUpdates[channel] + 1) + (thresholdTemp / (numUpdates[channel] + 1));// Recursive RMS estimate }
public AdaptiveRMSThreshold(int spikeBufferLengthIn, int numChannelsIn, int downsampleIn, int spikeWaveformLength, int numPostIn, int numPreIn, rawType threshMult, int detectionDeadTime, int minSpikeWidth, int maxSpikeWidth , double maxSpikeAmp, double minSpikeSlope, int spikeIntegrationTime, double deviceRefresh, int threshPolarity) : base(spikeBufferLengthIn, numChannelsIn, downsampleIn, spikeWaveformLength, numPostIn, numPreIn, threshMult, detectionDeadTime, minSpikeWidth, maxSpikeWidth, maxSpikeAmp, minSpikeSlope, threshPolarity) { numUpdatesForTrain = (int)Math.Round(updateBlockLengthSec / deviceRefresh); // 1 second worth of data used for estimating each RMS point to be feed into the exp filter filterHalfLife = filterHalfLifeSec / updateBlockLengthSec; // seconds threshold = new rawType[1, numChannels]; numUpdates = new int[numChannels]; RMSList = new double[numChannels, numUpdatesForTrain]; ChanThresh = new double[numUpdatesForTrain]; ThreshSorted = new double[(int)Math.Floor((double)(numUpdatesForTrain - 9 * numUpdatesForTrain / 10))]; alpha = 2 / (2.8854 * filterHalfLife + 1); chanStarted = new bool[numChannels]; warmupThreshold = threshold; warmedUp = new bool[numChannels]; countWarmup = new int[numChannels]; }
public LocalFit(rawType y_threshold, int N, int t_blankdepeg, int t_ahead, int t_chi2, rawType railHigh, rawType railLow, int bufferlength, int forcepegsamples) { elecState = state.PEGGED; this.N = N; this.tau = N; this.y_threshold = y_threshold; this.t_blankdepeg = t_blankdepeg; this.t_ahead = t_ahead; this.t_chi2 = t_chi2; this.railHigh = railHigh; this.railLow = railLow; this.forcepegsamples = forcepegsamples; //this code in the init_t section of DAW's MB my_thresh = 3.92 * t_chi2 * y_threshold; //a bunch of terms that get used repeatedly: tau_plus_1 = tau + 1; tau_plus_1_squared = tau_plus_1 * tau_plus_1; tau_plus_1_cubed = tau_plus_1_squared * tau_plus_1; minus_tau = -tau; minus_tau_squared = minus_tau * minus_tau; minus_tau_cubed = minus_tau_squared * minus_tau; T0 = T2 = T4 = T6 = 0; for (int t = -tau; t <= tau; t++) { int t2 = t * t; int t4 = t2 * t2; int t6 = t4 * t2; T0 += 1; T2 += t2; T4 += t4; T6 += t6; } this.bufferlength = bufferlength; PRE = 2 * N; POST = 2 * N + 1 + t_ahead; source = new rawType[PRE + POST + bufferlength]; previousData = new rawType[PRE + POST]; }
public LimAda(int spikeBufferLengthIn, int numChannelsIn, int downsampleIn, int spikeWaveformLength, int numPostIn, int numPreIn, rawType threshMult, int detectionDeadTime, int minSpikeWidth, int maxSpikeWidth, double maxSpikeAmp , double minSpikeSlope, int spikeIntegrationTime, int spikeSamplingRateIn, int threshPolarity) : base(spikeBufferLengthIn, numChannelsIn, downsampleIn, spikeWaveformLength, numPostIn, numPreIn, threshMult, detectionDeadTime, minSpikeWidth, maxSpikeWidth, maxSpikeAmp, minSpikeSlope, threshPolarity) { chunkSize = (int)(0.01 * (rawType)spikeSamplingRateIn); //Big enough for 10ms of data numChunks = spikeBufferLength / chunkSize; VLoIdx = (int)(0.02 * chunkSize); VHiIdx = (int)(0.3 * chunkSize); tempData = new rawType[chunkSize]; //To hold 10ms window threshold = new rawType[numChannels, spikeBufferLength]; limAdaPrevious = new rawType[numChannels]; for (int i = 0; i < numChannels; ++i) { limAdaPrevious[i] = 0.0001; } thresholdCarryOverBuffer = new double[numChannels, carryOverLength]; firstPass = new bool[numChannels]; }
/********************************************************************** * ccof_bwbp - calculates the c coefficients for a butterworth bandpass * filter. The coefficients are returned as an array of integers. */ public static rawType[] ccof_bwbp(int n, rawType lowCut, rawType highCut) { int[] tcof; int[] ccofTemp = new int[2 * n + 1]; rawType[] ccof = new rawType[2 * n + 1]; tcof = ccof_bwhp(n); for (int i = 0; i < n; ++i) { ccofTemp[2 * i] = tcof[i]; ccofTemp[2 * i + 1] = 0; } ccofTemp[2 * n] = tcof[n]; rawType scalingFactor = sf_bwbp(n, lowCut, highCut); for (int i = 0; i < ccof.Length; ++i) { ccof[i] = ccofTemp[i] * scalingFactor; } return(ccof); }
unsafe public void filterData(rawType[] data) { lock (this) { rawType temp = 0; //for (int i = 0; i < data.Length; ++i) //{ // temp = data[i]; // data[i] *= ccof[0]; // for (int j = 1; j < ccof.Length; ++j) // data[i] += ccof[j] * lastInput[j - 1] - dcof[j] * lastOutput[j - 1]; // //Update lastInput and lastOutput // for (int j = lastInput.Length - 1; j > 0; --j) // { // lastInput[j] = lastInput[j - 1]; // lastOutput[j] = lastOutput[j - 1]; // } // lastInput[0] = temp; // lastOutput[0] = data[i]; //} fixed(double *pData = data) { fixed(double *pOldData = oldData) { //for (int i = 0; i < data.Length; ++i) // oldData[i] = data[i]; for (int i = 0; i < data.Length; ++i) { pOldData[i] = pData[i]; } } for (int i = 0; i < ccof.Length; ++i) { temp = pData[i]; pData[i] *= ccof[0]; for (int j = 1; j < ccof.Length; ++j) { pData[i] += ccof[j] * lastInput[j - 1] - dcof[j] * lastOutput[j - 1]; } //Update lastInput and lastOutput for (int j = lastInput.Length - 1; j > 0; --j) { lastInput[j] = lastInput[j - 1]; lastOutput[j] = lastOutput[j - 1]; } lastInput[0] = temp; lastOutput[0] = pData[i]; } for (int i = ccof.Length; i < data.Length; ++i) { pData[i] *= ccof[0]; for (int j = 1; j < ccof.Length; ++j) { pData[i] += ccof[j] * oldData[i - j] - dcof[j] * pData[i - j]; } } for (int i = 0; i < lastOutput.Length; ++i) { lastOutput[i] = pData[data.Length - 1 - i]; lastInput[i] = oldData[oldData.Length - 1 - i]; } } } }
public ButterworthFilter(int order, rawType samplingRate, rawType lowCutF, rawType highCutF, int bufferSize) { Reset(order, samplingRate, lowCutF, highCutF, bufferSize); }