Problem with triggered TX
Posted: Thu Oct 08, 2026 6:04 am
Hi,
I am trying to send a signal synchronously and periodically with trigger input. For each trigger, I want to emit a fixed number of samples. So, if the trigger occurs every 10 milliseconds and I have samples for a 5-millisecond transmission, there will be a 5-millisecond gap between transmissions.
I have tried to achieve this using the gating mechanism, but I am unable to have a transmission where the tx burst does not drift and is always a constant distance relative to the trigger input.
I might have missed some functionality / misconfigured the radio, therefore any pointers would be greatly appreciated. Thanks
For reference this is the code i have used to try to get the specified behaviour:
I am trying to send a signal synchronously and periodically with trigger input. For each trigger, I want to emit a fixed number of samples. So, if the trigger occurs every 10 milliseconds and I have samples for a 5-millisecond transmission, there will be a 5-millisecond gap between transmissions.
I have tried to achieve this using the gating mechanism, but I am unable to have a transmission where the tx burst does not drift and is always a constant distance relative to the trigger input.
I might have missed some functionality / misconfigured the radio, therefore any pointers would be greatly appreciated. Thanks
For reference this is the code i have used to try to get the specified behaviour:
Code: Select all
status = bladerf_open(&dev, nullptr);
if (status != 0) {
fprintf(stderr, "Unable to open bladeRF: %s\n", bladerf_strerror(status));
return 1;
}
status = bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(0), frequency);
if (status == 0) {
status = bladerf_set_sample_rate(dev, BLADERF_CHANNEL_TX(0), (bladerf_sample_rate)requested_rate, &actual_rate);
}
if (status == 0) {
status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), actual_rate, &actual_bandwidth);
}
if (status == 0) {
status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(0), (bladerf_gain)tx_gain_db);
}
if (status != 0) {
fprintf(stderr, "Device configuration failed: %s\n", bladerf_strerror(status));
goto cleanup;
}
if (actual_rate % 100 != 0) {
fprintf(stderr, "Actual sample rate must produce an integral 10 ms frame.\n");
status = BLADERF_ERR_INVAL;
goto cleanup;
}
frame_samples = (size_t)(actual_rate / 100);
trigger_samples = (size_t)llround((double)actual_rate * (double)trigger_high_us / 1000000.0);
if (trigger_samples == 0 || trigger_samples >= frame_samples) {
fprintf(stderr, "Trigger pulse width is outside the usable frame.\n");
status = BLADERF_ERR_INVAL;
goto cleanup;
}
playback_samples = frame_samples - trigger_samples;
start_sample = (size_t)llround((double)actual_rate * start_ms / 1000.0);
duration_samples = (size_t)llround((double)actual_rate * duration_ms / 1000.0);
requested_end = start_sample + duration_samples;
if (duration_samples == 0 || requested_end > frame_samples) {
fprintf(stderr,
"Requested interval must contain samples and end within "
"the 10 ms frame.\n");
status = BLADERF_ERR_INVAL;
goto cleanup;
}
if (start_sample < trigger_samples) {
start_sample = trigger_samples;
if (start_sample >= requested_end) {
fprintf(stderr,
"Requested interval is entirely inside the "
"trigger-high portion.\n");
status = BLADERF_ERR_INVAL;
goto cleanup;
}
fprintf(stderr,
"Requested interval clipped at the trigger falling "
"edge; TX is gated while the trigger is high.\n");
}
duration_samples = requested_end - start_sample;
start_sample -= trigger_samples;
if (tone_hz >= (double)actual_rate / 2.0 || start_sample + duration_samples > playback_samples) {
fprintf(stderr, "Tone or interval is outside the playable range.\n");
status = BLADERF_ERR_INVAL;
goto cleanup;
}
if (playback_samples > UINT_MAX || playback_samples > SIZE_MAX / (2 * sizeof(*samples))) {
fprintf(stderr, "Computed playback frame is too large.\n");
status = BLADERF_ERR_INVAL;
goto cleanup;
}
samples = (int16_t *)malloc(playback_samples * 2 * sizeof(*samples));
if (samples == NULL) {
perror("malloc");
status = BLADERF_ERR_MEM;
goto cleanup;
}
// NOTE: Generate the playback frame with the specified tone and interval.
make_frame(samples, playback_samples, start_sample, duration_samples, actual_rate, tone_hz);
status = bladerf_sync_config(dev, BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11, 16, TX_BUFFER_SIZE, 8, 1000);
if (status != 0) {
fprintf(stderr, "TX stream configuration failed: %s\n", bladerf_strerror(status));
goto cleanup;
}
status = bladerf_trigger_init(dev, BLADERF_CHANNEL_TX(0), BLADERF_TRIGGER_MINI_EXP_1, &trigger);
if (status != 0) {
fprintf(stderr, "Trigger initialization failed: %s\n", bladerf_strerror(status));
goto cleanup;
}
trigger.role = BLADERF_TRIGGER_ROLE_SLAVE;
status = bladerf_trigger_arm(dev, &trigger, true, 0, 0);
if (status != 0) {
fprintf(stderr, "Unable to arm TX trigger: %s\n", bladerf_strerror(status));
goto cleanup;
}
trigger_armed = true;
status = bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), true);
if (status != 0) {
fprintf(stderr, "Unable to enable TX: %s\n", bladerf_strerror(status));
goto cleanup;
}
tx_enabled = true;
// NOTE: stop_requested is wired to a signal handler for SIGINT
while (!stop_requested) {
status = bladerf_sync_tx(dev, samples, (unsigned int)playback_samples, NULL, 1000);
if (status != 0) {
fprintf(stderr, "TX stream failed: %s\n", bladerf_strerror(status));
goto cleanup;
}
}
// NOTE: cleanup omitted for brevity