int sdr_bladerf_file_tx(void *dev, struct complexf *samples, unsigned int count) { int status = 0; size_t n; unsigned int to_write, total_written; struct sdr_bladerf_file *sdr = (struct sdr_bladerf_file *) dev; total_written = 0; while (status == 0 && total_written< count) { to_write = uint_min(sdr->buf_len, count - total_written); complexf_to_sc16q11(samples, sdr->buf, to_write); log_verbose("Writing'ing %u samples...\n", to_write); n = fwrite(sdr->buf, 2 * sizeof(int16_t), to_write, sdr->file); if (n != to_write) { log_debug("Sample file write was truncated.\n"); status = -1; } samples += to_write; total_written += to_write; } return status; }
int sdr_bladerf_file_rx(void *dev, struct complexf *samples, unsigned int count) { int status = 0; size_t n; unsigned int to_read, total_read; struct sdr_bladerf_file *sdr = (struct sdr_bladerf_file *) dev; total_read = 0; while (status == 0 && total_read < count) { to_read = uint_min(sdr->buf_len, count - total_read); log_verbose("Reading %u samples...\n", to_read); n = fread(sdr->buf, 2 * sizeof(int16_t), to_read, sdr->file); if (n == 0) { status = SDR_FILE_EOF; } else if (n < to_read) { /* Zero out the remaining samples. We're about to hit an EOF. */ int16_t *to_zero = sdr->buf + (2 * n); memset(to_zero, 0, 2 * sizeof(int16_t) * (to_read - n)); } sc16q11_to_complexf(sdr->buf, samples, to_read); samples += to_read; total_read += to_read; } return status; }
static void * tx_task(void *args) { int status; int16_t *samples; unsigned int i; struct bladerf_metadata meta; uint64_t samples_left; struct test *t = (struct test *) args; bool stop = false; int16_t zeros[] = { 0, 0, 0, 0 }; samples = (int16_t*) malloc(2 * sizeof(samples[0]) * t->params->buf_size); if (samples == NULL) { perror("malloc"); return NULL; } memset(&meta, 0, sizeof(meta)); for (i = 0; i < (2 * t->params->buf_size); i += 2) { samples[i] = samples[i + 1] = TX_MAGNITUDE; } status = bladerf_get_timestamp(t->dev, BLADERF_MODULE_TX, &meta.timestamp); if (status != 0) { fprintf(stderr, "Failed to get current timestamp: %s\n", bladerf_strerror(status)); } meta.timestamp += 400000; for (i = 0; i < t->num_bursts && !stop; i++) { meta.flags = BLADERF_META_FLAG_TX_BURST_START; samples_left = t->bursts[i].duration; assert(samples_left <= UINT_MAX); if (i != 0) { meta.timestamp += (t->bursts[i-1].duration + t->bursts[i].gap); } while (samples_left != 0 && status == 0) { unsigned int to_send = uint_min(t->params->buf_size, (unsigned int) samples_left); status = bladerf_sync_tx(t->dev, samples, to_send, &meta, t->params->timeout_ms); if (status != 0) { fprintf(stderr, "Failed to TX @ burst %-4u with %"PRIu64 " samples left: %s\n", i + 1, samples_left, bladerf_strerror(status)); /* Stop the RX worker */ pthread_mutex_lock(&t->lock); t->stop = true; pthread_mutex_unlock(&t->lock); } meta.flags &= ~BLADERF_META_FLAG_TX_BURST_START; samples_left -= to_send; } /* Flush TX samples by ensuring we have 2 zero samples at the end * of our burst (as required by libbladeRF) */ if (status == 0) { meta.flags = BLADERF_META_FLAG_TX_BURST_END; status = bladerf_sync_tx(t->dev, zeros, 2, &meta, t->params->timeout_ms); if (status != 0) { fprintf(stderr, "Failed to flush TX: %s\n", bladerf_strerror(status)); /* Stop the RX worker */ pthread_mutex_lock(&t->lock); t->stop = true; pthread_mutex_unlock(&t->lock); } } pthread_mutex_lock(&t->lock); stop = t->stop; pthread_mutex_unlock(&t->lock); } /* Wait for samples to finish */ printf("TX: Waiting for samples to finish.\n"); fflush(stdout); status = wait_for_timestamp(t->dev, BLADERF_MODULE_TX, meta.timestamp + t->bursts[i - 1].duration, 3000); if (status != 0) { fprintf(stderr, "Failed to wait for TX to complete: %s\n", bladerf_strerror(status)); } free(samples); printf("TX: Exiting task.\n"); fflush(stdout); return NULL; }
/* FIXME Clean up this function up. It's very unreadable. * Suggestions include moving to switch() and possibly adding some * intermediary states. (More smaller, simpler states); */ static void *rx_task(void *arg) { int lib_ret, write_ret; enum rxtx_state state, prev_state; struct cli_state *s = (struct cli_state *) arg; struct rx_cfg *rx = &s->rxtx_data->rx; unsigned int n_samples_left = 0; bool inf = false; size_t to_write = 0; /* We expect to be in the IDLE state when this task is kicked off */ state = prev_state = get_state(&rx->common); assert(state == RXTX_STATE_IDLE || state == RXTX_STATE_SHUTDOWN); while (state != RXTX_STATE_SHUTDOWN) { if (state == RXTX_STATE_RUNNING) { /* Transitioning from IDLE/ERROR -> RUNNING */ if (prev_state == RXTX_STATE_IDLE || prev_state == RXTX_STATE_ERROR) { pthread_mutex_lock(&rx->common.param_lock); n_samples_left = rx->n_samples; pthread_mutex_unlock(&rx->common.param_lock); inf = n_samples_left == 0; /* Task owns file while running */ pthread_mutex_lock(&rx->common.file_lock); /* Flush out any old samples before recording any data */ /*lib_ret = bladerf_read_c16(s->dev, rx->common.buff, rx->common.buff_size / 2);*/ lib_ret = bladerf_rx( s->dev, BLADERF_FORMAT_SC16_Q12, rx->common.buff, rx->common.buff_size/2, NULL ); if (lib_ret < 0) { set_last_error(&rx->common.error, ETYPE_BLADERF, lib_ret); state = RXTX_STATE_ERROR; } } else { /*lib_ret = bladerf_read_c16(s->dev, rx->common.buff, rx->common.buff_size / 2);*/ lib_ret = bladerf_rx( s->dev, BLADERF_FORMAT_SC16_Q12, rx->common.buff, rx->common.buff_size/2, NULL ); if (lib_ret < 0) { set_last_error(&rx->common.error, ETYPE_BLADERF, lib_ret); state = RXTX_STATE_ERROR; } else { /* Bug catcher */ assert((size_t)lib_ret <= (rx->common.buff_size / 2)); if (!inf) { to_write = uint_min(n_samples_left, lib_ret); } else { to_write = rx->common.buff_size / 2; } write_ret = rx->write_samples(s, to_write); if (write_ret < 0) { /* write_samples will have set the last error info */ state = RXTX_STATE_ERROR; } else if (!inf) { n_samples_left -= to_write; if (n_samples_left == 0) { state = RXTX_STATE_IDLE; } } } } } if (state != RXTX_STATE_RUNNING && prev_state == RXTX_STATE_RUNNING) { /* Clean up as we transition from RUNNING -> <any other state> */ lib_ret = bladerf_enable_module(s->dev, BLADERF_MODULE_RX, false); close_samples_file(&rx->common, false); pthread_mutex_unlock(&rx->common.file_lock); if (lib_ret < 0) { set_last_error(&rx->common.error, ETYPE_BLADERF, lib_ret); state = RXTX_STATE_ERROR; } set_state(&rx->common, state, false); } prev_state = state; /* Wait here while if we're in the IDLE/ERROR states */ pthread_mutex_lock(&rx->common.task_lock); while (state == RXTX_STATE_IDLE || state == RXTX_STATE_ERROR) { pthread_cond_wait(&rx->common.task_state_changed, &rx->common.task_lock); state = rx->common.task_state; } state = rx->common.task_state; pthread_mutex_unlock(&rx->common.task_lock); } return NULL; }
int main() { if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK ) < 0 ) { SYS_BREAK( "SDL_Init failed!\n" ); } SDL_SetVideoMode( ScreenWidth, ScreenHeight, 0u, SDL_OPENGL /*| SDL_FULLSCREEN */ ); #ifndef FONT_EDITOR SDL_ShowCursor( SDL_DISABLE ); #endif SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); SDL_AudioSpec audioSpec; audioSpec.freq = SoundSampleRate; audioSpec.format = AUDIO_S16SYS; audioSpec.channels = SoundChannelCount; audioSpec.silence = 0; audioSpec.samples = SoundBufferSampleCount; audioSpec.size = 0; audioSpec.callback = soundCallback; audioSpec.userdata = s_soundBuffer; SDL_OpenAudio( &audioSpec, NULL ); game_init(); SDL_PauseAudio( 0 ); uint32 lastTime = SDL_GetTicks(); uint32 buttonMask = 0u; uint32 joystickButtonMask = 0u; #ifndef SYS_BUILD_MASTER uint32 lastTimingTime = lastTime; uint32 timingFrameCount = 0u; #endif SDL_JoystickEventState( SDL_ENABLE ); SDL_Joystick* pJoystick0 = SDL_JoystickOpen( 0 ); SYS_USE_ARGUMENT(pJoystick0); int16 joystickAxis0 = 0; int16 joystickAxis1 = 0; float2 mousePos; float2_set(&mousePos, 0.0f, 0.0f); int leftMouseDown=0; int leftMousePressed=0; int rightMouseDown=0; int rightMousePressed=0u; int quit = 0; do { uint32 currentTime = SDL_GetTicks(); const float timeStep = ( float )( currentTime - lastTime ) / 1000.0f; lastTime = currentTime; #ifndef SYS_BUILD_MASTER if( currentTime - lastTimingTime > 500u ) { const float timingTimeSpan = ( float )( currentTime - lastTimingTime ) / 1000.0f; const float currentFps = ( float )( timingFrameCount ) / timingTimeSpan; char windowTitle[ 100u ]; sprintf( windowTitle, "fps=%f cb=%i", currentFps, s_callbackCount ); SDL_WM_SetCaption( windowTitle, 0 ); lastTimingTime = currentTime; timingFrameCount = 0u; } #endif // get local user input: SDL_Event event; while( SDL_PollEvent( &event ) ) { switch( event.type ) { case SDL_JOYAXISMOTION: if( event.jaxis.axis == 0u ) { joystickAxis0 = event.jaxis.value; } else if( event.jaxis.axis == 1u ) { joystickAxis1 = event.jaxis.value; } break; case SDL_JOYBUTTONDOWN: if( event.jbutton.button == 0 ) { updateButtonMask( &joystickButtonMask, ButtonMask_PlaceBomb, 1 ); } break; case SDL_JOYBUTTONUP: if( event.jbutton.button == 0 ) { updateButtonMask( &joystickButtonMask, ButtonMask_PlaceBomb, 0 ); } break; case SDL_MOUSEMOTION: float2_set(&mousePos, (float)event.motion.x, (float)event.motion.y); break; case SDL_MOUSEBUTTONDOWN: SYS_TRACE_DEBUG("down=%i\n", event.button.button); if( event.button.button == 1 ) { leftMouseDown = 1; leftMousePressed = 1; } else if( event.button.button == 3) { rightMouseDown = 1; rightMousePressed = 1; } break; case SDL_MOUSEBUTTONUP: if(event.button.button == 1) { leftMouseDown = 0; } else if(event.button.button == 3) { rightMouseDown = 0; } break; case SDL_KEYDOWN: case SDL_KEYUP: { const int ctrlPressed = ( event.key.keysym.mod & KMOD_CTRL ) != 0; switch( event.key.keysym.sym ) { case SDLK_ESCAPE: quit = 1; break; case SDLK_LEFT: updateButtonMask( &buttonMask, ctrlPressed ? ButtonMask_CtrlLeft : ButtonMask_Left, event.type == SDL_KEYDOWN ); break; case SDLK_RIGHT: updateButtonMask( &buttonMask, ctrlPressed ? ButtonMask_CtrlRight : ButtonMask_Right, event.type == SDL_KEYDOWN ); break; case SDLK_UP: updateButtonMask( &buttonMask, ctrlPressed ? ButtonMask_CtrlUp : ButtonMask_Up, event.type == SDL_KEYDOWN ); break; case SDLK_DOWN: updateButtonMask( &buttonMask, ctrlPressed ? ButtonMask_CtrlDown : ButtonMask_Down, event.type == SDL_KEYDOWN ); break; case SDLK_SPACE: updateButtonMask( &buttonMask, ButtonMask_PlaceBomb, event.type == SDL_KEYDOWN ); break; case SDLK_c: updateButtonMask( &buttonMask, ButtonMask_Client, event.type == SDL_KEYDOWN ); break; case SDLK_s: updateButtonMask( &buttonMask, ButtonMask_Server, event.type == SDL_KEYDOWN ); break; case SDLK_l: updateButtonMask( &buttonMask, ButtonMask_Leave, event.type == SDL_KEYDOWN ); break; default: break; } } break; case SDL_QUIT: quit = 1; break; } } updateButtonMaskFromJoystick( &joystickButtonMask, ButtonMask_Right, joystickAxis0, 16000 ); updateButtonMaskFromJoystick( &joystickButtonMask, ButtonMask_Left, -joystickAxis0, 16000 ); updateButtonMaskFromJoystick( &joystickButtonMask, ButtonMask_Down, joystickAxis1, 3200 ); updateButtonMaskFromJoystick( &joystickButtonMask, ButtonMask_Up, -joystickAxis1, 3200 ); #ifdef FONT_EDITOR static uint currentChar = '0'; static uint dataChar = '\0'; static float2 charPoints[ 128u ]; static uint charPointCount = 0u; static uint32 lastButtonMask = 0u; static int currentPointIndex = -1; static float2 oldMousePagePos; const uint32 buttonPressMask = buttonMask & ~lastButtonMask; lastButtonMask = buttonMask; float2 mousePagePos; mousePagePos.x=64.0f*mousePos.x/(float)ScreenWidth; mousePagePos.y=36.0f+-36.0f*mousePos.y/(float)ScreenHeight; if( buttonPressMask & ButtonMask_Right ) { currentChar=font_getNextGlyphCharCode(currentChar,1); } if( buttonPressMask & ButtonMask_Left ) { currentChar=font_getNextGlyphCharCode(currentChar,-1); } if( currentChar != dataChar ) { if( dataChar != '\0' ) { char filename[128u]; sprintf(filename,"./source/font/char_%i.h",dataChar); SYS_TRACE_DEBUG("writing char to file '%s'\n", filename); FILE* pFile=fopen(filename, "w"); if(pFile) { fprintf(pFile,"static const float2 s_points_%i[] =\n{\n",dataChar); for( uint i=0u;i<charPointCount;++i) { fprintf(pFile,"\t{%ff,%ff},\n",charPoints[i].x,charPoints[i].y); } fprintf(pFile,"};\n\n"); fclose(pFile); } else { SYS_TRACE_ERROR("Could not open file '%s'\n", filename); } } const FontGlyph* pGlyph=font_getGlyph((uint)currentChar); if(pGlyph) { charPointCount = uint_min(SYS_COUNTOF(charPoints),pGlyph->pointCount); memcpy(charPoints,pGlyph->pPoints,charPointCount*sizeof(float2)); dataChar = currentChar; } else { charPointCount = 0u; } currentPointIndex = -1; } if( dataChar && ( buttonPressMask & ButtonMask_Down ) ) { // add a point: if(charPointCount>0) { charPointCount--; } } if( dataChar && ( buttonPressMask & ButtonMask_Up ) ) { // add a point: if(charPointCount<SYS_COUNTOF(charPoints)) { float2_set(&charPoints[charPointCount],0.0f,0.0f); charPointCount++; } } if( renderer_isPageDone() ) { // new page: renderer_flipPage(); renderer_setPen( Pen_Font ); /* const float2 worldOffset = { 32.0f, 16.0f }; const float2 position = { 0.0f, 0.0f };*/ renderer_setVariance( 0.0f ); renderer_setTransform( 0 ); const float fontSize=3.0f; float2 textPos; float2_set(&textPos,32.0f,16.0f); if( charPointCount > 0u ) { float2x3 transform; float2x2_identity(&transform.rot); float2x2_scale1f(&transform.rot,&transform.rot,fontSize); transform.pos=textPos; renderer_setTransform( &transform ); renderer_addQuadraticStroke(charPoints,charPointCount); transform.pos.x -= 10.0f; renderer_setTransform( &transform ); renderer_addQuadraticStroke(charPoints,charPointCount); } // draw control points: const float cpSize=0.5f; int inRangePoint=-1; for(uint i = 0u; i < charPointCount; ++i) { float2 pos; float2_set(&pos,32.0f,16.0f); float2_addScaled1f(&pos,&pos,&charPoints[i],fontSize); const int inRange = float2_distance(&mousePagePos,&pos)<cpSize; if( inRange && inRangePoint==-1) { inRangePoint=(int)i; } if(currentPointIndex==-1&&inRange&&leftMousePressed) { currentPointIndex=(int)i; } else if(currentPointIndex!=-1) { if(!leftMouseDown) { currentPointIndex=-1; } else if( currentPointIndex==(int)i ) { // move cp to be under the mouse cursor: float2 newCpPos; float2_sub(&newCpPos,&mousePagePos,&textPos); float2_scale1f(&charPoints[i],&newCpPos,1.0f/fontSize); } } float3 color; if((int)i==currentPointIndex) { float3_set(&color,0.2f,1.0f,0.2f); } else if(inRange) { float3_set(&color,0.5f,0.5f,0.2f); } else { float3_set(&color,0.8f,0.2f,0.2f); } renderer_drawCircle(&pos,cpSize,&color); } oldMousePagePos=mousePagePos; leftMousePressed=0; rightMousePressed=0; float2_set(&textPos,5.0f,4.0f); font_drawText(&textPos,1.0f,0.0f,"0123456789A" ); } renderer_updatePage( timeStep ); FrameData frame; //memset( &frame, 0u, sizeof( frame ) ); //frame.time = s_game.gameTime; //frame.playerPos = s_game.player[ 0u ].position; renderer_drawFrame( &frame ); #elif defined( TEST_RENDERER ) if( renderer_isPageDone() ) { // new page: renderer_flipPage(); /*float2 points[] = { { -4.0f, 0.0f }, { -4.0f, -4.0f }, { 0.0f, -4.0f }, { 4.0f, -4.0f }, { 4.0f, 0.0f }, { 4.0f, 4.0f }, { 0.0f, 4.0f }, { -4.0f, 4.0f }, { -4.0f, 0.0f } { -0.2f, 1.0f } };*/ renderer_setPen( Pen_DebugGreen ); const float2 worldOffset = { 32.0f, 16.0f }; const float2 position = { 0.0f, 0.0f }; float2x3 bombTransform; float2x2_rotationY( &bombTransform.rot, 0.0f ); float2x2_scale1f( &bombTransform.rot, &bombTransform.rot, 1.0f ); float2_add( &bombTransform.pos, &position, &worldOffset ); renderer_setTransform( &bombTransform ); //renderer_addQuadraticStroke(&points[0u],&points[1u],&points[2u]); //renderer_addQuadraticStroke(points,SYS_COUNTOF(points)); float2 textPos; float2_set(&textPos,5.0f,4.0f); font_drawText(&textPos,2.0f,0.0f,"P"); } renderer_updatePage( timeStep ); FrameData frame; //memset( &frame, 0u, sizeof( frame ) ); //frame.time = s_game.gameTime; //frame.playerPos = s_game.player[ 0u ].position; renderer_drawFrame( &frame ); #else renderer_setVariance(0.0f); GameInput gameInput; memset( &gameInput, 0u, sizeof( gameInput ) ); gameInput.timeStep = timeStep; gameInput.buttonMask = buttonMask | joystickButtonMask; game_update( &gameInput ); game_render(); #endif SDL_GL_SwapBuffers(); #ifndef SYS_BUILD_MASTER timingFrameCount++; #endif } while( !quit ); // :TODO: nicer fade out.. SDL_PauseAudio( 1 ); game_done(); }
int sync_rx(struct bladerf *dev, void *samples, unsigned num_samples, struct bladerf_metadata *user_meta, unsigned int timeout_ms) { struct bladerf_sync *s = dev->sync[BLADERF_MODULE_RX]; struct buffer_mgmt *b; int status = 0; bool exit_early = false; bool copied_data = false; unsigned int samples_returned = 0; uint8_t *samples_dest = (uint8_t*)samples; uint8_t *buf_src = NULL; unsigned int samples_to_copy = 0; unsigned int samples_per_buffer = 0; uint64_t target_timestamp = UINT64_MAX; if (s == NULL || samples == NULL) { log_debug("NULL pointer passed to %s\n", __FUNCTION__); return BLADERF_ERR_INVAL; } else if (s->stream_config.format == BLADERF_FORMAT_SC16_Q11_META) { if (user_meta == NULL) { log_debug("NULL metadata pointer passed to %s\n", __FUNCTION__); return BLADERF_ERR_INVAL; } else { user_meta->status = 0; target_timestamp = user_meta->timestamp; } } b = &s->buf_mgmt; samples_per_buffer = s->stream_config.samples_per_buffer; log_verbose("%s: Requests %u samples.\n", __FUNCTION__, num_samples); while (!exit_early && samples_returned < num_samples && status == 0) { switch (s->state) { case SYNC_STATE_CHECK_WORKER: { int stream_error; sync_worker_state worker_state = sync_worker_get_state(s->worker, &stream_error); /* Propagate stream error back to the caller. * They can call this function again to restart the stream and * try again. */ if (stream_error != 0) { status = stream_error; } else { if (worker_state == SYNC_WORKER_STATE_IDLE) { log_debug("%s: Worker is idle. Going to reset buf " "mgmt.\n", __FUNCTION__); s->state = SYNC_STATE_RESET_BUF_MGMT; } else if (worker_state == SYNC_WORKER_STATE_RUNNING) { s->state = SYNC_STATE_WAIT_FOR_BUFFER; } else { status = BLADERF_ERR_UNEXPECTED; log_debug("%s: Unexpected worker state=%d\n", __FUNCTION__, worker_state); } } break; } case SYNC_STATE_RESET_BUF_MGMT: MUTEX_LOCK(&b->lock); /* When the RX stream starts up, it will submit the first T * transfers, so the consumer index must be reset to 0 */ b->cons_i = 0; MUTEX_UNLOCK(&b->lock); log_debug("%s: Reset buf_mgmt consumer index\n", __FUNCTION__); s->state = SYNC_STATE_START_WORKER; break; case SYNC_STATE_START_WORKER: sync_worker_submit_request(s->worker, SYNC_WORKER_START); status = sync_worker_wait_for_state( s->worker, SYNC_WORKER_STATE_RUNNING, SYNC_WORKER_START_TIMEOUT_MS); if (status == 0) { s->state = SYNC_STATE_WAIT_FOR_BUFFER; log_debug("%s: Worker is now running.\n", __FUNCTION__); } else { log_debug("%s: Failed to start worker, (%d)\n", __FUNCTION__, status); } break; case SYNC_STATE_WAIT_FOR_BUFFER: MUTEX_LOCK(&b->lock); /* Check the buffer state, as the worker may have produced one * since we last queried the status */ if (b->status[b->cons_i] == SYNC_BUFFER_FULL) { s->state = SYNC_STATE_BUFFER_READY; log_verbose("%s: buffer %u is ready to consume\n", __FUNCTION__, b->cons_i); } else { status = wait_for_buffer(b, timeout_ms, __FUNCTION__, b->cons_i); if (status == 0) { if (b->status[b->cons_i] != SYNC_BUFFER_FULL) { s->state = SYNC_STATE_CHECK_WORKER; } else { s->state = SYNC_STATE_BUFFER_READY; log_verbose("%s: buffer %u is ready to consume\n", __FUNCTION__, b->cons_i); } } } MUTEX_UNLOCK(&b->lock); break; case SYNC_STATE_BUFFER_READY: MUTEX_LOCK(&b->lock); b->status[b->cons_i] = SYNC_BUFFER_PARTIAL; b->partial_off = 0; MUTEX_UNLOCK(&b->lock); switch (s->stream_config.format) { case BLADERF_FORMAT_SC16_Q11: s->state = SYNC_STATE_USING_BUFFER; break; case BLADERF_FORMAT_SC16_Q11_META: s->state = SYNC_STATE_USING_BUFFER_META; s->meta.curr_msg_off = 0; s->meta.msg_num = 0; break; default: assert(!"Invalid stream format"); status = BLADERF_ERR_UNEXPECTED; } break; case SYNC_STATE_USING_BUFFER: /* SC16Q11 buffers w/o metadata */ MUTEX_LOCK(&b->lock); buf_src = (uint8_t*)b->buffers[b->cons_i]; samples_to_copy = uint_min(num_samples - samples_returned, samples_per_buffer - b->partial_off); memcpy(samples_dest + samples2bytes(s, samples_returned), buf_src + samples2bytes(s, b->partial_off), samples2bytes(s, samples_to_copy)); b->partial_off += samples_to_copy; samples_returned += samples_to_copy; log_verbose("%s: Provided %u samples to caller\n", __FUNCTION__, samples_to_copy); /* We've finished consuming this buffer and can start looking * for available samples in the next buffer */ if (b->partial_off >= samples_per_buffer) { /* Check for symptom of out-of-bounds accesses */ assert(b->partial_off == samples_per_buffer); advance_rx_buffer(b); s->state = SYNC_STATE_WAIT_FOR_BUFFER; } MUTEX_UNLOCK(&b->lock); break; case SYNC_STATE_USING_BUFFER_META: /* SC16Q11 buffers w/ metadata */ MUTEX_LOCK(&b->lock); switch (s->meta.state) { case SYNC_META_STATE_HEADER: assert(s->meta.msg_num < s->meta.msg_per_buf); buf_src = (uint8_t*)b->buffers[b->cons_i]; s->meta.curr_msg = buf_src + dev->msg_size * s->meta.msg_num; s->meta.msg_timestamp = metadata_get_timestamp(s->meta.curr_msg); s->meta.msg_flags = metadata_get_flags(s->meta.curr_msg); s->meta.curr_msg_off = 0; /* We've encountered a discontinuity and need to return * what we have so far, setting the status flags */ if (copied_data && s->meta.msg_timestamp != s->meta.curr_timestamp) { user_meta->status |= BLADERF_META_STATUS_OVERRUN; exit_early = true; log_debug("Sample discontinuity detected @ " "buffer %u, message %u: Expected t=%llu, " "got t=%llu\n", b->cons_i, s->meta.msg_num, (unsigned long long)s->meta.curr_timestamp, (unsigned long long)s->meta.msg_timestamp); } else { log_verbose("Got header for message %u: " "t_new=%u, t_old=%u\n", s->meta.msg_num, s->meta.msg_timestamp, s->meta.curr_timestamp); } s->meta.curr_timestamp = s->meta.msg_timestamp; s->meta.state = SYNC_META_STATE_SAMPLES; break; case SYNC_META_STATE_SAMPLES: if (!copied_data && (user_meta->flags & BLADERF_META_FLAG_RX_NOW) == 0 && target_timestamp < s->meta.curr_timestamp) { log_debug("Current timestamp is %llu, " "target=%llu (user=%llu)\n", (unsigned long long)s->meta.curr_timestamp, (unsigned long long)target_timestamp, (unsigned long long)user_meta->timestamp); status = BLADERF_ERR_TIME_PAST; } else if ((user_meta->flags & BLADERF_META_FLAG_RX_NOW) || target_timestamp == s->meta.curr_timestamp) { /* Copy the request amount up to the end of a * this message in the current buffer */ samples_to_copy = uint_min(num_samples - samples_returned, left_in_msg(s)); memcpy(samples_dest + samples2bytes(s, samples_returned), s->meta.curr_msg + METADATA_HEADER_SIZE + samples2bytes(s, s->meta.curr_msg_off), samples2bytes(s, samples_to_copy)); samples_returned += samples_to_copy; s->meta.curr_msg_off += samples_to_copy; if (!copied_data && (user_meta->flags & BLADERF_META_FLAG_RX_NOW)) { /* Provide the user with the timestamp at the * first returned sample when the * NOW flag has been provided */ user_meta->timestamp = s->meta.curr_timestamp; log_verbose("Updated user meta timestamp with: " "%llu\n", (unsigned long long) user_meta->timestamp); } copied_data = true; s->meta.curr_timestamp += samples_to_copy; /* We've begun copying samples, so our target will * just keep tracking the current timestamp. */ target_timestamp = s->meta.curr_timestamp; log_verbose("After copying samples, t=%llu\n", (unsigned long long)s->meta.curr_timestamp); if (left_in_msg(s) == 0) { assert(s->meta.curr_msg_off == s->meta.samples_per_msg); s->meta.state = SYNC_META_STATE_HEADER; s->meta.msg_num++; if (s->meta.msg_num >= s->meta.msg_per_buf) { assert(s->meta.msg_num == s->meta.msg_per_buf); advance_rx_buffer(b); s->meta.msg_num = 0; s->state = SYNC_STATE_WAIT_FOR_BUFFER; } } } else { const uint64_t time_delta = target_timestamp - s->meta.curr_timestamp; uint64_t left_in_buffer = (uint64_t) s->meta.samples_per_msg * (s->meta.msg_per_buf - s->meta.msg_num); /* Account for current position in buffer */ left_in_buffer -= s->meta.curr_msg_off; if (time_delta >= left_in_buffer) { /* Discard the remainder of this buffer */ advance_rx_buffer(b); s->state = SYNC_STATE_WAIT_FOR_BUFFER; s->meta.state = SYNC_META_STATE_HEADER; log_verbose("%s: Discarding rest of buffer.\n", __FUNCTION__); } else if (time_delta <= left_in_msg(s)) { /* Fast forward within the current message */ assert(time_delta <= SIZE_MAX); s->meta.curr_msg_off += (size_t) time_delta; s->meta.curr_timestamp += time_delta; log_verbose("%s: Seeking within message (t=%llu)\n", __FUNCTION__, s->meta.curr_timestamp); } else { s->meta.state = SYNC_META_STATE_HEADER; s->meta.msg_num += timestamp_to_msg(s, time_delta); log_verbose("%s: Seeking to message %u.\n", __FUNCTION__, s->meta.msg_num); } } break; default: assert(!"Invalid state"); status = BLADERF_ERR_UNEXPECTED; } MUTEX_UNLOCK(&b->lock); break; } } if (user_meta) { user_meta->actual_count = samples_returned; } return status; }
static int tx_task_exec_running(struct rxtx_data *tx, struct cli_state *s) { int status = 0; unsigned int samples_per_buffer; int16_t *tx_buffer; struct tx_params *tx_params = tx->params; unsigned int repeats_remaining; unsigned int delay_us; unsigned int delay_samples; unsigned int delay_samples_remaining; bool repeat_infinite; unsigned int timeout_ms; unsigned int sample_rate; enum state { INIT, READ_FILE, DELAY, PAD_TRAILING, DONE }; enum state state = INIT; /* Fetch the parameters required for the TX operation */ MUTEX_LOCK(&tx->param_lock); repeats_remaining = tx_params->repeat; delay_us = tx_params->repeat_delay; MUTEX_UNLOCK(&tx->param_lock); repeat_infinite = (repeats_remaining == 0); MUTEX_LOCK(&tx->data_mgmt.lock); samples_per_buffer = (unsigned int)tx->data_mgmt.samples_per_buffer; timeout_ms = tx->data_mgmt.timeout_ms; MUTEX_UNLOCK(&tx->data_mgmt.lock); status = bladerf_get_sample_rate(s->dev, tx->module, &sample_rate); if (status != 0) { set_last_error(&tx->last_error, ETYPE_BLADERF, status); return CLI_RET_LIBBLADERF; } /* Compute delay time as a sample count */ delay_samples = (unsigned int)((uint64_t)sample_rate * delay_us / 1000000); delay_samples_remaining = delay_samples; /* Allocate a buffer to hold each block of samples to transmit */ tx_buffer = (int16_t*) malloc(samples_per_buffer * 2 * sizeof(int16_t)); if (tx_buffer == NULL) { status = CLI_RET_MEM; set_last_error(&tx->last_error, ETYPE_ERRNO, errno == 0 ? ENOMEM : errno); } /* Keep writing samples while there is more data to send and no failures * have occurred */ while (state != DONE && status == 0) { unsigned char requests; unsigned int buffer_samples_remaining = samples_per_buffer; int16_t *tx_buffer_current = tx_buffer; /* Stop stream on STOP or SHUTDOWN, but only clear STOP. This will keep * the SHUTDOWN request around so we can read it when determining * our state transition */ requests = rxtx_get_requests(tx, RXTX_TASK_REQ_STOP); if (requests & (RXTX_TASK_REQ_STOP | RXTX_TASK_REQ_SHUTDOWN)) { break; } /* Keep adding to the buffer until it is full or a failure occurs */ while (buffer_samples_remaining > 0 && status == 0 && state != DONE) { size_t samples_populated = 0; switch (state) { case INIT: case READ_FILE: MUTEX_LOCK(&tx->file_mgmt.file_lock); /* Read from the input file */ samples_populated = fread(tx_buffer_current, 2 * sizeof(int16_t), buffer_samples_remaining, tx->file_mgmt.file); assert(samples_populated <= UINT_MAX); /* If the end of the file was reached, determine whether * to delay, re-read from the file, or pad the rest of the * buffer and finish */ if (feof(tx->file_mgmt.file)) { repeats_remaining--; if ((repeats_remaining > 0) || repeat_infinite) { if (delay_samples != 0) { delay_samples_remaining = delay_samples; state = DELAY; } } else { state = PAD_TRAILING; } /* Clear the EOF condition and rewind the file */ clearerr(tx->file_mgmt.file); rewind(tx->file_mgmt.file); } /* Check for errors */ else if (ferror(tx->file_mgmt.file)) { status = errno; set_last_error(&tx->last_error, ETYPE_ERRNO, status); } MUTEX_UNLOCK(&tx->file_mgmt.file_lock); break; case DELAY: /* Insert as many zeros as are necessary to realize the * specified repeat delay */ samples_populated = uint_min(buffer_samples_remaining, delay_samples_remaining); memset(tx_buffer_current, 0, samples_populated * 2 * sizeof(uint16_t)); delay_samples_remaining -= (unsigned int)samples_populated; if (delay_samples_remaining == 0) { state = READ_FILE; } break; case PAD_TRAILING: /* Populate the remainder of the buffer with zeros */ memset(tx_buffer_current, 0, buffer_samples_remaining * 2 * sizeof(uint16_t)); state = DONE; break; case DONE: default: break; } /* Advance the buffer pointer. * Remember, two int16_t's make up 1 sample in the SC16Q11 format */ buffer_samples_remaining -= (unsigned int)samples_populated; tx_buffer_current += (2 * samples_populated); } /* If there were no errors, transmit the data buffer */ if (status == 0) { bladerf_sync_tx(s->dev, tx_buffer, samples_per_buffer, NULL, timeout_ms); } } free(tx_buffer); return status; }
static int run(struct bladerf *dev, struct app_params *p, const struct test_case *t) { int status, status_out, status_wait; unsigned int samples_left; size_t i; struct bladerf_metadata meta; int16_t *samples, *buf; samples = calloc(2 * sizeof(int16_t), t->burst_len + 2); if (samples == NULL) { perror("malloc"); return BLADERF_ERR_MEM; } /* Leave the last two samples zero */ for (i = 0; i < (2 * t->burst_len); i += 2) { samples[i] = samples[i + 1] = MAGNITUDE; } memset(&meta, 0, sizeof(meta)); status = perform_sync_init(dev, BLADERF_MODULE_TX, t->buf_len, p); if (status != 0) { goto out; } status = bladerf_get_timestamp(dev, BLADERF_MODULE_TX, &meta.timestamp); if (status != 0) { fprintf(stderr, "Failed to get timestamp: %s\n", bladerf_strerror(status)); goto out; } else { printf("Initial timestamp: 0x%016"PRIx64"\n", meta.timestamp); } meta.timestamp += 200000; for (i = 0; i < t->iterations && status == 0; i++) { meta.flags = BLADERF_META_FLAG_TX_BURST_START; samples_left = t->burst_len + 2; buf = samples; printf("Sending burst @ %llu\n", (unsigned long long) meta.timestamp); while (samples_left && status == 0) { unsigned int to_send = uint_min(p->buf_size, samples_left); if (to_send == samples_left) { meta.flags |= BLADERF_META_FLAG_TX_BURST_END; } else { meta.flags &= ~BLADERF_META_FLAG_TX_BURST_END; } status = bladerf_sync_tx(dev, buf, to_send, &meta, 10000); //p->timeout_ms); if (status != 0) { fprintf(stderr, "TX failed @ iteration (%u) %s\n", (unsigned int )i, bladerf_strerror(status)); } meta.flags &= ~BLADERF_META_FLAG_TX_BURST_START; samples_left -= to_send; buf += 2 * to_send; } meta.timestamp += (t->burst_len + t->gap_len - 2); } printf("Waiting for samples to finish...\n"); fflush(stdout); /* Wait for samples to be transmitted before shutting down the TX module */ status_wait = wait_for_timestamp(dev, BLADERF_MODULE_TX, meta.timestamp - t->gap_len + 2, 3000); if (status_wait != 0) { status = first_error(status, status_wait); fprintf(stderr, "Failed to wait for TX to finish: %s\n", bladerf_strerror(status_wait)); } out: status_out = bladerf_enable_module(dev, BLADERF_MODULE_TX, false); if (status_out != 0) { fprintf(stderr, "Failed to disable TX module: %s\n", bladerf_strerror(status)); } else { printf("Done waiting.\n"); fflush(stdout); } status = first_error(status, status_out); free(samples); return status; }