Something went wrong. Try again.
The open source OpenXR runtime
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689// Copyright 2026, Beyley Cardellio// SPDX-License-Identifier: BSL-1.0/*! * @file * @brief A routine to automatically refine latency offsets of LED blink times using constellation samples. * @author Beyley Cardellio <ep1cm1n10n123@gmail.com> * @ingroup tracking */
#include "util/u_var.h"#include "util/u_debug.h"
#include "math/m_api.h"
#include "os/os_time.h"
#include "t_led_sync_refinement.h"
DEBUG_GET_ONCE_LOG_OPTION(led_sync_refinement_log, "T_LED_SYNC_REFINEMENT_LOG", U_LOGGING_INFO)
#define LOG_TRACE(refinement, ...) U_LOG_IFL_T(refinement->log_level, __VA_ARGS__)#define LOG_DEBUG(refinement, ...) U_LOG_IFL_D(refinement->log_level, __VA_ARGS__)#define LOG_INFO(refinement, ...) U_LOG_IFL_I(refinement->log_level, __VA_ARGS__)#define LOG_WARN(refinement, ...) U_LOG_IFL_W(refinement->log_level, __VA_ARGS__)#define LOG_ERROR(refinement, ...) U_LOG_IFL_E(refinement->log_level, __VA_ARGS__)
//! The amount of frames to not see it for consider a device "not found" during an active search#define MAX_SAMPLE_LATENCY 3
/* * Helper functions */
//! The amount of time of which a change under this amount is considered "good" and we can exit the binary searchstatic time_duration_nschange_good_cutoff(struct t_led_sync_refinement *refinement){ return MIN(refinement->current_blink_duration_ns, refinement->exposure_time_ns) / 50;}
//! The amount of frames since the sample was applied to the controllerstatic uint32_tframes_since_sample_apply(struct t_led_sync_refinement *refinement, timepoint_ns now_ns){ return (uint32_t)((now_ns - refinement->last_sample_apply_time_ns) / refinement->exposure_interval_ns);}
static voidset_offset_duration(struct t_led_sync_refinement *refinement, time_duration_ns latency_offset_ns, time_duration_ns fudge_offset_ns, time_duration_ns blink_duration_ns){ refinement->current_latency_offset_ns = latency_offset_ns; refinement->current_blink_fudge_ns = fudge_offset_ns; refinement->current_blink_duration_ns = blink_duration_ns;
refinement->has_sample_for_driver = true; refinement->sample_applied = false; refinement->sample_for_driver = XRT_C11_COMPOUND(struct t_led_sync_sample){ .timestamp.device_host_latency_ns = latency_offset_ns, .timestamp_mode = T_LED_SYNC_SAMPLE_TIMESTAMP_MODE_DEVICE_HOST_LATENCY, .fudge_offset_ns = fudge_offset_ns, .blink_duration_ns = blink_duration_ns, };
// If we're doing optical driven offset and have a saved offset, use that mode. if ((refinement->options.flags & T_LED_SYNC_REFINEMENT_FLAGS_OPTICAL_DRIVEN_OFFSET) != 0 && refinement->optical_driven_offset.has_saved_offset) { refinement->sample_for_driver.timestamp_mode = T_LED_SYNC_SAMPLE_TIMESTAMP_MODE_HOST_DEVICE_CLOCK_OFFSET; refinement->sample_for_driver.timestamp.host_device_clock_offset_ns = refinement->optical_driven_offset.saved_offset + latency_offset_ns; }
LOG_TRACE(refinement, "Setting offset to %" PRId64 "ns with fudge %" PRId64 "ns and blink duration to %" PRId64 "ns", latency_offset_ns, fudge_offset_ns, blink_duration_ns);}
static voidset_centered_duration(struct t_led_sync_refinement *refinement, time_duration_ns blink_duration_ns){ // Find the latency offset, the offset needing to be applied to get blink start to line up with exposure // start, this is the transfer latency from the controller->host. time_duration_ns new_latency_offset_ns = refinement->found_left_edge_ns;
// Set the offset so that the center of the blink is at the center of the exposure, since latency and // fudge offset are added together. time_duration_ns new_fudge_offset_ns = (refinement->exposure_time_ns / 2) - (blink_duration_ns / 2);
set_offset_duration(refinement, new_latency_offset_ns, new_fudge_offset_ns, blink_duration_ns);}
//! Gets the period to iterate over when searching for a latency offset.static time_duration_nsget_search_period_locked(struct t_led_sync_refinement *refinement){ int divisor = 4;
if (!refinement->has_exposure_time) { return refinement->current_blink_duration_ns / divisor; }
if (refinement->phase == T_LED_SYNC_SEARCH_PHASE_FIND_INITIAL_OFFSET) { // If we're in initial offset search, we can use the larger of the two, since we just need to find one // that overlaps. return MAX(refinement->current_blink_duration_ns, refinement->exposure_time_ns) / divisor; } else { return MIN(refinement->current_blink_duration_ns, refinement->exposure_time_ns) / divisor; }}
static time_duration_nsmax_right_edge(struct t_led_sync_refinement *refinement){ if ((refinement->options.flags & T_LED_SYNC_REFINEMENT_FLAGS_HAS_LATENCY_CAP) != 0) { return refinement->options.latency_cap_ns; } else { return refinement->exposure_interval_ns - refinement->current_blink_duration_ns; }}
static voidset_phase_locked(struct t_led_sync_refinement *refinement, enum t_led_sync_phase new_phase){ refinement->phase = new_phase;
LOG_INFO(refinement, "Entering search phase: %d", new_phase);
switch (new_phase) { case T_LED_SYNC_SEARCH_PHASE_FIND_INITIAL_OFFSET: { refinement->found_left_edge_ns = -1; refinement->found_right_edge_ns = -1;
refinement->current_blink_duration_ns = refinement->options.initial_blink_duration_ns;
refinement->optical_driven_offset.has_saved_offset = false;
set_offset_duration(refinement, 0, 0, refinement->current_blink_duration_ns);
break; } case T_LED_SYNC_SEARCH_PHASE_FIND_RIGHT_EDGE: { // If we're doing clock driven offset if (refinement->options.flags & T_LED_SYNC_REFINEMENT_FLAGS_OPTICAL_DRIVEN_OFFSET) { refinement->optical_driven_offset.has_saved_offset = true; refinement->optical_driven_offset.saved_offset = refinement->optical_driven_offset.driver_host_device_clock_offset_ns; }
// Left bound is the current offset refinement->binary_search_state.left_bound_ns = refinement->current_blink_fudge_ns; // Right bound is the start of the next exposure minus the blink duration, we don't want to blink into // the next frame, rather start at zero, since latency will always be >0. refinement->binary_search_state.right_bound_ns = max_right_edge(refinement);
LOG_DEBUG(refinement, "Starting binary search for right edge, left bound at: %" PRId64 "ns, right bound at: %" PRId64 "ns", refinement->binary_search_state.left_bound_ns, refinement->binary_search_state.right_bound_ns);
time_duration_ns new_offset_ns = (refinement->binary_search_state.right_bound_ns + refinement->binary_search_state.left_bound_ns) / 2; set_offset_duration(refinement, 0, new_offset_ns, refinement->current_blink_duration_ns);
break; } case T_LED_SYNC_SEARCH_PHASE_FIND_LEFT_EDGE: { // We should always have a right edge by now assert(refinement->found_right_edge_ns > -1);
// Left bound is when the exposure *should* be given zero latency refinement->binary_search_state.left_bound_ns = 0; // Right bound is the found right edge refinement->binary_search_state.right_bound_ns = refinement->found_right_edge_ns;
LOG_DEBUG(refinement, "Starting binary search for left edge, right edge at: %" PRId64 "ns", refinement->found_right_edge_ns);
time_duration_ns new_offset_ns = (refinement->binary_search_state.right_bound_ns + refinement->binary_search_state.left_bound_ns) / 2; set_offset_duration(refinement, 0, new_offset_ns, refinement->current_blink_duration_ns);
break; } case T_LED_SYNC_SEARCH_PHASE_REFINE_BLINK_DURATION: { // We should have both edges by now assert(refinement->found_right_edge_ns > -1); assert(refinement->found_left_edge_ns > -1);
set_centered_duration(refinement, refinement->current_blink_duration_ns);
refinement->blink_time_refinement_state.backing_off = false; refinement->blink_time_refinement_state.last_good_blink_duration_ns = refinement->current_blink_duration_ns;
break; } case T_LED_SYNC_SEARCH_PHASE_MAINTAIN_OFFSET: { // We should have both edges by now assert(refinement->found_right_edge_ns > -1); assert(refinement->found_left_edge_ns > -1);
set_centered_duration(refinement, refinement->current_blink_duration_ns);
LOG_DEBUG(refinement, "Maintaining offset at: %" PRId64 "ns", refinement->current_latency_offset_ns);
break; } default: break; }}
static voidhandle_find_initial_offset(struct t_led_sync_refinement *refinement){ if (refinement->frames_since_last_visually_seen > MAX_SAMPLE_LATENCY) { const time_duration_ns search_period_ns = get_search_period_locked(refinement); const time_duration_ns new_fudge_offset_ns = (refinement->current_blink_fudge_ns + search_period_ns) % max_right_edge(refinement);
LOG_DEBUG(refinement, "Device hasn't been seen, trying offset: %" PRId64 "ns", new_fudge_offset_ns);
// Scan the fudge offset around instead of latency, so latency isn't jumping around at random and // confusing time conversion math. set_offset_duration(refinement, 0, new_fudge_offset_ns, refinement->current_blink_duration_ns); } else { LOG_DEBUG(refinement, "Device has been seen, moving to find edge phases");
// We got a sample recently, try to find the edges now. set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_FIND_RIGHT_EDGE); }}
static voidhandle_find_right_edge(struct t_led_sync_refinement *refinement){ if (refinement->frames_since_last_visually_seen > MAX_SAMPLE_LATENCY) { LOG_DEBUG(refinement, "Device hasn't been seen for %" PRIu32 " frames, advancing binary search with left side closer", refinement->frames_since_last_visually_seen);
// We didn't see the device refinement->binary_search_state.right_bound_ns = refinement->current_blink_fudge_ns;
time_duration_ns new_offset_ns = (refinement->binary_search_state.left_bound_ns + refinement->binary_search_state.right_bound_ns) / 2;
set_offset_duration(refinement, 0, new_offset_ns, refinement->current_blink_duration_ns);
} else { LOG_DEBUG(refinement, "Device has been seen, advancing binary search with right side closer");
// We saw the device refinement->binary_search_state.left_bound_ns = refinement->current_blink_fudge_ns;
time_duration_ns new_offset_ns = (refinement->binary_search_state.left_bound_ns + refinement->binary_search_state.right_bound_ns) / 2;
set_offset_duration(refinement, 0, new_offset_ns, refinement->current_blink_duration_ns); }
bool done = refinement->binary_search_state.right_bound_ns - refinement->binary_search_state.left_bound_ns < change_good_cutoff(refinement); if (done) { // We found a good enough offset for the right edge, move on to finding the left edge. refinement->found_right_edge_ns = refinement->current_blink_fudge_ns;
LOG_DEBUG(refinement, "Found right edge at offset: %" PRId64 "ns. Difference %" PRId64 "ns was lower than cutoff %" PRId64 "ns, moving to find left edge phase", refinement->current_blink_fudge_ns, refinement->binary_search_state.right_bound_ns - refinement->binary_search_state.left_bound_ns, change_good_cutoff(refinement));
set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_FIND_LEFT_EDGE); }}
static voidhandle_find_left_edge(struct t_led_sync_refinement *refinement){ if (refinement->frames_since_last_visually_seen > MAX_SAMPLE_LATENCY) { LOG_DEBUG(refinement, "Device hasn't been seen for %" PRIu32 " frames, advancing binary search with right side closer", refinement->frames_since_last_visually_seen);
// We didn't see the device refinement->binary_search_state.left_bound_ns = refinement->current_blink_fudge_ns + refinement->current_blink_duration_ns;
time_duration_ns new_offset_ns = ((refinement->binary_search_state.left_bound_ns + refinement->binary_search_state.right_bound_ns) / 2); // Set the new offset so that we're moving the right edge of the blink to the left edge of the exposure set_offset_duration(refinement, 0, new_offset_ns - refinement->current_blink_duration_ns, refinement->current_blink_duration_ns); } else { LOG_DEBUG(refinement, "Device has been seen, advancing binary search with left side closer");
// We saw the device refinement->binary_search_state.right_bound_ns = refinement->current_blink_fudge_ns + refinement->current_blink_duration_ns;
time_duration_ns new_offset_ns = (refinement->binary_search_state.left_bound_ns + refinement->binary_search_state.right_bound_ns) / 2; // Set the new offset so that we're moving the right edge of the blink to the left edge of the exposure set_offset_duration(refinement, 0, new_offset_ns - refinement->current_blink_duration_ns, refinement->current_blink_duration_ns); }
bool done = refinement->binary_search_state.right_bound_ns - refinement->binary_search_state.left_bound_ns < change_good_cutoff(refinement); if (done) { // We found a good enough offset for the left edge, we can maintain the offset now. refinement->found_left_edge_ns = refinement->current_blink_fudge_ns + refinement->current_blink_duration_ns;
LOG_DEBUG(refinement, "Found left edge at offset: %" PRId64 "ns. Difference %" PRId64 "ns was lower than cutoff %" PRId64 "ns, moving to maintain offset phase", refinement->current_blink_fudge_ns, refinement->binary_search_state.right_bound_ns - refinement->binary_search_state.left_bound_ns, change_good_cutoff(refinement));
// The visibility window is how much of the time window the LEDs were visible for time_duration_ns visibility_window_ns = refinement->found_right_edge_ns - refinement->current_blink_fudge_ns;
// The real LED on period is the amount of time the LEDs were actually visible to the camera for time_duration_ns real_led_on_window_ns = visibility_window_ns - (refinement->exposure_time_ns); LOG_DEBUG(refinement, "Found left edge at offset: %" PRId64 "ns, right edge at offset: %" PRId64 "ns, visibility window: %" PRId64 "ns, real LED on window: %" PRId64 "ns", refinement->found_left_edge_ns, refinement->found_right_edge_ns, visibility_window_ns, real_led_on_window_ns);
// We've found the latency offset, so if we're not optimizing blink duration, we can move to just // maintaining the offset now. if (refinement->options.flags & T_LED_SYNC_REFINEMENT_FLAGS_BLINK_DURATION) { set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_REFINE_BLINK_DURATION); } else { set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_MAINTAIN_OFFSET); } }}
static voidhandle_refine_blink_duration(struct t_led_sync_refinement *refinement){ if (refinement->frames_since_last_visually_seen > MAX_SAMPLE_LATENCY) { LOG_DEBUG(refinement, "Device hasn't been seen for %" PRIu32 " frames, trying shorter blink duration", refinement->frames_since_last_visually_seen);
if (refinement->blink_time_refinement_state.backing_off) { // Backing off still didn't work, just go back to the start and hope for the best. time_duration_ns new_blink_duration = refinement->options.initial_blink_duration_ns; set_centered_duration(refinement, new_blink_duration);
LOG_DEBUG(refinement, "Backing off didn't work, resetting blink duration to initial value: %" PRId64 "ns", new_blink_duration);
set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_MAINTAIN_OFFSET); return; }
// We didn't see the device, try increasing the blink duration again to back off time_duration_ns new_blink_duration = refinement->blink_time_refinement_state.last_good_blink_duration_ns;
set_centered_duration(refinement, new_blink_duration); refinement->blink_time_refinement_state.backing_off = true;
LOG_DEBUG(refinement, "Trying to back off to last good blink duration: %" PRId64 "ns", new_blink_duration); } else { if (refinement->blink_time_refinement_state.backing_off) { // We backed off and it worked, so now we found the minimum blink duration that doesn't cause // instability, we can stop refining. LOG_DEBUG(refinement, "Back off worked, found good blink duration: %" PRId64 "ns, stopping refinement", refinement->current_blink_duration_ns); set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_MAINTAIN_OFFSET); return; }
time_duration_ns search_period_ns = get_search_period_locked(refinement); time_duration_ns new_blink_duration_ns = refinement->current_blink_duration_ns - search_period_ns;
// We saw the device, this is a good blink duration, save it in case we need to back off. refinement->blink_time_refinement_state.last_good_blink_duration_ns = refinement->current_blink_duration_ns; refinement->blink_time_refinement_state.backing_off = false;
LOG_DEBUG(refinement, "Device has been seen, trying shorter blink duration: %" PRId64 "ns (was %" PRId64 "ns)", new_blink_duration_ns, refinement->current_blink_duration_ns);
if (new_blink_duration_ns < refinement->options.min_blink_duration_ns) { LOG_DEBUG(refinement, "New blink duration %" PRId64 "ns was below minimum blink duration %" PRId64 "ns, stopping refinement", new_blink_duration_ns, refinement->options.min_blink_duration_ns); set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_MAINTAIN_OFFSET); return; }
set_centered_duration(refinement, new_blink_duration_ns); }}
static voidhandle_maintain_offset(struct t_led_sync_refinement *refinement){}
static voidhandle_timing_event_locked(struct t_led_sync_refinement *refinement, const struct t_timing_event_camera_exposure_start *event){ if (event->exposure_time_ns > 0) { refinement->has_exposure_time = true; refinement->exposure_time_ns = event->exposure_time_ns; }
assert(event->frame_period_ns > 0); // No frame period present. Please estimate this externally.
refinement->exposure_interval_ns = event->frame_period_ns; refinement->frames_since_last_visually_seen++; refinement->current_sequence_id = event->sequence_id;
// We have an exposure interval now, start searching! if (refinement->phase == T_LED_SYNC_SEARCH_PHASE_INIT) { set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_FIND_INITIAL_OFFSET); }
// Not applied yet if (!refinement->sample_applied) { return; }
timepoint_ns now_ns = os_monotonic_get_ns();
// Let the device settle for a few frames after applying a sample before we start trying to refine again if (frames_since_sample_apply(refinement, now_ns) < refinement->options.settle_frames) { return; }
bool trigger_resync = refinement->frames_since_last_visually_seen > (refinement->options.time_to_resync_ns / refinement->exposure_interval_ns) && refinement->phase > T_LED_SYNC_SEARCH_PHASE_FIND_INITIAL_OFFSET; // If we're past the initial find and we haven't seen the controller in some amount of // time, start searching for an offset again. if (trigger_resync) { LOG_DEBUG(refinement, "Triggering resync due to not being visually seen for %" PRIu32 " frames", refinement->frames_since_last_visually_seen); set_phase_locked(refinement, T_LED_SYNC_SEARCH_PHASE_FIND_INITIAL_OFFSET); }
switch (refinement->phase) { case T_LED_SYNC_SEARCH_PHASE_INIT: return; case T_LED_SYNC_SEARCH_PHASE_FIND_INITIAL_OFFSET: { handle_find_initial_offset(refinement); break; } case T_LED_SYNC_SEARCH_PHASE_FIND_RIGHT_EDGE: { handle_find_right_edge(refinement); break; } case T_LED_SYNC_SEARCH_PHASE_FIND_LEFT_EDGE: { handle_find_left_edge(refinement); break; } case T_LED_SYNC_SEARCH_PHASE_REFINE_BLINK_DURATION: { handle_refine_blink_duration(refinement); break; } case T_LED_SYNC_SEARCH_PHASE_MAINTAIN_OFFSET: { handle_maintain_offset(refinement); break; } default: break; }}
static voidhandle_constellation_sample_locked(struct t_led_sync_refinement *refinement, const struct t_constellation_tracker_sample *sample){ refinement->frames_since_last_visually_seen = 0;}
/* * Exported functions */
intt_led_sync_refinement_init(struct t_led_sync_refinement *refinement, const struct t_led_sync_refinement_options *options){ int result;
(*refinement) = XRT_C11_COMPOUND(struct t_led_sync_refinement){ .initialized = false,
.log_level = debug_get_log_option_led_sync_refinement_log(),
.options = *options,
.has_exposure_time = false, .exposure_time_ns = 0,
.exposure_interval_ns = 0,
// Push an initial sample .has_sample_for_driver = true, .sample_for_driver = { .timestamp.device_host_latency_ns = 0, .timestamp_mode = T_LED_SYNC_SAMPLE_TIMESTAMP_MODE_DEVICE_HOST_LATENCY, .fudge_offset_ns = 0, .blink_duration_ns = options->initial_blink_duration_ns, }, .sample_applied = false,
.phase = T_LED_SYNC_SEARCH_PHASE_INIT, .current_latency_offset_ns = 0, .current_blink_fudge_ns = 0, .current_blink_duration_ns = options->initial_blink_duration_ns, .frames_since_last_visually_seen = 0, .current_sequence_id = 0, .last_sample_apply_time_ns = 0,
.found_left_edge_ns = -1, .found_right_edge_ns = -1,
.binary_search_state = { .left_bound_ns = 0, .right_bound_ns = 0, }, };
result = os_mutex_init(&refinement->lock); if (result < 0) { return result; }
refinement->initialized = true;
u_var_add_root(refinement, "LED Sync Refinement", true);
u_var_add_gui_header(refinement, NULL, "Options"); u_var_add_ro_i32(refinement, (int32_t *)&refinement->options.flags, "flags"); u_var_add_ro_i64_ns(refinement, &refinement->options.initial_blink_duration_ns, "initial_blink_duration_ns"); u_var_add_ro_i64_ns(refinement, &refinement->options.min_blink_duration_ns, "min_blink_duration_ns"); u_var_add_ro_i64_ns(refinement, &refinement->options.max_blink_duration_ns, "max_blink_duration_ns"); u_var_add_ro_i64_ns(refinement, &refinement->options.time_to_resync_ns, "time_to_resync_ns"); u_var_add_ro_u32(refinement, &refinement->options.settle_frames, "settle_frames"); u_var_add_ro_i64_ns(refinement, &refinement->options.latency_cap_ns, "latency_cap_ns");
u_var_add_gui_header(refinement, NULL, "Exposure Info"); u_var_add_bool(refinement, &refinement->has_exposure_time, "Has Exposure Time"); u_var_add_ro_i64_ns(refinement, &refinement->exposure_time_ns, "Exposure Time (ns)"); u_var_add_ro_i64_ns(refinement, &refinement->exposure_interval_ns, "Exposure Interval (ns)");
u_var_add_gui_header(refinement, NULL, "Driver Sample"); u_var_add_bool(refinement, &refinement->has_sample_for_driver, "Has Sample For Driver"); u_var_add_i32(refinement, (int32_t *)&refinement->sample_for_driver.timestamp_mode, "Sample Timestamp Mode"); u_var_add_ro_i64_ns(refinement, &refinement->sample_for_driver.timestamp.device_host_latency_ns, "Sample Device Host Latency (ns)"); u_var_add_ro_i64_ns(refinement, &refinement->sample_for_driver.fudge_offset_ns, "Sample Fudge Offset (ns)"); u_var_add_ro_i64_ns(refinement, &refinement->sample_for_driver.blink_duration_ns, "Sample Blink Duration (ns)"); u_var_add_bool(refinement, &refinement->sample_applied, "Sample Applied");
u_var_add_gui_header(refinement, NULL, "State"); u_var_add_ro_u32(refinement, (uint32_t *)&refinement->phase, "phase"); u_var_add_ro_i64_ns(refinement, &refinement->current_latency_offset_ns, "current_latency_offset_ns"); u_var_add_ro_i64_ns(refinement, &refinement->current_blink_fudge_ns, "current_blink_fudge_ns"); u_var_add_ro_i64_ns(refinement, &refinement->current_blink_duration_ns, "current_blink_duration_ns"); u_var_add_ro_u32(refinement, &refinement->frames_since_last_visually_seen, "frames_since_last_visually_seen"); u_var_add_ro_u32(refinement, &refinement->current_sequence_id, "current_sequence_id"); u_var_add_ro_i64_ns(refinement, &refinement->last_sample_apply_time_ns, "last_sample_apply_time_ns"); u_var_add_ro_i64_ns(refinement, &refinement->found_left_edge_ns, "found_left_edge_ns"); u_var_add_ro_i64_ns(refinement, &refinement->found_right_edge_ns, "found_right_edge_ns"); u_var_add_ro_i64_ns(refinement, &refinement->binary_search_state.left_bound_ns, "binary_search.left_bound_ns"); u_var_add_ro_i64_ns(refinement, &refinement->binary_search_state.right_bound_ns, "binary_search.right_bound_ns"); u_var_add_ro_i64_ns(refinement, &refinement->blink_time_refinement_state.last_good_blink_duration_ns, "blink_time_refinement.last_good_blink_duration_ns"); u_var_add_bool(refinement, &refinement->blink_time_refinement_state.backing_off, "blink_time_refinement.backing_off"); u_var_add_ro_i64_ns(refinement, &refinement->optical_driven_offset.driver_host_device_clock_offset_ns, "optical_driven_offset.driver_host_device_clock_offset_ns"); u_var_add_bool(refinement, &refinement->optical_driven_offset.has_saved_offset, "optical_driven_offset.has_saved_offset"); u_var_add_ro_i64_ns(refinement, &refinement->optical_driven_offset.saved_offset, "optical_driven_offset.saved_offset");
return 0;}
voidt_led_sync_refinement_destroy(struct t_led_sync_refinement *refinement){ if (!refinement->initialized) { return; }
os_mutex_destroy(&refinement->lock);}
voidt_led_sync_push_timing_event(struct t_led_sync_refinement *refinement, const struct t_timing_event_camera_exposure_start *event){ assert(refinement->initialized);
os_mutex_lock(&refinement->lock); handle_timing_event_locked(refinement, event); os_mutex_unlock(&refinement->lock);}
voidt_led_sync_push_constellation_sample(struct t_led_sync_refinement *refinement, const struct t_constellation_tracker_sample *sample){ assert(refinement->initialized);
os_mutex_lock(&refinement->lock); handle_constellation_sample_locked(refinement, sample); os_mutex_unlock(&refinement->lock);}
boolt_led_sync_get_sample(struct t_led_sync_refinement *refinement, struct t_led_sync_sample *out_sample){ assert(refinement->initialized);
os_mutex_lock(&refinement->lock); { if (!refinement->has_sample_for_driver) { os_mutex_unlock(&refinement->lock); return false; }
*out_sample = refinement->sample_for_driver; refinement->has_sample_for_driver = false; } os_mutex_unlock(&refinement->lock);
return true;}
voidt_led_sync_mark_latest_sample_applied(struct t_led_sync_refinement *refinement, timepoint_ns apply_time_ns){ assert(refinement->initialized);
os_mutex_lock(&refinement->lock); refinement->sample_applied = true; refinement->last_sample_apply_time_ns = apply_time_ns; os_mutex_unlock(&refinement->lock);}
voidt_led_sync_push_host_device_clock_offset(struct t_led_sync_refinement *refinement, time_duration_ns clock_offset_ns){ assert(refinement->initialized);
os_mutex_lock(&refinement->lock); { refinement->optical_driven_offset.driver_host_device_clock_offset_ns = clock_offset_ns; } os_mutex_unlock(&refinement->lock);}