Created
February 2, 2016 15:27
-
-
Save anonymous/c4ef841fde56d6069703 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Author & Date: Tom Gradel 4 April 2015 | |
// Purpose: Control real-time cell array data acquisition methods | |
void OnSnapShot( | |
int nlhs, // Number of left hand side (output) arguments | |
mxArray *plhs[ ], // Array of left hand side arguments | |
int nrhs, // Number of right hand side (input) arguments | |
const mxArray *prhs[ ] )// Array of right hand side arguments | |
{ | |
UINT32 nInstance = 0; | |
cbSdkResult res = CBSDKRESULT_SUCCESS; | |
if ( nrhs < 2) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Too few inputs provided\n" ); | |
char cmdstr[ 128 ]; | |
if ( mxGetString( prhs[ 1 ], cmdstr, 16 ) ) | |
{ | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "invalid command name\n" ); | |
} | |
enum | |
{ | |
SNAPSHOT_GET, | |
SNAPSHOT_SAVE, | |
SNAPSHOT_UNKNOWN, | |
} command = SNAPSHOT_UNKNOWN; | |
if ( 0 == _strnicmp( cmdstr, "get", ARRAYSIZE( cmdstr ) ) ) | |
command = SNAPSHOT_GET; | |
else if ( 0 == _strnicmp( cmdstr, "save", ARRAYSIZE( cmdstr ) ) ) | |
command = SNAPSHOT_SAVE; | |
if ( command == SNAPSHOT_UNKNOWN ) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Unknown snapshot parameter\n" ); | |
switch (command) | |
{ | |
case SNAPSHOT_GET: | |
{ | |
UINT32 timestamp = 0; | |
UINT32 stopCell[ cbNUM_ANALOG_CHANS ]; | |
if ( nrhs != 3 && nrhs != 4 ) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Incorrect number of input parameters to 'snapshot:get'\n" ); | |
if ( nlhs > 3 ) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Incorrect number of output parameters to 'snapshot:get'\n" ); | |
if (nrhs == 4 ) | |
{ | |
if ( !mxIsNumeric( prhs[ 3 ] ) ) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Invalid instance number" ); | |
nInstance = ( UINT32 ) mxGetScalar( prhs[ 3 ] ); | |
} | |
UINT32 bufferLength = (UINT32) mxGetScalar( prhs[ 2 ] ); | |
res = cbSdkSnapShotGet( nInstance, ×tamp, stopCell, bufferLength); | |
if ( res != CBSDKRESULT_SUCCESS ) | |
{ | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Error getting snapshot\n" ); | |
} | |
if ( nlhs > 0 ) | |
{ | |
plhs[ 0 ] = mxCreateDoubleScalar( timestamp ); | |
if ( nlhs > 1 ) | |
{ | |
mxArray *mxa = mxCreateDoubleMatrix( cbNUM_ANALOG_CHANS, 1, mxREAL ); | |
double *pDest = mxGetPr( mxa ); | |
for ( int i = 0; i < cbNUM_ANALOG_CHANS; i++ ) | |
pDest[ i ] = stopCell[ i ]; | |
plhs[ 1 ] = mxa; | |
} | |
} | |
break; | |
} | |
case SNAPSHOT_SAVE: | |
{ | |
if ( nrhs < 4 || nrhs > 6) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Incorrect number of input parameters to 'snapshot:save'" ); | |
if ( nlhs > 0 ) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "'snapshot:save' has no output parameters" ); | |
UINT32 stopTimestamp = 0; | |
if ( nrhs > 4 ) | |
{ | |
if ( ! mxIsNumeric( prhs[4] ) ) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Invalid timestamp" ); | |
stopTimestamp = ( UINT32 ) mxGetScalar( prhs[ 4 ] ); | |
if ( nrhs == 6 ) | |
{ | |
if ( !mxIsNumeric( prhs[ 5 ] ) ) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Invalid instance number" ); | |
nInstance = ( UINT32 ) mxGetScalar( prhs[ 5 ] ); | |
} | |
} | |
if ( mxGetN( prhs[ 3 ] ) != cbNUM_ANALOG_CHANS ) | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "'snapshot:save' sample_buffer must have 144 columns" ); | |
double *pStopCell = mxGetPr( prhs[ 2 ] ); | |
UINT32 stopCell[ cbNUM_ANALOG_CHANS ]; | |
for ( int i = 0; i < cbNUM_ANALOG_CHANS; i++ ) | |
stopCell[ i ] = ( UINT32 ) pStopCell[ i ]; | |
// To support different sample frequencies, a sample_size parameter could be passed. | |
// Instead, assume sample_buffer rows size provides the proper length for all channels | |
UINT32 samples = (UINT32) mxGetM( prhs[ 3 ] ); | |
double *pSampleBuffer = mxGetPr( prhs[ 3 ] ); | |
res = cbSdkSnapShotSave( nInstance, stopCell, stopTimestamp, samples, pSampleBuffer ); | |
if ( nlhs > 0 ) | |
{ | |
plhs[ 0 ] = mxCreateDoubleScalar( ( double ) res ); | |
} else if (res != CBSDKRESULT_SUCCESS ) | |
{ | |
PrintHelp( CBMEX_FUNCTION_SNAPSHOT, true, "Snapshot save failed, likely because too much time has elapsed since snapshot get\n" ); | |
} | |
break; | |
} | |
default: | |
PrintErrorSDK( CBSDKRESULT_UNKNOWN, "OnSnapShot()" ); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define CBMEX_USAGE_SNAPSHOT \ | |
"Control continuous event data snapshots configured by 'trialconfig' with the ring_buffer option\n" \ | |
"Format:\n" \ | |
" [stop_timestamp, cell_stop_index] = cbmex('snapshot', 'get', buffer_length, [instance])\n" \ | |
" status = cbmex('snapshot', 'save', cell_stop_index, sample_buffer, [stop_timestamp,[instance]])\n" \ | |
"Inputs:\n" \ | |
" 'buffer_length': size of sample, eg 2000 for 2 seconds of data at 1K samples/second\n" \ | |
" Used to ensure write pointer doesn't overlap with read.\n" \ | |
" 'cell_stop_index': values previously returned by cbmex('snapshot', 'get')\n" \ | |
" 'stop_timestamp' : value previously returned by cbmex('snapshot', 'get') \n"\ | |
" ensures no buffer overlap\n"\ | |
" 'sample_buffer': a number-of-samples x 144 matrix of doubles, where\n" \ | |
" ' number-of-samples is copied fromm the ring buffer starting\n" \ | |
" and cell_stop_index - number-of-samples and wraps if necessary.\n" \ | |
" Elements are converted to doubles when they are copied.\n" \ | |
" 'instance' (optional), value: value is the library instance to use (default is 0)\n" \ | |
"Outputs:\n" \ | |
" Outputs for the 'get' case are:\n" \ | |
" 'stop_timestamp': NeuroPort time of the sample that starts in cell(cell_stop_index)\n" \ | |
" 'cell_stop_index': 1-based index (MATLAB-style) of the cell that contains the last available data to be read. \n" \ | |
" Since a ring buffer is used, cell_stop_index can be < cell_start_index, indicating that new\n" \ | |
" data is from cell_start_index:end and from 1:cell_stop_index.\n" \ | |
" When cell_stop_index is an input, a zero value of is ignored.\n" \ | |
" Outputs for the 'status' case are:\n" \ | |
" 'overflow': Has a value of 0 if no overflow has occurred, and 1 if an overflow has occurred" \ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Author & Date: Tom Gradel 4 March 2015 | |
// Purpose: Get real-time snapshot information | |
// Inputs: | |
// nInstance - the instance number | |
// pTimestamp - array of timestamps for the time corresponding to the start cell | |
// pStartCell - array of start cells for each channel of the current real-time dataset | |
// pStopCell - array of stop cell for each channel of the current real-time dataset | |
// Outputs: | |
// returns the error code | |
cbSdkResult cbSdkSnapShotGet( UINT32 nInstance, UINT32 *pTimestamp, UINT32 *pStopCell, UINT32 bufferLength ) | |
{ | |
if ( nInstance >= cbMAXOPEN ) | |
return CBSDKRESULT_INVALIDPARAM; | |
if ( g_app[ nInstance ] == NULL ) | |
return CBSDKRESULT_CLOSED; | |
return g_app[ nInstance ]->SdkSnapShotGet( pTimestamp, pStopCell, bufferLength); | |
} | |
// Author & Date: Isaac Pedisich 19 January 2015 | |
// Purpose: Get real-time snapshot information | |
// Inputs: | |
// pTimestamp - array of timestamps for the time corresponding to the start cell | |
// pStopCell - array of start indicies for each channel of the current real-time dataset | |
// bufferLength - moves write_start_index to stopCell-bufferLength | |
// Outputs: | |
// returns the error code | |
cbSdkResult SdkApp::SdkSnapShotGet( UINT32 *pTimestamp, UINT32 *pStopCell, UINT32 bufferLength ) | |
{ | |
if ( m_instInfo == 0 ) | |
return CBSDKRESULT_CLOSED; | |
if ( !m_bWithinTrial || m_CD == NULL ) | |
return CBSDKRESULT_CLOSED; | |
cbGetSystemClockTime( pTimestamp, m_nInstance ); | |
m_lockTrial.lock( ); | |
for ( int ch = 0; ch < cbNUM_ANALOG_CHANS; ch++ ) | |
{ | |
if ( m_CD->write_start_index[ ch ] == m_CD->write_index[ ch ] ) // No data exists | |
{ | |
pStopCell[ ch ] = 0; | |
} | |
else | |
{ | |
// Return 1-based indices to MATLAB | |
pStopCell[ ch ] = m_CD->write_index[ ch ]; // Already 1-based since is last written + 1 | |
INT32 startCell = (INT32) pStopCell[ ch ] - (INT32) bufferLength; | |
if ( startCell < 0 ) | |
startCell = (INT32) m_CD->size + startCell; | |
m_CD->write_start_index[ ch ] = startCell; | |
} | |
} | |
m_lockTrial.unlock( ); | |
return CBSDKRESULT_SUCCESS; | |
} | |
// Author & Date: Tom Gradel 4 March 2015 | |
// Purpose: Determine snapshot overflow status | |
// Inputs: | |
// nInstance - the instance number | |
// pStopCell - array of stop cells for each to release | |
// nLatencyOffset - offset into ring buffer to account for latency | |
// samples - number of data elements preceeding (and including) 'stop' that are to be kept | |
// pSampleBuffer - data buffer to hold data records from stop - samples + 1 to stop | |
// Outputs: | |
// returns the error code | |
CBSDKAPI cbSdkResult cbSdkSnapShotSave( UINT32 nInstance, UINT32 *pStopCell, UINT32 nLatencyOffset, UINT32 samples, double *pSampleBuffer ) | |
{ | |
if ( nInstance >= cbMAXOPEN ) | |
return CBSDKRESULT_INVALIDPARAM; | |
if ( g_app[ nInstance ] == NULL ) | |
return CBSDKRESULT_CLOSED; | |
return g_app[ nInstance ]->SdkSnapShotSave( pStopCell, nLatencyOffset, samples, pSampleBuffer ); | |
} | |
// Author & Date: Tom Gradel 4 March 2015 | |
// Purpose: Send an extension command | |
// Inputs: | |
// pStopCell - MATLAB 1-based set of stop cells | |
// nLatencyOffset - offset into ring buffer to account for latency | |
// samples - number of samples (N) to fill in pSampleBuffer | |
// pSampleBuffer - double matrix (Nx144) | |
// Outputs: | |
// returns the error code | |
cbSdkResult SdkApp::SdkSnapShotSave( UINT32 *pStopCell, UINT32 stopTimestamp, UINT32 samples, double *pSampleBuffer ) | |
{ | |
if ( m_instInfo == 0 ) | |
return CBSDKRESULT_CLOSED; | |
if (stopTimestamp != 0) | |
{ | |
UINT32 nowTimestamp = 0; | |
cbGetSystemClockTime( &nowTimestamp, m_nInstance ); | |
double stopTimeInSeconds = ( double ) stopTimestamp / ( double ) 30000; // clock frequency hardwired to 30000 | |
double nowTimeInSeconds = ( double ) nowTimestamp / ( double ) 30000; | |
// Check to make sure that we won't be reading from portions of the buffer that have been ovewritten | |
// Assumes first sample rate is the one we care about! | |
double maxSamples = m_CD->size - m_CD->current_sample_rates[ 0 ] * ( nowTimeInSeconds - stopTimeInSeconds ); | |
if ( samples > maxSamples ) | |
{ | |
return CBSDKRESULT_ERROVERLAP; | |
} | |
} | |
// Do not lock the ring buffer. The assumption is that data was returned to the caller previously and has not been released, | |
// So it cannot be overwritten | |
double *pBuffer = pSampleBuffer; | |
for ( int ch = 0; ch < cbNUM_ANALOG_CHANS; ch++ ) | |
{ | |
// Use 'stop' and compute backwards to find the start. Do not include data from the latency offset. | |
INT32 start = (INT32) pStopCell[ ch ] - samples; | |
if ( start < 0 ) | |
{ | |
start = m_CD->size + start; // Start is negative, so this subtracts | |
} | |
if ( start + samples - 1 < m_CD->size ) // No ring buffer wrap | |
{ | |
std::copy( &m_CD->continuous_channel_data[ ch ][ start ], &m_CD->continuous_channel_data[ ch ][ start + samples ], &pBuffer[ 0 ] ); | |
// for ( UINT32 i = 0; i < samples; i++ ) | |
// pBuffer[ i ] = ( double ) m_CD->continuous_channel_data[ ch ][ j++ ]; | |
} | |
else // Ring buffer wrap | |
{ | |
double *p = &pBuffer[ 0 ]; | |
p = std::copy( &m_CD->continuous_channel_data[ ch ][ start ], &m_CD->continuous_channel_data[ ch ][ m_CD->size ], p ); | |
UINT32 partLength2 = samples - (m_CD->size - start); | |
std::copy( &m_CD->continuous_channel_data[ ch ][ 0 ], &m_CD->continuous_channel_data[ ch ][ partLength2 ], p ); | |
} | |
pBuffer += samples; // Next column | |
} | |
return CBSDKRESULT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment