CerebusOSS/CereLink

Connection to BlackRock and analog data acquisition EXAMPLE c++

Closed this issue · 6 comments

Hi,

I have a BlackRock device and I would like to connect to it using a graphical user interface I developed in ViusalStudio. Your code is extremely helpful and I would like to use it for my application.

I would like to:

  1. Establish a communication with the NSP
  2. Acquire analog input data real-time

Do you have an example that I can start from?

Thanks a lot,
Flavio

You can look at the labstreaminglayer app I made.

I have a longer explanation in a reply to #60 here.

Thanks for the reply.
Based on what I saw in the other code I developed the pieces of code listed below, but unfortunately it is not clear to me what some of the functions do.

I need the last 16 channels related to the analig input, in this case EMG signal for me.

int CBlackrock::connection_init_EMG()

        {
	cbSdkResult resTest2;
	// What these values actually do?
	UINT16 uBegChan   = emgChannelsFirstPosition; // emgChannelsFirstPosition = 128;
	UINT32 uBegMask   = 0;
	UINT32 uBegVal    = 0;
	UINT16 uEndChan   = emgChannelsFirstPosition + cbNUM_ANAIN_CHANS; // cbNUM_ANAIN_CHANS = 16
	UINT32 uEndMask   = 0;
	UINT32 uEndVal    = 0;
	bool   bDouble    = false;
	bool   bAbsolute  = false;
	UINT32 uWaveforms = 0;
	UINT32 uConts     = 0;
	UINT32 uEvents    = 0;
	UINT32 uComments  = 0;
	UINT32 uTrackings = 0;
	UINT32 bWithinTrial = false;

         // What does this function do?
	resTest2 = cbSdkGetTrialConfig(m_blkrckInstance
									, &bWithinTrial
									, &uBegChan
									, &uBegMask
									, &uBegVal
									, &uEndChan
									, &uEndMask
									, &uEndVal
									, &bDouble
									, &uWaveforms
									, &uConts
									, &uEvents
									, &uComments
									, &uTrackings);

	uConts = cbSdk_CONTINUOUS_DATA_SAMPLES;
	UINT32 bActive = 1; // 0 leave buffer intact, 1 clear the buffer

	//uBegChan after the function is  0. Why?
	//uEndChan after the function is  0. Why?
	uBegChan   = emgChannelsFirstPosition;
	uEndChan   = emgChannelsFirstPosition + m_numEmgChannels;

         // What does this function do?
	resTest2 = cbSdkSetTrialConfig(m_blkrckInstance
									, bActive
									, uBegChan
									, uBegMask
									, uBegVal
									, uEndChan
									, uEndMask
									, uEndVal
									, bDouble
									, uWaveforms
									, uConts
									, uEvents
									, uComments
									, uTrackings
									, bAbsolute); // Configure a data collection trial

	// Deactivate all the channels
	UINT32 bActive2 = 0; //activate channel 1, deactivating 0
	UINT16 dchannel = 0;
	resTest2 = cbSdkSetChannelMask(m_blkrckInstance, dchannel, bActive2);

	// Activate only needed channels (EMG)
	bActive2 = 1; //activate channel: 1, deactivating 0
	for (UINT16 ch = emgChannelsFirstPosition; ch <= emgChannelsFirstPosition + m_numEmgChannels; ch++)
	{
		resTest2 = cbSdkSetChannelMask(m_blkrckInstance, ch, bActive2);
	}

	return 0;
}

After there is the function I use for extracting analog data.

void CBlackrock::get_EMG(const size_t noChan2Use
							, const size_t* const CHANNELS2USE
							, INT16** EMGsignal
							, size_t& noSamples)
       {
// CHANNELS2USE goes from 0 to 15
// noChan2Use = 16

	cbSdkResult resTest2;

	// Get configuration
	bool bTrialDouble = false;
	resTest2 = cbSdkGetTrialConfig(m_blkrckInstance, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &bTrialDouble);
	if (resTest2 != CBSDKRESULT_SUCCESS )
		throw std::runtime_error("cbSdkGetTrialConfig {Blackrock.cpp}"); 

	 **// What does this function do? After there is _cbSdkGetTrialData_ that looks the same**
	resTest2 = cbSdkInitTrialData(m_blkrckInstance, NULL, &m_trialCont, NULL, NULL);
	if (resTest2 != CBSDKRESULT_SUCCESS )
		throw std::runtime_error("cbSdkInitTrialData {Blackrock.cpp}"); 

	// Allocate memory
	int ch = 0;

	if(bTrialDouble)
	{
		for(ch=0; ch<m_trialCont.count; ch++)
		{
			m_trialCont.samples[ch] = new double[m_trialCont.num_samples[ch]];
		}
	}
	else
	{
		for(ch=0; ch<m_trialCont.count; ch++)
		{
			m_trialCont.samples[ch] = new INT16[m_trialCont.num_samples[ch]];
		}
	}

	 **// What does this function do?**
	bool bFlushBuffer = true;
	resTest2 = cbSdkGetTrialData(m_blkrckInstance, bFlushBuffer, NULL, &m_trialCont, NULL, NULL);

	// Store in global function
	for (size_t chan = 0; chan<noChan2Use; chan++)
	{
		EMGsignal[chan] = (INT16 *)m_trialCont.samples[CHANNELS2USE[chan]];
	}
	noSamples = m_trialCont.num_samples[CHANNELS2USE[0]];
}

When I check m_trialCont, it has 144 elements. Moreover, only the first 10 goes from 128 to 138.

Can you help me understanding how does it work?

Thanks,
Flavio

Can you edit your post so the code blocks are preceded and followed by triple back-ticks? You can even decorate the first one with C++ so it will highlight the code. i.e. ```c++

But, quickly, cbSdkGetTrialConfig gets the current config into the pass-by-reference variables. Often, before you've configured your system, this function is used simply to make sure your variables have some reasonable default values. Then, AFTER you call cbSdkGetTrialConfig, you can modify some or all of the values to your desired settings, THEN call cbSdkSetTrialConfig.

Voila, I edited it as asked.

Therefore, if I set

	UINT16 uBegChan   = emgChannelsFirstPosition; // emgChannelsFirstPosition = 128;
	UINT16 uEndChan   = emgChannelsFirstPosition + cbNUM_ANAIN_CHANS; // cbNUM_ANAIN_CHANS = 16

before cbSdkSetTrialConfig, I should be able to set these channels for getting the data?

I also have implemented some questions in the edited code.

Thanks again for the help!

I think I already answered the questions in the first block. Except for any questions about the masks. To be honest, I never figured out how to use the masks. I always do my hardware settings via central then just use my custom code to get the data.

In the second block, cbSdkInitTrialData is used just to find out how many samples are waiting, per channel. After you know how many samples are waiting, you can allocate memory into m_trialCont. When your memory is allocated, you can then call cbSdkGetTrialData and it will actually put the data into the m_trialCont struct.

In your new block, you asked if you set those variables after cbSdkGetTrialConfig but before cbSdkSetTrialConfig then the values should be accepted. That is correct. However, in the block of code you provided, you are re-initializing the variables. They should be initialized only once, before cbSdkGetTrialConfig, then modified (but not re-initialized) before cbSdkSetTrialConfig. I think you would have figured this out because it should generate a compiler error.

Perfect, Thanks!!
Now I have a clearer idea!