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(); } }
// 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 DisplayFilter::Process( const GenericSignal& Input, GenericSignal& Output ) { if( Input.Channels() != mFilter.Channels() ) mFilter.Initialize( Input.Channels() ); mFilter.Process( Input, Output ); if( mFilter.NanStalled() ) mFilter.Initialize(); }
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 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 ]; }
//////////////////////////////////////////////////////////////////////////////// // 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 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 CursorFeedbackTask::DoFeedback( const GenericSignal& ControlSignal, bool& doProgress ) { // Update cursor position float x = mpFeedbackScene->CursorXPosition(), y = mpFeedbackScene->CursorYPosition(), z = mpFeedbackScene->CursorZPosition(); if( ControlSignal.Channels() > 0 ) x += mCursorSpeedX * ControlSignal( 0, 0 ); if( ControlSignal.Channels() > 1 ) y += mCursorSpeedY * ControlSignal( 1, 0 ); if( ControlSignal.Channels() > 2 ) z += mCursorSpeedZ * ControlSignal( 2, 0 ); // Restrict cursor movement to the inside of the bounding box: float r = mpFeedbackScene->CursorRadius(); x = max( r, min( 100 - r, x ) ), y = max( r, min( 100 - r, y ) ), z = max( r, min( 100 - r, z ) ); mpFeedbackScene->SetCursorPosition( x, y, z ); const float coordToState = ( ( 1 << cCursorPosBits ) - 1 ) / 100.0; State( "CursorPosX" ) = static_cast<int>( x * coordToState ); State( "CursorPosY" ) = static_cast<int>( y * coordToState ); State( "CursorPosZ" ) = static_cast<int>( z * coordToState ); // Test for target hits if( Parameter( "TestAllTargets" ) != 0 ) { int hitTarget = 0; for( int i = 0; i < mpFeedbackScene->NumTargets(); ++i ) if( mpFeedbackScene->TargetHit( i ) ) { // In case of a positive hit test for multiple targets, take the closer one. if( hitTarget == 0 || mpFeedbackScene->CursorTargetDistance( hitTarget - 1 ) > mpFeedbackScene->CursorTargetDistance( i ) ) hitTarget = i + 1; } State( "ResultCode" ) = hitTarget; } else { if( mpFeedbackScene->TargetHit( State( "TargetCode" ) - 1 ) ) State( "ResultCode" ) = State( "TargetCode" ); } doProgress = ( ++mCurFeedbackDuration > mMaxFeedbackDuration ); doProgress = doProgress || ( State( "ResultCode" ) != 0 ); }
void TaskFilter::Process( const GenericSignal& Input, GenericSignal& Output ) { if( Input.Channels() > 0 ) { float cursorX = mWindow.CursorX() + mCursorSpeed * Input( 0, 0 ); mWindow.SetCursorX( cursorX - ::floor( cursorX ) ); } if( Input.Channels() > 1 ) { float cursorY = mWindow.CursorY() + mCursorSpeed * Input( 1, 0 ); mWindow.SetCursorY( cursorY - ::floor( cursorY ) ); } mWindow.RedrawWindow(); State( "StimulusTime" ) = PrecisionTime::Now(); Output = Input; }
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 ]; }
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 ] ) ); }
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 ); } }
// ************************************************************************** // 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 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 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 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; }
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 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 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; }
// ************************************************************************** // 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 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 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. } } }
// ************************************************************************** // Function: Process // Purpose: This function applies the Temporal routine // Parameters: input - input signal for the // output - output signal for this filter // Returns: 0 ... on error // 1 ... no error // ************************************************************************** void FIRFilter::Process(const GenericSignal& input, GenericSignal& output) { int out_channel; float value[MAXDATA]; float result[MAXDATA]; float rms= 0; float mean= 0; float max= 0; int ocount,ncount; int i,j,k; static count= 0; // static rcount= 0; // actually perform the Temporal Filtering on the input and write it into the output signal winlgth= datawindows * samples; for(i=0;i<input.Channels();i++) { for(j=datawindows-1;j>0;j--) { for(k=0;k<samples;k++) { ncount= j*samples + k; ocount= (j-1)*samples + k; datwin[i][ncount]= datwin[i][ocount]; } } count= samples; for(j=0;j<samples;j++) { count--; datwin[i][j]= input(i,j); // was count); } fir->convolve( i, winlgth, datwin[i], result ); if( integrate == 2 ) { rms= fir->rms( winlgth - (n_coef-1), result ); // sub order output( i, 0 ) = rms; } else if( integrate == 1 ) { mean= fir->mean( winlgth - (n_coef-1), result ); // sub order output( i, 0 ) = mean; } else if( integrate == 0 ) { for(j=0;j< ( winlgth - (n_coef-1) );j++) { output( i, j ) = result[j]; } } else if( integrate == 3 ) { max= fir->max( winlgth - (n_coef-1), result ); output( i, 0 ) = max; } } if( visualize ) { vis->Send(output); } }
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; } } }