/// <summary> /// Create synthetic USB data frames. /// </summary> /// <param name="neuralSynthData">Neural waveforms.</param> /// <param name="EMGSynthData">EMG waveforms.</param> /// <param name="rawFrameBlock">Array for raw USB data.</param> /// <param name="dataRate">Data rate (High, Medium, or Low).</param> /// <param name="hammingDecoder">Hamming decoder object.</param> /// <param name="BER">Bit error rate (set to zero to disable).</param> private void USBDataFrameCreate(double[,] neuralSynthData, double[,] EMGSynthData, UInt16[] rawFrameBlock, DataRate dataRate, HammingDecoder hammingDecoder, double BER) { int frame, i, channel, index, indexNeural; int dataWord; Random myRand = new Random(unchecked ((int)DateTime.Now.Ticks)); index = 0; indexNeural = 0; for (frame = 0; frame < Constant.FramesPerBlock; frame++) { for (i = 0; i < Constant.NeuralSamplesPerFrame; i++) { for (channel = Constant.MinNeuralChannel(dataRate); channel <= Constant.MaxNeuralChannel(dataRate); channel++) { dataWord = (int)((neuralSynthData[channel, indexNeural] / Constant.ADCStepNeural) + Constant.ADCOffset); rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, BER); } indexNeural++; } for (channel = 0; channel < Constant.TotalEMGChannels; channel++) { dataWord = (int)((EMGSynthData[channel, frame] / Constant.ADCStepEMG) + Constant.ADCOffset); rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, BER); } dataWord = 0; // AUX 1 rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, BER); dataWord = (int)(((1.8 + 0.1 * gaussian(myRand)) / Constant.ADCStepAux) + Constant.ADCOffset); rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, BER); dataWord = 1536; // Chip ID rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, BER); dataWord = chipCounter; // Chip Counter rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, BER); dataWord = frameCounterLow; // Frame Counter Low rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, 0.0); dataWord = frameCounterHigh; // Frame Counter High rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, 0.0); dataWord = 960; // Frame Timer Low (= 960) rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, 0.0); dataWord = 0; // Frame Timer High (= 960) rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, 0.0); dataWord = 0; // TTL Inputs rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, 0.0); dataWord = 144; // Frame Marker Correlation rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, 0.0); if (dataRate == DataRate.Medium || dataRate == DataRate.Low) { dataWord = 0; rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, 0.0); rawFrameBlock[index++] = hammingDecoder.EncodeData(dataWord, myRand, 0.0); } chipCounter++; if (chipCounter > 2047) { chipCounter = 0; } frameCounterLow++; if (frameCounterLow > 2047) { frameCounterLow = 0; frameCounterHigh++; if (frameCounterHigh > 2047) { frameCounterHigh = 0; } } } }
/// <summary> /// USBData constructor. /// </summary> /// <param name="rawFrameBlock">Data block from USB interface.</param> /// <param name="dataRate">Data rate (Low, Medium, or High).</param> /// <param name="hammingDecoder">Hamming code decoder object.</param> public USBData(UInt16[] rawFrameBlock, DataRate dataRate, bool rawDataMode, HammingDecoder hammingDecoder) { int frame, channel, index, indexNeural, i; int numBitErrors; int bitErrorCount = 0; int wordErrorCount = 0; int minNeuralChannel = Constant.MinNeuralChannel(dataRate); int maxNeuralChannel = Constant.MaxNeuralChannel(dataRate); int frameSize = Constant.FrameSize(dataRate, rawDataMode); const double expectedFrameTimer = Constant.FPGAClockFreq * Constant.FramePeriod / 32.0; // = 960 const int maxFrameTimer = (int)(expectedFrameTimer * 1.01); const int minFrameTimer = (int)(expectedFrameTimer / 1.01); missingFrameCount = 0; falseFrameCount = 0; timeStampNanos = MainForm.GetAbsTimeNS(); for (i = 0; i < frameSize * Constant.FramesPerBlock; i++) { rawData[i] = rawFrameBlock[i]; } index = 0; indexNeural = 0; double vOut; for (frame = 0; frame < Constant.FramesPerBlock; frame++) { for (i = 0; i < Constant.NeuralSamplesPerFrame; i++) { for (channel = minNeuralChannel; channel <= maxNeuralChannel; channel++) { neuralData[channel, indexNeural + i] = Constant.ADCStepNeural * ((neuralData16[channel, indexNeural + i] = hammingDecoder.DecodeDataCountErrors(rawFrameBlock[index], out numBitErrors, ref bitErrorCount, ref wordErrorCount)) - Constant.ADCOffset); if (numBitErrors == 2 && (indexNeural + i) > 0) { neuralData[channel, indexNeural + i] = neuralData[channel, indexNeural + i - 1]; neuralData16[channel, indexNeural + i] = neuralData16[channel, indexNeural + i - 1]; } index++; } for (channel = 0; channel < minNeuralChannel; channel++) { neuralData[channel, indexNeural + i] = 0.0; neuralData16[channel, indexNeural + i] = (UInt16)Constant.ADCOffset; } for (channel = maxNeuralChannel + 1; channel < Constant.TotalNeuralChannels; channel++) { neuralData[channel, indexNeural + i] = 0.0; neuralData16[channel, indexNeural + i] = (UInt16)Constant.ADCOffset; } } indexNeural += Constant.NeuralSamplesPerFrame; for (channel = 0; channel < Constant.TotalEMGChannels; channel++) { EMGData[channel, frame] = Constant.ADCStepEMG * ((EMGData16[channel, frame] = hammingDecoder.DecodeDataCountErrors(rawFrameBlock[index], out numBitErrors, ref bitErrorCount, ref wordErrorCount)) - Constant.ADCOffset); if (numBitErrors == 2 && frame > 0) { EMGData[channel, frame] = EMGData[channel, frame - 1]; EMGData16[channel, frame] = EMGData16[channel, frame - 1]; } index++; } for (channel = 0; channel < Constant.TotalAuxChannels; channel++) { vOut = Constant.ADCStep * (hammingDecoder.DecodeDataCountErrors(rawFrameBlock[index], out numBitErrors, ref bitErrorCount, ref wordErrorCount) - Constant.ADCOffset); // Empirical equation modeling nonlinearity of auxiliary amplifier (see testing data, 6/25/11) auxData[channel, frame] = 4.343 * vOut + 0.1 * Math.Exp(12.0 * (vOut - 0.82)) + 0.2 * Math.Exp(130.0 * (vOut - 1.0)); // Limit result to between 0 and 6.0 V if (auxData[channel, frame] > 6.0) { auxData[channel, frame] = 6.0; } else if (auxData[channel, frame] < 0.0) { auxData[channel, frame] = 0.0; } if (numBitErrors == 2 && frame > 0) { auxData[channel, frame] = auxData[channel, frame - 1]; } auxData16[channel, frame] = (UInt16)(auxData[channel, frame] / Constant.ADCStep + Constant.ADCOffset); index++; } chipID[frame] = hammingDecoder.DecodeDataCountErrors(rawFrameBlock[index], out numBitErrors, ref bitErrorCount, ref wordErrorCount); index++; chipFrameCounter[frame] = hammingDecoder.DecodeDataCountErrors(rawFrameBlock[index], out numBitErrors, ref bitErrorCount, ref wordErrorCount); index++; boardFrameCounter[frame] = hammingDecoder.DecodeData(rawFrameBlock[index++]) + 2048 * hammingDecoder.DecodeData(rawFrameBlock[index++]); boardFrameTimer[frame] = hammingDecoder.DecodeData(rawFrameBlock[index++]) + 2048 * hammingDecoder.DecodeData(rawFrameBlock[index++]); TTLInputs[frame] = hammingDecoder.DecodeData(rawFrameBlock[index++]); frameMarkerCorrelation[frame] = hammingDecoder.DecodeData(rawFrameBlock[index++]); if (dataRate == DataRate.Low || dataRate == DataRate.Medium) { index += 2; } if (boardFrameTimer[frame] > maxFrameTimer) { missingFrameCount++; } else if (boardFrameTimer[frame] < minFrameTimer) { falseFrameCount++; } } BER = ((double)bitErrorCount) / ((double)(Constant.FramesPerBlock * Constant.BitsPerWord * (Constant.NeuralSamplesPerFrame * (maxNeuralChannel - minNeuralChannel + 1) + (Constant.TotalEMGChannels + Constant.TotalAuxChannels + 2)))); WER = ((double)wordErrorCount) / ((double)(Constant.FramesPerBlock * (Constant.NeuralSamplesPerFrame * (maxNeuralChannel - minNeuralChannel + 1) + (Constant.TotalEMGChannels + Constant.TotalAuxChannels + 2)))); }
/// <summary> /// Check to see if there is at least 25 msec worth of data (40 frames) in the USB read buffer. /// </summary> /// <param name="plotQueue">Queue used for plotting data to screen.</param> /// <param name="saveQueue">Queue used for saving data to disk.</param> /// <param name="dataRate">Data rate (High, Medium, or Low).</param> /// <param name="hammingDecoder">Hamming decoder object.</param> /// <returns>Number of pages remaining in FPGA board RAM buffer.</returns> public int CheckForUSBData(Queue <USBData> plotQueue, Queue <USBData> saveQueue, DataRate dataRate, bool rawDataMode, HammingDecoder hammingDecoder) { bool haveEnoughData = false; int i, numPagesInRAM, numPagesLeftInRAM, numBytesToRead, numBytesRead, indexUSB, indexFrame; int word1, word2, word3, word4; int pageThreshold = 20; numPagesLeftInRAM = 0; if (rawDataMode) { pageThreshold = 24; } else { switch (dataRate) { case DataRate.High: pageThreshold = 20; break; case DataRate.Medium: pageThreshold = 11; break; case DataRate.Low: pageThreshold = 6; break; } } if (synthDataMode) { if (newSynthDataReady) { //HACK XXX TODO FIXME TESTING -- comment-in the following conditional to test the "no data from USB condition" //long sec = System.DateTime.Now.Second; //if ((sec / 4) % 2 == 0) //{ haveEnoughData = true; newSynthDataReady = false; //} } } else { myXEM.UpdateWireOuts(); numPagesInRAM = (int)myXEM.GetWireOutValue(wireOutNumPages); //Debug.WriteLine("numPagesInRAM = " + numPagesInRAM); if (numPagesInRAM > pageThreshold) { haveEnoughData = true; } } if (haveEnoughData) { USBData USBDataBlock; long tsNow = MainForm.GetAbsTimeNS(); if (synthDataMode) { int channel, j; Random myRand = new Random(unchecked ((int)DateTime.Now.Ticks)); for (channel = 0; channel < Constant.TotalNeuralChannels; channel++) { for (i = 0; i < Constant.NeuralSamplesPerFrame * Constant.FramesPerBlock; i++) { neuralSynthData[channel, i] = synthSpikeOffset[channel] + 5.7 * gaussian(myRand); // create realistic offset and background of 5.7 uV rms noise } i = 0; while (i < Constant.NeuralSamplesPerFrame * Constant.FramesPerBlock - 52) // 52 samples = 2 msec (refractory period) { if (myRand.NextDouble() < 0.01) { for (j = 0; j < synthSpikeWidth1[channel]; j++) { neuralSynthData[channel, i + j] += (synthSpikeAmp1[channel] * Math.Exp(-1 * (double)j / 12.5) * Math.Sin(6.28 * (double)j / synthSpikeWidth1[channel])); } i += 40 + 52; // advance by 2 msec (refractory period) } else if (myRand.NextDouble() < 0.02) { for (j = 0; j < synthSpikeWidth2[channel]; j++) { neuralSynthData[channel, i + j] += (synthSpikeAmp2[channel] * Math.Exp(-1 * (double)j / 12.5) * Math.Sin(6.28 * (double)j / synthSpikeWidth2[channel])); } i += 40 + 52; // advance by 2 msec (refractory period) } else { i += 26; // advance by 1 msec } } } for (channel = 0; channel < Constant.TotalEMGChannels; channel++) { for (i = 0; i < Constant.FramesPerBlock; i++) { EMGSynthData[channel, i] = synthEMGOffset[channel] + 0.043 * gaussian(myRand); // create realistic offset and background of 43 uV rms noise } i = 0; while (i < Constant.FramesPerBlock - 10) // 10 = max 'spike' width { if (myRand.NextDouble() < 0.004) { for (j = 0; j < synthEMGWidth1[channel]; j++) { EMGSynthData[channel, i + j] += (synthEMGAmp1[channel] * Math.Exp(-1 * (double)j / 12.5) * Math.Sin(6.28 * (double)j / synthEMGWidth1[channel])); } i += 10 + 3; // advance by 2 msec (refractory period) } else if (myRand.NextDouble() < 0.008) { for (j = 0; j < synthEMGWidth2[channel]; j++) { EMGSynthData[channel, i + j] += (synthEMGAmp2[channel] * Math.Exp(-1 * (double)j / 12.5) * Math.Sin(6.28 * (double)j / synthEMGWidth2[channel])); } i += 10 + 3; // advance by 2 msec (refractory period) } else { i += 2; // advance by 1 msec } } } // USBDataBlock = new USBData(neuralSynthData, EMGSynthData); USBDataFrameCreate(neuralSynthData, EMGSynthData, rawFrameBlock, dataRate, hammingDecoder, 0.001); USBDataBlock = new USBData(rawFrameBlock, dataRate, rawDataMode, hammingDecoder); numPagesLeftInRAM = 1; } else { numBytesToRead = (4 * Constant.FrameSize(dataRate, rawDataMode) / 3) * Constant.BytesPerWord * Constant.FramesPerBlock; numBytesRead = myXEM.ReadFromPipeOut(pipeOutData, numBytesToRead, USBBuf); if (numBytesRead != numBytesToRead) { UsbException e = new UsbException("USB read error; not enough bytes read"); throw e; } if ((USBBuf[1] & 0xe0) != 0xe0) // USBBuf[1] = high byte of first word; should have the form 111xxxxx if we are in sync { Debug.WriteLine("Resync"); // Reset FIFOs myXEM.SetWireInValue(wireInResetReadWrite, 0x04); myXEM.UpdateWireIns(); myXEM.SetWireInValue(wireInResetReadWrite, 0x00); myXEM.UpdateWireIns(); // Enable data transfers myXEM.SetWireInValue(wireInResetReadWrite, 0x03); // read and write myXEM.UpdateWireIns(); return(0); // abandon corrupted frame; get out of here // UsbException e = new UsbException("USB sync error"); // throw e; } indexUSB = 0; indexFrame = 0; for (i = 0; i < (Constant.FrameSize(dataRate, rawDataMode) / 3) * Constant.FramesPerBlock; i++) { word1 = 256 * Convert.ToInt32(USBBuf[indexUSB + 1]) + Convert.ToInt32(USBBuf[indexUSB]); indexUSB += 2; word2 = 256 * Convert.ToInt32(USBBuf[indexUSB + 1]) + Convert.ToInt32(USBBuf[indexUSB]); indexUSB += 2; word3 = 256 * Convert.ToInt32(USBBuf[indexUSB + 1]) + Convert.ToInt32(USBBuf[indexUSB]); indexUSB += 2; word4 = 256 * Convert.ToInt32(USBBuf[indexUSB + 1]) + Convert.ToInt32(USBBuf[indexUSB]); indexUSB += 2; rawFrameBlock[indexFrame++] = Convert.ToUInt16(((word1 & 0x0f00) << 4) + (word2 & 0x0fff)); rawFrameBlock[indexFrame++] = Convert.ToUInt16(((word1 & 0x00f0) << 8) + (word3 & 0x0fff)); rawFrameBlock[indexFrame++] = Convert.ToUInt16(((word1 & 0x000f) << 12) + (word4 & 0x0fff)); } USBDataBlock = new USBData(rawFrameBlock, dataRate, rawDataMode, hammingDecoder); myXEM.UpdateWireOuts(); numPagesLeftInRAM = (int)myXEM.GetWireOutValue(wireOutNumPages); } if (dataJustStarted) { USBDataBlock.InitNeuralFilterState(neuralState, neuralDelay1, neuralDelay2, neuralNotchDelay1, neuralNotchDelay2); USBDataBlock.InitEMGFilterState(EMGState, EMGDelay1, EMGDelay2, EMGNotchDelay1, EMGNotchDelay2); dataJustStarted = false; } if (enableHPF) { USBDataBlock.NeuralFilterHPF(neuralState, fHPF, Constant.NeuralSampleRate); USBDataBlock.EMGFilterHPF(EMGState, fHPF, Constant.EMGSampleRate); } if (enableNotch) { USBDataBlock.NeuralFilterNotch(neuralDelay1, neuralDelay2, neuralNotchDelay1, neuralNotchDelay2, fNotch, Constant.NeuralSampleRate); USBDataBlock.EMGFilterNotch(EMGDelay1, EMGDelay2, EMGNotchDelay1, EMGNotchDelay2, fNotch, Constant.EMGSampleRate); } USBDataBlock.timeStampNanos = tsNow; plotQueue.Enqueue(USBDataBlock); saveQueue.Enqueue(USBDataBlock); } return(numPagesLeftInRAM); }