Skip to content

Instantly share code, notes, and snippets.

Created February 2, 2016 15:27
Show Gist options
  • Save anonymous/c4ef841fde56d6069703 to your computer and use it in GitHub Desktop.
Save anonymous/c4ef841fde56d6069703 to your computer and use it in GitHub Desktop.
// 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, &timestamp, 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()" );
}
}
#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" \
// 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