Raycast sample frequency in Fixed Update

Hello there,
I am designing an eye-tracking / head-tracking application in VR with the Varjo Aero HMD in Unity with Unity Experiment Framework (UXF; see here) (for a bit of background on what I’m trying to achieve : The saccade main sequence revised: A fast and repeatable tool for oculomotor analysis | Behavior Research Methods).

I am sampling the eye-tracking data at 200 Hz as per the HMD specifications. For the head-tracking data, I’m casting a ray from the VR camera forward to get the hit point of the head position in the VR environment AND I adjusted the fixed timestep in Unity’s Time settings to control the physics update rate (to reach ~ 200 Hz),allowing me to sample data more frequently and that works - to an extent. The head-tracking system here relies on the tracker class from UXF : unity-experiment-framework/Assets/UXF/Scripts/Etc/Tracker.cs at master · immersivecognition/unity-experiment-framework · GitHub

The problem is that the head-tracking data is the same for every ~ 4 samples (see image below), suggesting that, while I’m collecting data at 200 Hz, the raycast data is updated at ~ 50 Hz.

My questions :

Is this because I’m doing something wrong or is it because I’m reaching some sort of limits I’m not aware of ? Is there by any chance a way to circumvent this ?

using System;
using System.Collections.Generic;
using UnityEngine;

using UXF;


    public class test_HeadTracker : Tracker
    {
   
        public Session session;
   
        public override string MeasurementDescriptor => "MS";
        public override IEnumerable<string> CustomHeader => new string[] {"DateTime","DateTime_ticks","Participant_ID", "Session_#", "Block_#", "Trial_#","head_x", "head_y","head_z", "pos_x", "pos_y", "pos_z", "ROI", "target_status", "Position" };
   
        public Camera vrCamera;
        public GameObject target;
   
        // Get the current block number
        private int blockNumber; // Add 1 to start from 1-based index
        private string blockNumberString;
   
        // Get the current trial number within the block
        private int trialNumber; // Add 1 to start from 1-based index
        private string trialNumberString;
   
        // Get the current trial number within the block
        private int sessionNumber; // Add 1 to start from 1-based index
        private string sessionNumberString;
   
        private long dateTime;
        private string formattedDateTime;
   
        private string participantID;
   
        protected override UXFDataRow GetCurrentValues()
        {
            var row = new UXFDataRow();
   
            RecordHeadData(row);
   
            return row;
        }
   
        void RecordHeadData(UXFDataRow row)
        {
            Ray ray = new Ray(vrCamera.transform.position, vrCamera.transform.forward);
            RaycastHit hit;
   
            // Get the current block number
            blockNumber = session.currentBlockNum; // Add 1 to start from 1-based index
            blockNumberString = blockNumber.ToString();
   
            // Get the current trial number within the block
            trialNumber = session.currentTrialNum; // Add 1 to start from 1-based index
            trialNumberString = trialNumber.ToString();
   
            // Get the current trial number within the block
            sessionNumber = session.number; // Add 1 to start from 1-based index
            sessionNumberString = sessionNumber.ToString();
   
            participantID = session.ppid;
   
            int ecc_pos = session.CurrentTrial.settings.GetInt("Position");
   
            // Perform a raycast and check if it hits an object
            if (Physics.Raycast(ray, out hit))
            {
                // Store the hit point in the HitPoint property
                Vector3 HitPoint = hit.point;
                String ROI = hit.collider.name;
                Vector3 cameraPosition = vrCamera.transform.position;
   
                dateTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
                formattedDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffff");
   
                row.Add(("DateTime", formattedDateTime));
                row.Add(("DateTime_ticks", dateTime));
                row.Add(("Participant_ID", participantID));
                row.Add(("Session_#", blockNumberString));
                row.Add(("Block_#", blockNumberString));
                row.Add(("Trial_#", trialNumberString));
                row.Add(("head_x", cameraPosition.x));
                row.Add(("head_y", cameraPosition.y));
                row.Add(("head_z", cameraPosition.z));
                row.Add(("pos_x", HitPoint.x));
                row.Add(("pos_y", HitPoint.y));
                row.Add(("pos_z", HitPoint.z));
                row.Add(("ROI", ROI));
                row.Add(("target_status", target.GetComponent<MeshRenderer>().enabled));
                row.Add(("Position", ecc_pos));
            }
            else
            {
                Vector3 HitPoint = Vector3.zero;
                String ROI = "";
   
                Vector3 cameraPosition = vrCamera.transform.position;
                dateTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
                formattedDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffff");
   
                row.Add(("DateTime", formattedDateTime));
                row.Add(("DateTime_ticks", dateTime));
                row.Add(("Participant_ID", participantID));
                row.Add(("Session_#", blockNumberString));
                row.Add(("Block_#", blockNumberString));
                row.Add(("Trial_#", trialNumberString));
                row.Add(("head_x", cameraPosition.x));
                row.Add(("head_y", cameraPosition.y));
                row.Add(("head_z", cameraPosition.z));
                row.Add(("pos_x", HitPoint.x));
                row.Add(("pos_y", HitPoint.y));
                row.Add(("pos_z", HitPoint.z));
                row.Add(("ROI", ROI));
                row.Add(("target_status", target.GetComponent<MeshRenderer>().enabled));
                row.Add(("Position", ecc_pos));
            }
        }
    }

It this an issue?
I do not think that it would be noticeable for the end user.
Setting the FixedUpdate to run at the framerate of the vr headset could be beneficial, but I am not sure if increasing it more will yield anything more responsive

I am trying to collect data through the raycasting and sample the head position quite frequently (> 100 Hz) - high sample rates is quite important for our applications. It doesn’t affect the game whatsoever; it’s merely a passive data collection tool