void
SignalDisplay::AdaptTo( const GenericSignal& inSignal )
{
    // Any changes in the signal size that we must react to?
    bool reconfigure = false;
    if( inSignal.Elements() > mNumSamples )
    {
        OSMutex::Lock lock( mDataLock );
        SetNumSamples( inSignal.Elements() );
        reconfigure = true;
    }
    if( inSignal.Channels() != mData.Channels() )
    {
        OSMutex::Lock lock( mDataLock );
        int newNumDisplayGroups = ( inSignal.Channels() - mMarkerChannels - 1 ) / mChannelGroupSize + 1;
        if( mNumDisplayGroups == 0 )
            mNumDisplayGroups = min<int>( newNumDisplayGroups, cInitialMaxDisplayGroups );
        else if( newNumDisplayGroups < mNumDisplayGroups )
            mNumDisplayGroups = newNumDisplayGroups;
        reconfigure = true;
    }
    if( reconfigure )
    {
        OSMutex::Lock lock( mDataLock );
        mData = GenericSignal( inSignal.Channels(), mNumSamples );
        SetDisplayGroups( DisplayGroups() );
        SyncLabelWidth();
        mSampleCursor = 0;
        Invalidate();
    }
}
// **************************************************************************
// Function:   Process
// Purpose:    This function is called within the data acquisition loop
//             it fills its output signal with values
//             and does not return until all data has been acquired.
// Parameters: References to input signal (ignored) and output signal.
// Returns:    N/A
// **************************************************************************
void NeuroSkyADC::Process( const GenericSignal&, GenericSignal& Output )
{
  // Clear out old data
  mDataBlock.clear();
  
  // Fill a Sample Block
  for( int i = 0; i < Output.Elements(); i++ )
  {    
    // Read Packets until new raw data comes in
    while( true )
    {
      TG_ReadPackets( mConnectionID, 1 );
      if( TG_GetValueStatus( mConnectionID, TG_DATA_RAW ) != 0 )
        break;
      Sleep( 1 );
    } 
    
    // Push the newest data into the data block
    mDataBlock.push_back( TG_GetValue( mConnectionID, TG_DATA_RAW ) );
  }
  
  // Fill the output with that data
  for( int sample = 0; sample < Output.Elements(); ++sample )
      Output( 0, sample ) = mDataBlock[ sample ];
}
void
Normalizer::Process( const GenericSignal& Input, GenericSignal& Output )
{
  if( mDoAdapt )
  {
    for( size_t channel = 0; channel < mBufferConditions.size(); ++channel )
      for( size_t buffer = 0; buffer < mBufferConditions[ channel ].size(); ++buffer )
      {
        double label = mBufferConditions[ channel ][ buffer ].Evaluate( &Input );
        if( label != 0 )
          for( int sample = 0; sample < Input.Elements(); ++sample )
            mDataBuffers[ channel ][ buffer ].Put( Input( channel, sample ) );
      }
    if( mpUpdateTrigger != NULL )
    {
      bool currentTrigger = mpUpdateTrigger->Evaluate( &Input );
      if( currentTrigger && !mPreviousTrigger )
        Update();
      mPreviousTrigger = currentTrigger;
    }
    else
      Update();
  }

  for( int channel = 0; channel < Input.Channels(); ++channel )
    for( int sample = 0; sample < Input.Elements(); ++sample )
      Output( channel, sample )
        = ( Input( channel, sample ) - mOffsets[ channel ] ) * mGains[ channel ];
}
void
FFTFilter::Process( const GenericSignal& Input, GenericSignal& Output )
{
  if( mFFTOutputSignal == eInput )
    Output = Input;

  for( size_t i = 0; i < mFFTInputChannels.size(); ++i )
  {
    // Copy input signal values to the value buffer.
    vector<float>& buffer = mValueBuffers[ i ];
    int inputSize = Input.Elements(),
        bufferSize = static_cast<int>( buffer.size() );
    // Move old values towards the beginning of the buffer, if any.
    for( int j = 0; j < bufferSize - inputSize; ++j )
      buffer[ j ] = buffer[ j + inputSize ];
    // Copy new values to the end of the buffer;
    // buffer size may be greater or less than input size.
    for( int j = ::max( 0, bufferSize - inputSize ); j < bufferSize; ++j )
      buffer[ j ] = static_cast<float>( Input( mFFTInputChannels[ i ], j + inputSize - bufferSize ) );
    // Prepare the buffer.
    if( mFFTWindow == eNone )
      for( int j = 0; j < bufferSize; ++j )
        mFFT.Input( j ) = buffer[ j ];
    else
      for( int j = 0; j < bufferSize; ++j )
        mFFT.Input( j ) = buffer[ j ] * mWindow[ j ];
    // Compute the power spectrum and visualize it if requested.
    mFFT.Compute();
    if( mVisualizeFFT || mFFTOutputSignal == ePower )
    {
      int maxIdx = mPowerSpectrum.Channels() - 1;
      double normFactor = 1.0 / bufferSize;
      mPowerSpectrum( maxIdx, 0 ) = mFFT.Output( 0 ) * mFFT.Output( 0 ) * normFactor;
      for( int k = 1; k < ( bufferSize + 1 ) / 2; ++k )
        mPowerSpectrum( maxIdx - k, 0 ) = (
          mFFT.Output( k ) * mFFT.Output( k ) +
          mFFT.Output( bufferSize - k ) * mFFT.Output( bufferSize - k ) ) * normFactor;
      if( bufferSize % 2 == 0 )
        mPowerSpectrum( maxIdx - bufferSize / 2, 0 )
          = mFFT.Output( bufferSize / 2 ) * mFFT.Output( bufferSize / 2 ) * normFactor;
    }
    if( mVisualizeFFT )
      mVisualizations[ i ].Send( mPowerSpectrum );

    if( mFFTOutputSignal == ePower )
    {
      for( int j = 0; j < Output.Elements(); ++j )
        Output( i, j ) = mPowerSpectrum( mPowerSpectrum.Channels() - 1 - j, 0 );
    }
    else if( mFFTOutputSignal == eHalfcomplex )
    {
      double normFactor = 1.0 / ::sqrt( 1.0 * bufferSize );
      for( int j = 0; j < Output.Elements(); ++j )
        Output( i, j ) = mFFT.Output( j ) * normFactor;
    }
  }
}
////////////////////////////////////////////////////////////////////////////////
// MatlabEngine::DoubleMatrix definitions                                     //
////////////////////////////////////////////////////////////////////////////////
MatlabEngine::DoubleMatrix::DoubleMatrix( const GenericSignal& inSignal )
    : vector<vector<double> >( inSignal.Channels(), vector<double>( inSignal.Elements() ) )
{
    for( size_t channel = 0; channel < size(); ++channel )
        for( size_t sample = 0; sample < ( *this )[ channel ].size(); ++sample )
            ( *this )[ channel ][ sample ] = inSignal( channel, sample );
}
void
CustomFIRFilter::Process( const GenericSignal& Input, GenericSignal& Output )
{
  if( mBuffer.empty() || mFilter.size() == 0 )
    Output = Input;
  else for( size_t channel = 0; channel < mBuffer.size(); ++channel )
  {
    int bufferLength = static_cast<int>( mBuffer[channel].size() ),
        filterLength = static_cast<int>( mFilter.size() ),
        inputLength = Input.Elements();
    // Move buffer content towards the buffer's begin.
    for( int sample = 0; sample < bufferLength - inputLength; ++sample )
      mBuffer[channel][sample] = mBuffer[channel][sample + inputLength];
    // Copy current input into the buffer's end.
    for( int sample = 0; sample < inputLength; ++sample )
      mBuffer[channel][bufferLength - inputLength + sample] = Input( channel, sample );
    // Compute buffer's convolution with coefficient vector.
    DataVector result( 0.0, inputLength );
    for( int sample = 0; sample < inputLength; ++sample )
      result[sample] = inner_product( &mFilter[0], &mFilter[filterLength], &mBuffer[channel][sample], 0.0 );
    // Compute output.
    for( int sample = 0; sample < inputLength; ++sample )
      Output( channel, sample ) = result[sample];
  }
}
// The Process() function is called from the main thread in regular intervals.
void
BufferedADC::Process( const GenericSignal&,
                      GenericSignal& Output )
{
    this->OnProcess();
    AcquisitionBuffer& buffer = mpBuffers[mReadCursor];
    ++mReadCursor %= mSourceBufferSize;

    Lock lock( buffer );
    if( !IsAcquiring() )
    {
        bcierr_ << ( mError.empty() ? "Acquisition Error" : mError );
        if( State( "Running" ) )
            State( "Running" ) = 0;
        return;
    }

    if( buffer.Signal.Channels() == Output.Channels() )
        Output = buffer.Signal;
    else
    {
        const LabelIndex& labels = mAcquisitionProperties.ChannelLabels();
        for( int el = 0; el < Output.Elements(); ++el )
        {
            for( int ch = 0; ch < Output.Channels(); ++ch )
                Output( ch, el ) = buffer.Signal( ch, el );
            for( int ch = Output.Channels(); ch < buffer.Signal.Channels(); ++ch )
                State( labels[ch].c_str() + 1 )( el ) = static_cast<State::ValueType>( buffer.Signal( ch, el ) );
        }
    }
    State( "SourceTime" ) = buffer.TimeStamp;
}
void
BCI2000OutputFormat::Write( ostream& os, const GenericSignal& inSignal, const StateVector& inStatevector )
{
  switch( mInputProperties.Type() )
  {
    case SignalType::int16:
    case SignalType::float32:
    case SignalType::int32:
      // Note that the order of Elements and Channels differs from the one in the
      // socket protocol.
      for( int j = 0; j < inSignal.Elements(); ++j )
      {
        for( int i = 0; i < inSignal.Channels(); ++i )
          inSignal.WriteValueBinary( os, i, j );
        os.write(
          reinterpret_cast<const char*>( inStatevector( min( j, inStatevector.Samples() - 1 ) ).Data() ),
          inStatevector.Length()
        );
      }
      break;

    default:
      bcierr << "Unsupported signal data type" << endl;
  }
}
void
LinearClassifier::Process( const GenericSignal& Input, GenericSignal& Output )
{
  for( int ch = 0; ch < Output.Channels(); ++ch )
    for( int el = 0; el < Output.Elements(); ++el )
      Output( ch, el ) = 0.0;

  for( size_t i = 0; i < mWeights.size(); ++i )
    Output( mOutputChannels[ i ], 0 )
      += Input( mInputChannels[ i ], mInputElements[ i ] ) * mWeights[ i ];
}
// **************************************************************************
// Function:   ADReadDataBlock
// Purpose:    This function is called within fMain->MainDataAcqLoop()
//             it fills the already initialized array RawEEG with values
//             and DOES NOT RETURN, UNTIL ALL DATA IS ACQUIRED
// Parameters: N/A
// Returns:    0 ... on error
//             1 ... no error
// **************************************************************************
void ModularEEGADC::Process( const GenericSignal&, GenericSignal& signal )
{
int     value;
const long  maxvalue = ( 1L << 15 ) - 1,
            minvalue = - ( 1L << 15 );

  // generate the sine wave and write it into the signal
  for (int sample=0; sample<signal.Elements(); sample++)
  {
    read_channels(devicehandle,protocol);
    for (int channel=0; channel<signal.Channels(); channel++)
    {
      value = PACKET.buffer[channel];
      if( value > maxvalue ) value = maxvalue;
      if( value < minvalue ) value = minvalue;
      signal(channel, sample) = (short)value;
    }
  }
  mCount=mCount+signal.Elements();
}
void
EDFFileWriterBase::PutBlock( const GenericSignal& inSignal, const StateVector& inStatevector )
{
  for( int i = 0; i < inSignal.Channels(); ++i )
    for( int j = 0; j < inSignal.Elements(); ++j )
      GDF::Num<T>( inSignal( i, j ) ).WriteToStream( OutputStream() );
  for( size_t i = 0; i < mStateNames.size(); ++i )
    GDF::PutField< GDF::Num<GDF::int16> >(
      OutputStream(),
      inStatevector.StateValue( mStateNames[ i ] )
    );
}
// **************************************************************************
// Function:   Process
// Purpose:    This function is called within the data acquisition loop
//             it fills its output signal with values
//             and does not return until all data has been acquired.
// Parameters: References to input signal (ignored) and output signal.
// Returns:    N/A
// **************************************************************************
void NicoletOneADC::Process( const GenericSignal&, GenericSignal& Output )
{
  // Grab the data for each channel and sample, push it into the output
  bool newData = false;
  while( !newData )
  {
    // Grab new information from thread
    bool newTsInfo = false;
    newData = !( mNT->ExtractData( Output.Channels(), Output.Elements(), mDataBlock, newTsInfo ) );

	// If we have a new data block, we can fill out sample block
    if( newData )
      for( int sample = 0; sample < Output.Elements(); ++sample )
	    for( int channel = 0; channel < Output.Channels(); ++channel )		
		  Output( channel, sample ) = mDataBlock[channel][sample];

	// If we have new TsInfo we need to see if its still valid.
    if( newTsInfo )
    {
	  // We need to check the signal properties to verify that all is well.
	  // Check the number of channels
	  int numChannels = 0;
	  while( mNT->GetNumChannels( &numChannels ) )
		Sleep( 10 );
	  if( mChannels != numChannels )
		bcierr << "The number of channels reported has changed.  Experiment is ending. Device is reporting " << numChannels << " channels" << endl;

	  // Check Sampling Rate
	  double rate = 0.0f;
	  while( mNT->GetSampleRate( &rate ) )
		  Sleep( 10 );
	  if( mSamplingRate != (int)rate )
		bcierr << "Sampling Rate has changed.  Experiment is ending.  Device is reporting " << (int)rate << " Hz." << endl;
    }

    // Needed?
    Sleep( 10 );
  }
}
void
SpatialFilter::Process( const GenericSignal& Input, GenericSignal& Output )
{
  // Actually perform Spatial Filtering on the input and write it into the output signal.
  for( int sample = 0; sample < Input.Elements(); ++sample )
    for( int outChannel = 0; outChannel < Output.Channels(); ++outChannel )
    {
      double value = 0;
      for( int inChannel = 0; inChannel < Input.Channels(); ++inChannel )
        value += mFilterMatrix[ outChannel ][ inChannel ] * Input( inChannel, sample );
      Output( outChannel, sample ) = value;
    }
}
void
RDAClientADC::DoAcquire( GenericSignal& Output )
{
  if( !mConnection.ReceiveData( Output ) )
    Error( "Lost connection to VisionRecorder software" );
  else if( mAddMarkerChannel )
  {
    int from = Output.Channels() - 2,
        to = from + 1;
    for( int el = 0; el < Output.Elements(); ++el )
      Output( to, el ) = Output( from, el );
  }
}
void
BrainVisionGDRConverter::OutputSignal( const GenericSignal& inSignal, long /*inSamplePos*/ )
{
  Idle();
  for( int sample = 0; sample < inSignal.Elements(); ++sample )
    for( int channel = 0; channel < inSignal.Channels(); ++channel )
    {
      float value = inSignal( channel, sample );
      mDataFile.write( reinterpret_cast<const char*>( &value ), sizeof( value ) );
    }

  if( !mDataFile )
    bcierr << "Error writing data file" << endl;
}
void
AlignmentFilter::Process( const GenericSignal& Input, GenericSignal& Output )
{
  if( mAlign ) // Perform the alignment on the input and write it into the output signal.
    for( int channel = 0; channel < Input.Channels(); ++channel )
      for( int sample = 0; sample < Input.Elements(); ++sample )
      {
        Output( channel, sample ) =
          Input( channel, sample ) * mWeightCur[ channel ]
            + mWeightPrev[ channel ] * mPrevSample[ channel ];
        mPrevSample[ channel ] = Input( channel, sample );
      }
  else // No alignment.
    Output = Input;
}
Exemple #17
0
void
RDAClientADC::Process( const GenericSignal&, GenericSignal& Output )
{
  for( int sample = 0; sample < Output.Elements(); ++sample )
    for( int channel = 0; channel < Output.Channels(); ++channel )
    {
      if( !mInputQueue )
      {
        bcierr << "Lost connection to VisionRecorder software" << endl;
        return;
      }
      Output( channel, sample ) = mInputQueue.front();
      mInputQueue.pop();
    }
}
void
BAlertADC::Process( const GenericSignal& Input, GenericSignal& Output )
{
	float fBuffer[768];
	if(BAlertWaitForData(fBuffer,mSampleBlockSize))
	{	
		int i=0;
		for( int el = 0; el < Output.Elements(); el++ )
			for( int ch = 0; ch < Output.Channels(); ch++ )
				Output( ch, el ) = fBuffer[ i++ ];
	}
	else 
	{
		bcierr<< "ABM: Data acquisition failed..." << endl;
	}

}
void
ASCIIConverter::OutputSignal( const GenericSignal& inSignal, long long /*inSamplePos*/ )
{
    Idle();
    for( int sample = 0; sample < inSignal.Elements(); ++sample )
    {
        for( int channel = 0; channel < inSignal.Channels(); ++channel )
        {
            double value = inSignal( channel, sample );
            mDataFile << ' ' << value;
        }
        for( size_t state = 0; state < mStateValues.size(); ++state )
            mDataFile << ' ' << mStateValues[ state ];

        mDataFile << '\n';
    }
    if( !mDataFile )
        bcierr << "Error writing data file" << endl;
}
void
ConnectorInput::Process( const GenericSignal& Input, GenericSignal& Output )
{
    try
    {
        Output = Input;
        string buffer;
        while( mConnection && mConnection.rdbuf()->in_avail() > 0 )
            buffer += mConnection.get();
        istringstream iss( buffer );
        string name;
        while( iss >> name )
        {
            double value;
            string remainder;
            if( !std::getline( iss >> value, remainder ) || !remainder.empty() )
                throw bciexception( "Malformed input, expected state name, followed by a single number as a value,"
                                    "space-separated, and terminated with a newline character" );
            bool match = false;
            for( vector<string>::const_iterator i = mInputFilters.begin(); i != mInputFilters.end() && !match; ++i )
                match = match || WildcardMatch( *i, name, false );
            if( match )
            {
                if( name.find( "Signal(" ) == 0 )
                {
                    istringstream iss( name.substr( name.find( '(' ) ) );
                    char ignore;
                    int channel = 0,
                        element = 0;
                    if( !( iss >> ignore >> channel >> ignore >> element >> ignore ) )
                        throw bciexception( "Incorrect Signal index syntax: " << name );
                    if( channel >= Input.Channels() || element >= Input.Elements() )
                        throw bciexception( "Received signal index out-of-bounds: " << name );
                    Output( channel, element ) = value;
                }
                else
                {
                    if( !States->Exists( name ) )
                        throw bciexception( "Ignoring value for non-existent " << name << " state" );
                    State( name.c_str() ) = static_cast<int>( value );
                }
            }
        }
void
ComplexDemodulator::Process( const GenericSignal& Input, GenericSignal& Output )
{
  for( int channel = 0; channel < Input.Channels(); ++channel )
  {
    for( int sample = 0; sample < Input.Elements(); ++sample )
    {
      mSignalBuffer[ channel ].push_back( Input( channel, sample ) );
      mSignalBuffer[ channel ].erase( mSignalBuffer[ channel ].begin() );
    }
    for( size_t band = 0; band < mCoefficients.size(); ++band )
    {
      Output( channel, band )
        = norm( inner_product(
            mSignalBuffer[ channel ].begin(),
            mSignalBuffer[ channel ].end(),
            mCoefficients[ band ].begin(),
            complex<double>( 0.0, 0.0 )
          ) );
    }
  }
}
void
AverageDisplay::Process( const GenericSignal& Input, GenericSignal& Output )
{
  size_t targetCode = State( "TargetCode" );
  if( targetCode == 0 && targetCode != mLastTargetCode )
  {
    size_t targetIndex = find( mTargetCodes.begin(), mTargetCodes.end(), mLastTargetCode ) - mTargetCodes.begin();
    if( targetIndex == mTargetCodes.size() )
      mTargetCodes.push_back( mLastTargetCode );
    // End of the current target code run.
    for( size_t i = 0; i < mChannelIndices.size(); ++i )
    {
      for( int power = 0; power <= maxPower; ++power )
      {
        // - If the target code occurred for the first time, adapt the power sums.
        if( mPowerSums[ power ][ i ].size() <= targetIndex )
          mPowerSums[ power ][ i ].resize( targetIndex + 1 );
        // - Update power sum sizes.
        if( mPowerSums[ power ][ i ][ targetIndex ].size() < mSignalOfCurrentRun[ i ].size() )
          mPowerSums[ power ][ i ][ targetIndex ].resize( mSignalOfCurrentRun[ i ].size(), 0 );
      }
#ifdef SET_BASELINE
      if( mBaselineSamples[ i ] > 0 )
        mBaselines[ i ] /= mBaselineSamples[ i ];
#endif // SET_BASELINE
      // - Compute the power sum entries.
      for( size_t j = 0; j < mSignalOfCurrentRun[ i ].size(); ++j )
      {
#ifdef SET_BASELINE
        mSignalOfCurrentRun[ i ][ j ] -= mBaselines[ i ];
#endif // SET_BASELINE
        float summand = 1.0;
        for( size_t power = 0; power < maxPower; ++power )
        {
          mPowerSums[ power ][ i ][ targetIndex ][ j ] += summand;
          summand *= mSignalOfCurrentRun[ i ][ j ];
        }
        mPowerSums[ maxPower ][ i ][ targetIndex ][ j ] += summand;
      }
    }
    // - Clear target run buffer.
    for( size_t i = 0; i < mSignalOfCurrentRun.size(); ++i )
      mSignalOfCurrentRun[ i ].clear();
#ifdef SET_BASELINE
    for( size_t i = 0; i < mBaselines.size(); ++i )
    {
      mBaselineSamples[ i ] = 0;
      mBaselines[ i ] = 0;
    }
#endif // SET_BASELINE
    // - Compute and display the averages.
    for( size_t channel = 0; channel < mVisualizations.size(); ++channel )
    {
      size_t numTargets = mPowerSums[ maxPower ][ channel ].size(),
             numSamples = numeric_limits<size_t>::max();
      for( size_t target = 0; target < numTargets; ++target )
        if( mPowerSums[ maxPower ][ channel ][ target ].size() < numSamples )
          numSamples = mPowerSums[ maxPower ][ channel ][ target ].size();

      // To minimize user confusion, always send target averages in ascending order
      // of target codes. This ensures that colors in the display don't depend
      // on the order of target codes in the task sequence once all target codes
      // occurred.
      // We cannot, however, avoid color changes when yet unknown target codes
      // occur.
      //
      // The map is automatically sorted by its "key", so all we need to do
      // is to put the target codes and their indices into it, using the
      // target code as "key" and the index as "value", and later iterate over
      // the map to get the indices sorted by their associated target code.
      map<int, int> targetCodesToIndex;
      for( size_t target = 0; target < numTargets; ++target )
        targetCodesToIndex[ mTargetCodes[ target ] ] = target;

      GenericSignal average( numTargets, numSamples );
      LabelList     labels;
      for( map<int, int>::const_iterator target = targetCodesToIndex.begin();
                                   target != targetCodesToIndex.end(); ++target )
      {
        for( size_t sample = 0; sample < numSamples; ++sample )
          // If everything behaves as we believe it will,
          // a division by zero is impossible.
          // If it occurs nevertheless, the logic is messed up.
          average( target->second, sample ) =
            mPowerSums[ 1 ][ channel ][ target->second ][ sample ] /
                          mPowerSums[ 0 ][ channel ][ target->second ][ sample ];
        ostringstream oss;
        oss << "Target " << target->first;
        string targetName = OptionalParameter( "TargetNames", target->first );
        if( targetName != "" )
          oss << " (" << targetName << ")";
        labels.push_back( Label( target->second, oss.str() ) );
      }
      mVisualizations[ channel ].Send( CfgID::ChannelLabels, labels );
      ostringstream oss;
      oss << ( Parameter( "SampleBlockSize" )  / Input.Elements() / Parameter( "SamplingRate" ) ) << "s";
      mVisualizations[ channel ].Send( CfgID::SampleUnit, oss.str() );
      mVisualizations[ channel ].Send( average );
    }
  }
  if( targetCode != 0 )
  {
    // Store the current signal to the end of the run buffer.
    for( size_t i = 0; i < mChannelIndices.size(); ++i )
    {
      size_t signalCursorPos = mSignalOfCurrentRun[ i ].size();
      mSignalOfCurrentRun[ i ].resize( signalCursorPos + Input.Elements() );
      for( int j = 0; j < Input.Elements(); ++j )
        mSignalOfCurrentRun[ i ][ signalCursorPos + j ] = Input( mChannelIndices[ i ], j );
    }
#ifdef SET_BASELINE
    if( OptionalState( "BaselineInterval" ) || OptionalState( "Baseline" ) )
    {
      for( size_t i = 0; i < mChannelIndices.size(); ++i )
      {
        mBaselineSamples[ i ] += Input.Elements();
        for( int j = 0; j < Input.Elements(); ++j )
          mBaselines[ i ] += Input( mChannelIndices[ i ], j );
      }
    }
#endif // SET_BASELINE
  }
  mLastTargetCode = targetCode;
  Output = Input;
}
Exemple #23
0
void
ARFilter::Process( const GenericSignal& Input, GenericSignal& Output )
{
  for( int channel = 0; channel < Input.Channels(); ++channel )
  {
    DataVector& buf = mBuffer[ channel ];
    // Shift buffer contents left:
    size_t i = 0, j = Input.Elements();
    while( j < buf.size() )
      buf[ i++ ] = buf[ j++ ];
    // Fill the rightmost part of the buffer with new input:
    j = Input.Elements() - ( buf.size() - i );
    while( i < buf.size() )
      buf[ i++ ] = Input( channel, j++ );

    const DataVector* inputData = &buf;
    switch( mDetrend )
    {
      case none:
        break;

      case mean:
        inputData = &Detrend::MeanDetrend( buf );
        break;

      case linear:
        inputData = &Detrend::LinearDetrend( buf );
        break;

      default:
        bcierr << "Unknown detrend option" << endl;
    }

    const Ratpoly<Complex>&
      transferFunction = mMEMPredictor.TransferFunction( *inputData );

    switch( mOutputType )
    {
      case SpectralAmplitude:
      {
        const std::valarray<float>& spectrum = mTransferSpectrum.Evaluate( transferFunction );
        for( size_t bin = 0; bin < spectrum.size(); ++bin )
          Output( channel, bin ) = sqrt( spectrum[ bin ] );
      } break;

      case SpectralPower:
      {
        const std::valarray<float>& spectrum = mTransferSpectrum.Evaluate( transferFunction );
        for( size_t bin = 0; bin < spectrum.size(); ++bin )
          Output( channel, bin ) = spectrum[ bin ];
      } break;

      case ARCoefficients:
      {
        const Polynomial<Complex>::Vector& coeff = transferFunction.Denominator().Coefficients();
        for( size_t i = 1; i < coeff.size(); ++i )
          Output( channel, i - 1 ) = coeff[ i ].real();
      } break;

      default:
        bcierr << "Unknown output type" << endl;
    }
  }
}
void
P3TemporalFilter::Process( const GenericSignal& Input, GenericSignal& Output )
{
    Output = GenericSignal( mOutputProperties );

    if( mEpochsToAverage > 0 )
    {
        int curStimulusCode = State( "StimulusCode" );
        mStimulusTypes[ curStimulusCode ] = State( "StimulusType" );
        // If the StimulusBegin state is available, use it to detect stimulus onset.
        // Otherwise, check whether StimulusCode has just switched to nonzero.
        bool stimulusOnset = curStimulusCode > 0 &&
                             ( OptionalState( "StimulusBegin", 0 ) || mPreviousStimulusCode == 0 );
        if( stimulusOnset )
        {   // First block of stimulus presentation -- create a new epoch buffer.
            bcidbg( 3 ) << "New epoch for stimulus code #" << curStimulusCode << endl;
            mEpochs[ curStimulusCode ].insert( new EpochBuffer( mOutputProperties ) );
        }
        mPreviousStimulusCode = curStimulusCode;

        State( "StimulusCodeRes" ) = 0;
        State( "StimulusTypeRes" ) = 0;
        for( EpochMap::iterator i = mEpochs.begin(); i != mEpochs.end(); ++i )
        {
            int stimulusCode = i->first;
            EpochSet obsoleteEpochs;
            for( EpochSet::iterator j = i->second.begin(); j != i->second.end(); ++j )
            {
                ( *j )->Process( Input );
                if( ( *j )->EpochDone() )
                {   // Move buffer data into the epoch sum associated with the stimulus code,
                    // and note the epoch buffer for later disposal.
                    bcidbg( 3 ) << "Epoch done for stimulus code #" << stimulusCode << endl;
                    if( mEpochSums[ stimulusCode ] == NULL )
                    {
                        bcidbg( 2 ) << "Allocating result buffer for stimulus code #" << stimulusCode << endl;
                        mEpochSums[ stimulusCode ] = new DataSum( mOutputProperties );
                    }
                    mEpochSums[ stimulusCode ]->Add( ( *j )->Data() );
                    obsoleteEpochs.insert( *j );

                    if( mEpochSums[ stimulusCode ]->Count() == mEpochsToAverage )
                    {   // When the number of required epochs is reached, copy the buffer average
                        // into the output signal, and set states appropriately.
                        bcidbg( 2 ) << "Reporting average for stimulus code #" << i->first
                                    << endl;
                        for( int channel = 0; channel < Output.Channels(); ++channel )
                            for( int sample = 0; sample < Output.Elements(); ++sample )
                                Output( channel, sample ) = ( *mEpochSums[ stimulusCode ] )( channel, sample ) / mEpochsToAverage;
                        State( "StimulusCodeRes" ) = stimulusCode;
                        State( "StimulusTypeRes" ) = mStimulusTypes[ stimulusCode ];
                        *mEpochSums[ stimulusCode ] = DataSum( mOutputProperties );

                        if( mVisualize && i->first - 1 < mVisSignal.Channels() )
                        {
                            for( int sample = 0; sample < Output.Elements(); ++sample )
                                mVisSignal( stimulusCode - 1, sample ) = Output( mTargetERPChannel, sample );
                            mVis.Send( mVisSignal );
                        }
                    }
                }
            }
            for( EpochSet::iterator j = obsoleteEpochs.begin(); j != obsoleteEpochs.end(); ++j )
                i->second.erase( *j );
            // Epochs will be deallocated from the obsoleteEpochs class destructor.
        }
    }
}
Exemple #25
0
void
FFTFilter::Process( const GenericSignal& inputSignal, GenericSignal& outputSignal )
{
  for( size_t i = 0; i < mFFTInputChannels.size(); ++i )
  {
    // Copy the input signal values to the value buffer.
    vector<float>& buffer = mValueBuffers[ i ];
    int inputSize = inputSignal.Elements(),
        bufferSize = buffer.size();
    // Move old values towards the beginning of the buffer, if any.
    for( int j = 0; j < bufferSize - inputSize; ++j )
      buffer[ j ] = buffer[ j + inputSize ];
    // Copy new values to the end of the buffer;
    // the buffer size may be greater of smaller than the input size.
    for( int j = ::max( 0, bufferSize - inputSize ); j < bufferSize; ++j )
      buffer[ j ] = inputSignal( mFFTInputChannels[ i ], j + inputSize - bufferSize );
    // Prepare the buffer.
    if( mFFTWindow == eNone )
      for( int j = 0; j < bufferSize; ++j )
        mFFT.Input( j ) = buffer[ j ];
    else
      for( int j = 0; j < bufferSize; ++j )
        mFFT.Input( j ) = buffer[ j ] * mWindow[ j ];
    // Compute the power spectrum and visualize it if requested.
    mFFT.Transform();
    GenericSignal& spectrum = mSpectra[ i ];
    if( mVisualizeFFT || mFFTOutputSignal == ePower )
    {
      float normFactor = 1.0 / bufferSize;
      spectrum( 0, 0 ) = mFFT.Output( 0 ) * mFFT.Output( 0 ) * normFactor;
      for( int k = 1; k < ( bufferSize + 1 ) / 2; ++k )
        spectrum( k, 0 ) = (
          mFFT.Output( k ) * mFFT.Output( k ) +
          mFFT.Output( bufferSize - k ) * mFFT.Output( bufferSize - k ) ) * normFactor;
      if( bufferSize % 2 == 0 )
        spectrum( bufferSize / 2, 0 ) = mFFT.Output( bufferSize / 2 ) * mFFT.Output( bufferSize / 2 ) * normFactor;
    }
    else if( mFFTOutputSignal == eHalfcomplex )
    {
      float normFactor = 1.0 / ::sqrt( 1.0 * bufferSize );
      for( int k = 0; k < bufferSize; ++k )
        spectrum( k, 0 ) = mFFT.Output( k ) * normFactor;
    }
    if( mVisualizeFFT )
      mVisualizations[ i ].Send( spectrum );
  }
  switch( mFFTOutputSignal )
  {
    case eInput:
      outputSignal = inputSignal;
      break;
    case ePower:
    case eHalfcomplex:
      for( size_t channel = 0; channel < mSpectra.size(); ++channel )
        for( int element = 0; element < mSpectra[ channel ].Channels(); ++element )
          outputSignal( channel, element ) = mSpectra[ channel ]( element, 0 );
      break;
    default:
      assert( false );
  }
}
const GenericSignal&
BCI2000Viewer::ConstructDisplaySignal( long inPos, long inLength )
{
  static GenericSignal result;
  if( mFile.IsOpen() )
  {
    QApplication::setOverrideCursor( Qt::WaitCursor );
    int i = 1;
    vector<StateRef> states;
    for( ; i < ui->channelList->count() && ( ui->channelList->item( i )->flags() & Qt::ItemIsUserCheckable ); ++i )
      if( ui->channelList->item( i )->checkState() == Qt::Checked )
        states.push_back( mFile.State( ui->channelList->item( i )->text().toLocal8Bit().constData() ) );
    vector<int> channels;
    int base = ++i;
    for( ; i < ui->channelList->count() && ( ui->channelList->item( i )->flags() & Qt::ItemIsUserCheckable ); ++i )
      if( ui->channelList->item( i )->checkState() == Qt::Checked )
        channels.push_back( i - base );

    GenericSignal signal( channels.size() + states.size(), inLength ),
                  statevalues( states.size(), inLength );
    long sampleInFile = inPos;
    for( long sample = 0;
         sample < signal.Elements() && sampleInFile < mFile.NumSamples();
         ++sample, ++sampleInFile )
    {
      for( int channelIdx = 0; channelIdx < static_cast<int>( channels.size() ); ++channelIdx )
        signal( channelIdx, sample ) = mFile.CalibratedValue( channels[ channelIdx ], sampleInFile );

      mFile.ReadStateVector( sampleInFile );
      for( size_t i = 0; i < states.size(); ++i )
        statevalues( i, sample ) = states[i];
    }

    if( FilterActive() )
    {
      mFilter.Reset();
      result = GenericSignal( signal.Properties() );
      // run the filter twice to avoid transient artifacts
      mFilter.Process( signal, result );
      mFilter.Process( signal, result );
    }
    else
    {
      result = signal;
    }

    if( mRemoveMean )
    {
      for( size_t ch = 0; ch < channels.size(); ++ch )
      {
        double mean = 0.0;
        for( int sample = 0; sample < result.Elements(); ++sample )
          mean += result( ch, sample );
        mean /= result.Elements();
        for( int sample = 0; sample < result.Elements(); ++sample )
          result( ch, sample ) -= mean;
      }
    }

    for( int ch = 0; ch < statevalues.Channels(); ++ch )
      for( int sample = 0; sample < statevalues.Elements(); ++sample )
        result( ch + channels.size(), sample ) = statevalues( ch, sample );

    QApplication::restoreOverrideCursor();
  }
  else
  {
    result = GenericSignal( 0, 0 );
  }
  return result;
}