Created
June 18, 2025 11:15
-
-
Save zeyus/cab8049b898e489b30566bd536779bde to your computer and use it in GitHub Desktop.
FSR -> Photodiode latency data analysis
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
| library("conflicted") | |
| library("tidyverse") | |
| # Define data sources with file paths and names | |
| data_sources <- list( | |
| list( | |
| file_path = "timing_log_simplified_5091.csv", | |
| name = "iPadM3Pro|Flutter|Comprehensive" | |
| ), | |
| list( | |
| file_path = "timing_log_simplified_9987.csv", | |
| name = "iPadM3Pro|Swift|Basic" | |
| ), | |
| list( | |
| file_path = "timing_log_simplified_1542.csv", | |
| name = "iPadM3Pro|Flutter|Basic" | |
| ), | |
| list( | |
| file_path = "timing_log_simplified_7802.csv", | |
| name = "Pixel7a|Flutter|Comprehensive" | |
| ), | |
| list( | |
| file_path = "timing_log_simplified_3362.csv", | |
| name = "Pixel7a|Flutter|Basic" | |
| ) | |
| ) | |
| # example: | |
| # frame_number,pin_number,pin_state,is_start_pin,trigger_active,pin_states | |
| # 0,2,1,0,0,0;0;0;1 | |
| # 322577,0,1,1,1,1;0;0;1 | |
| # 324950,0,0,1,0,0;0;0;1 | |
| # 325307,1,1,0,0,0;0;1;1 | |
| # 327530,1,0,0,1,0;0;0;1 | |
| # 534245,0,1,1,1,1;0;0;1 | |
| # 537016,0,0,1,0,0;0;0;1 | |
| # 537362,1,1,0,0,0;0;1;1 | |
| # 539586,1,0,0,1,0;0;0;1 | |
| # 554218,0,1,1,1,1;0;0;1 | |
| # 557208,1,1,0,0,1;0;1;1 | |
| # 557309,0,0,1,0,0;0;1;1 | |
| # Function to find previous FSR event for each photodiode event | |
| calculate_latencies <- function(fsr_events, photodiode_events) { | |
| latencies <- numeric(nrow(photodiode_events)) | |
| for (i in 1:nrow(photodiode_events)) { | |
| photo_frame <- photodiode_events$frame_number[i] | |
| # Find the most recent FSR event before this photodiode event | |
| previous_fsr <- fsr_events %>% | |
| dplyr::filter(frame_number < photo_frame) %>% | |
| slice_max(frame_number, n = 1) | |
| if (nrow(previous_fsr) > 0) { | |
| latencies[i] <- photo_frame - previous_fsr$frame_number | |
| } else { | |
| latencies[i] <- NA | |
| } | |
| } | |
| return(latencies) | |
| } | |
| # Function to process a single data file | |
| process_data_file <- function(file_path, data_name) { | |
| cat("Processing:", data_name, "\n") | |
| # Read the data | |
| bela_data <- read_csv(file_path, show_col_types = FALSE) | |
| # Filter FSR events (input events) | |
| fsr_events <- bela_data %>% | |
| dplyr::filter(pin_number == 0, is_start_pin == 1, trigger_active == 1) %>% | |
| arrange(frame_number) | |
| # Filter photodiode events (display events) | |
| photodiode_events <- bela_data %>% | |
| dplyr::filter(pin_number == 1, is_start_pin == 0, trigger_active == 1) %>% | |
| arrange(frame_number) | |
| # Calculate latencies in frames | |
| latency_frames <- calculate_latencies(fsr_events, photodiode_events) | |
| # Convert to milliseconds (44.1 kHz = 44100 samples per second) | |
| sampling_rate <- 44100 | |
| latency_ms <- latency_frames / sampling_rate * 1000 | |
| # Remove NA values and create data frame | |
| valid_latencies <- latency_ms[!is.na(latency_ms)] | |
| if (length(valid_latencies) > 0) { | |
| return(data.frame( | |
| latency_ms = valid_latencies, | |
| source = data_name | |
| )) | |
| } else { | |
| cat("Warning: No valid latencies found for", data_name, "\n") | |
| return(data.frame(latency_ms = numeric(0), source = character(0))) | |
| } | |
| } | |
| # Process all data files | |
| all_latency_data <- map_dfr(data_sources, function(source) { | |
| process_data_file(source$file_path, source$name) | |
| }) | |
| summary_stats <- all_latency_data %>% | |
| group_by(source) %>% | |
| summarise( | |
| n_events = n(), | |
| mean_ms = round(mean(latency_ms), 2), | |
| median_ms = round(median(latency_ms), 2), | |
| min_ms = round(min(latency_ms), 2), | |
| max_ms = round(max(latency_ms), 2), | |
| sd_ms = round(sd(latency_ms), 2), | |
| .groups = 'drop' | |
| ) | |
| # order sources by lowest to highest mean latency | |
| summary_stats <- summary_stats %>% | |
| arrange(mean_ms) | |
| # set the levels of the source factor to make sure that the lowest latency source is first | |
| all_latency_data$source <- factor(all_latency_data$source, levels = summary_stats$source) | |
| # Create side-by-side violin plot | |
| latency_plot <- ggplot(all_latency_data, aes(x = source, y = latency_ms, fill = source)) + | |
| geom_violin(alpha = 0.7) + | |
| geom_boxplot(width = 0.2, fill = "white", alpha = 0.8) + | |
| labs( | |
| title = "Display Latency Distribution Comparison", | |
| subtitle = "FSR to Photodiode Response Time", | |
| x = "Data Source", | |
| y = "Latency (ms)" | |
| ) + | |
| theme_minimal() + | |
| theme( | |
| axis.text.x = element_text(angle = 45, hjust = 1), | |
| plot.title = element_text(hjust = 0.5), | |
| plot.subtitle = element_text(hjust = 0.5), | |
| legend.position = "none" | |
| ) + | |
| scale_fill_viridis_d() + | |
| scale_y_continuous(breaks = seq(0, max(all_latency_data$latency_ms, na.rm = TRUE), by = 5)) | |
| # Print summary statistics for each source | |
| cat("\n=== LATENCY SUMMARY BY SOURCE ===\n") | |
| print(summary_stats) | |
| # Display the plot | |
| print(latency_plot) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment