/** * This function implements the task Habituation */ IEnumerator StartExperiment() { int trialCnt = 0; // Request to the start the imaging session (if available) sensorScript.StartRecording(); // The experiment continues until the user does not explicitly request for termination while (((trialCnt < numberOfTrials) || numberOfTrials == -1) && (!quitApplication)) { // A new recording element is created. All the following instructions regarding `dataInfo' are meant to collect data during the experiment DataCollector.Element dataInfo = dc.NewElem(); dataInfo.TrialStart = DateTime.Now; // The sensorScript is set to initialize a new trial sensorScript.InitNewTrial(); if (isWhiteNoiseEnabled) // If the white noise is enabled { // Calculate the duration for the white noise float wn = whiteNoiseDuration; if (isWhiteNoiseRandom) { wn *= UnityEngine.Random.Range(minWhiteNoiseDuration, maxWhiteNoiseDuration); } // Request to the sensor script to start the white noise sensorScript.Tone(ToneFrequency.WhiteNoiseStart); dataInfo.WhiteNoiseOn = DateTime.Now; if (useLaser && laserEvent == HFLaserEvent.TrialStart) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.LaserStartTime = DateTime.Now; dataInfo.IsLaserActivated = true; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } // Wait for the amount of time chosen while (wn > 0) { wn -= Time.deltaTime; yield return(0); } // Request to the sensor script to stop the white noise sensorScript.Tone(ToneFrequency.WhiteNoiseStop); dataInfo.WhiteNoiseOff = DateTime.Now; } // Wait for the amount of time chosen in the GUI between the white noise and the cue yield return(new WaitForSecondsRealtime(wnCueDelay)); int cueType = UnityEngine.Random.Range(0, numberOfTargets); dataInfo.VisualCueType = HFTargetConfigurator.shared.GetVisualStimIndex(cueType); dataInfo.AuditoryCueType = HFTargetConfigurator.shared.GetAudioStimIndex(cueType); dataInfo.OutcomeProbability = HFTargetConfigurator.shared.GetOutcomeProbability(cueType); cueTrials[cueType]++; // Set the target size in the variable `actualTargetSize' float actualTargetSize = targetSize; if (!isTargetSizeFixed) { actualTargetSize *= UnityEngine.Random.Range(0.4f, 1.1f); //min size = 0.6; max size = 1.65 //Note: it should be always higher than the mouse indicator size } dataInfo.TargetSize = actualTargetSize; // Set size and position and spawn a new target Vector3 spawnPosition; Quaternion spawnRotation; float timeoutCounter = HFTargetConfigurator.shared.GetDuration(cueType); int posIdx = 0; if (extendedScreen) { posIdx = UnityEngine.Random.Range(0, 3); } if (flat) { spawnPosition = new Vector3(gridPositions[posIdx].Item1, gridPositions[posIdx].Item2, gridPositions[posIdx].Item3); spawnRotation = new Quaternion(); dataInfo.TargetPos.X = gridPositions[posIdx].Item1; dataInfo.TargetPos.Y = gridPositions[posIdx].Item2; dataInfo.TargetPos.Z = gridPositions[posIdx].Item3; } else { spawnPosition = new Vector3(250f, 1.6f, 250f); spawnRotation = Quaternion.Euler(gridPositions[posIdx].Item1, gridPositions[posIdx].Item2, gridPositions[posIdx].Item3); dataInfo.TargetPos.Pitch = gridPositions[posIdx].Item2; dataInfo.TargetPos.Yaw = gridPositions[posIdx].Item1; dataInfo.TargetPos.Roll = gridPositions[posIdx].Item3; } if (HFTargetConfigurator.shared.GetSoundBefore(cueType)) { // Preallocation of the target targetSpawned = Instantiate(Target, spawnPosition, spawnRotation); targetSpawned.GetComponentInChildren <Renderer>().enabled = false; // Arrives here if for that cue the order should be audio -> delay -> video sensorScript.Tone(GetFrequency(HFTargetConfigurator.shared.GetAudioStimIndex(cueType))); dataInfo.AuditoryOn = DateTime.Now; // Activate laser stimulation iff: a) user requested to use laser, b) the event attached is "auditory on", c) the audio stimulus is not disabled if (useLaser && (laserEvent == HFLaserEvent.AuditoryOn) && (!(HFTargetConfigurator.shared.GetAudioStimIndex(cueType) == 0))) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.LaserStartTime = DateTime.Now; dataInfo.IsLaserActivated = true; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } yield return(new WaitForSecondsRealtime(HFTargetConfigurator.shared.GetDelays(cueType))); targetSpawned.GetComponentInChildren <Renderer>().material = GetMaterial(HFTargetConfigurator.shared.GetVisualStimIndex(cueType)); targetSpawned.GetComponentInChildren <Renderer>().enabled = !(HFTargetConfigurator.shared.GetVisualStimIndex(cueType) == 0); if (isVisualStimEnabled) { targetSpawned.transform.localScale = new Vector3(targetSize, targetSize, 1.0f); } dataInfo.VisualTargetOnTime = DateTime.Now; // Activate laser stimulation iff: a) user requested to use laser, b) the event attached is "visual on", c) the visual stimulus is not disabled if (useLaser && (laserEvent == HFLaserEvent.VisualTargetOn) && (!(HFTargetConfigurator.shared.GetVisualStimIndex(cueType) == 0))) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.LaserStartTime = DateTime.Now; dataInfo.IsLaserActivated = true; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } } else { // Arrives here if for that cue the order should be video -> delay -> audio targetSpawned = Instantiate(Target, spawnPosition, spawnRotation); targetSpawned.GetComponentInChildren <Renderer>().enabled = !(HFTargetConfigurator.shared.GetVisualStimIndex(cueType) == 0); targetSpawned.GetComponentInChildren <Renderer>().material = GetMaterial(HFTargetConfigurator.shared.GetVisualStimIndex(cueType)); if (isVisualStimEnabled) { targetSpawned.transform.localScale = new Vector3(targetSize, targetSize, 1.0f); } dataInfo.VisualTargetOnTime = DateTime.Now; // Activate laser stimulation iff: a) user requested to use laser, b) the event attached is "target on", c) the visual stimulus is not disabled if (useLaser) { if ((laserEvent == HFLaserEvent.VisualTargetOn) && (!(HFTargetConfigurator.shared.GetVisualStimIndex(cueType) == 0))) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.LaserStartTime = DateTime.Now; dataInfo.IsLaserActivated = true; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } } yield return(new WaitForSecondsRealtime(HFTargetConfigurator.shared.GetDelays(cueType))); sensorScript.Tone(GetFrequency(HFTargetConfigurator.shared.GetAudioStimIndex(cueType))); dataInfo.AuditoryOn = DateTime.Now; // Activate laser stimulation iff: a) user requested to use laser, b) the event attached is "auditory on", c) the audio stimulus is not disabled if (useLaser && (laserEvent == HFLaserEvent.AuditoryOn) && (!(HFTargetConfigurator.shared.GetAudioStimIndex(cueType) == 0))) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.LaserStartTime = DateTime.Now; dataInfo.IsLaserActivated = true; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } } // Set a destroy timer equals to 1 second. This duration is not adjustable, because should be coordinated with the sound (that is embedded in the arduino script). Destroy(targetSpawned, timeoutCounter); // Wait for the cue to disappear while (timeoutCounter > 0) { timeoutCounter -= Time.deltaTime; yield return(0); } dataInfo.TargetDisappearingTime = DateTime.Now; if (useLaser && (laserEvent == HFLaserEvent.VisualTargetOff)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.LaserStartTime = DateTime.Now; dataInfo.IsLaserActivated = true; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } // Wait for the amount of time chosen in the GUI between the white noise and the cue yield return(new WaitForSecondsRealtime(cueRewardDelay)); dataInfo.CueOutcomeDelay = cueRewardDelay; if (HFTargetConfigurator.shared.GetPumpType(cueType) == 0) { dataInfo.OutcomeType = false; } else { dataInfo.OutcomeType = true; } // Procedure of online calculation of the ratio. if ((cueRewardedTrials[cueType] / cueTrials[cueType]) < HFTargetConfigurator.shared.GetOutcomeProbability(cueType)) // If the trial should be rewarded { cueRewardedTrials[cueType]++; dataInfo.IsOutcomeDelivered = true; // Request to the sensor script to play the `Success' or `Missed' tone and to deliver the reward/punishment if (HFTargetConfigurator.shared.GetPumpType(cueType) == 0) { sensorScript.Tone(ToneFrequency.SuccessTone); sensorScript.Reward(); dataInfo.OutcomeTime = DateTime.Now; // activate laser if it was requested on reward or rewardTone if (useLaser && (laserEvent == HFLaserEvent.Reward || laserEvent == HFLaserEvent.RewardToneOn || laserEvent == HFLaserEvent.Outcome || laserEvent == HFLaserEvent.OutcomeToneOn)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.LaserStartTime = DateTime.Now; dataInfo.IsLaserActivated = true; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } } else { sensorScript.Tone(ToneFrequency.MissedTone); sensorScript.Punishment(); dataInfo.OutcomeTime = DateTime.Now; // activate laser if it was requested on penalty or penaltyTone if (useLaser && (laserEvent == HFLaserEvent.Punishment || laserEvent == HFLaserEvent.PunishmentToneOn || laserEvent == HFLaserEvent.Outcome || laserEvent == HFLaserEvent.OutcomeToneOn)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.LaserStartTime = DateTime.Now; dataInfo.IsLaserActivated = true; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } } // Check if in the remaining time of the trial (aka ITI) the reward is consumed or not bool consumed = false; float iti = interTrialTime; if (isInterTrialTimeRandom) { iti = UnityEngine.Random.Range(minInterTrialTime, maxInterTrialTime); } dataInfo.ITIOn = DateTime.Now; while (iti > 0) { if (sensorScript.numberOfLicksAfterReward >= licksToReward) { consumed = true; } iti -= Time.deltaTime; yield return(0); } dataInfo.ITIOff = DateTime.Now; if (consumed) { // If a reward has been consumed, then request to the sensor script to send the signal of `Consumed' dataInfo.IsRewardConsumed = true; sensorScript.Consumed(); } } else // If the trial should NOT be rewarded { // Wait for the ITI dataInfo.ITIOn = DateTime.Now; if (isInterTrialTimeRandom) { yield return(new WaitForSecondsRealtime(UnityEngine.Random.Range(minInterTrialTime, maxInterTrialTime))); } else { yield return(new WaitForSecondsRealtime(interTrialTime)); } dataInfo.ITIOff = DateTime.Now; } // Save the licks timestamps and the head positions dataInfo.LicksTimeOptical = new List <DateTime>(sensorScript.licksTimeOptical); dataInfo.LicksTimeElectrical = new List <DateTime>(sensorScript.licksTimeElectrical); dataInfo.HeadPositions = new List <DataCollector.Position>(sensorScript.headPositions); dataInfo.TrialEnd = DateTime.Now; trialCnt++; } sensorScript.StopRecording(); Debug.Log("Task completed"); }
/** * This function implements the task Habituation */ IEnumerator StartExperiment() { int trialCnt = 0; // The collision time chosen is injected in the target script targetScript.CollisionDuration = collisionTime; // Request to the start the imaging session (if available) sensorScript.StartRecording(); // The experiment continues when: // the user has not explicitly requested to terminate AND // the current trial is less than the limit OR the user requested to have unlimited trials while (((trialCnt < numberOfTrials) || numberOfTrials == -1) && (!quitApplication)) { // A new recording element is created. All the following instructions regarding `dataInfo' are meant to collect data during the experiment DataCollector.Element dataInfo = dc.NewElem(); dataInfo.TrialStart = DateTime.Now; // The sensorScript is set to initialize a new trial sensorScript.InitNewTrial(); // Calculate the duration of the white noise float wn = whiteNoiseDuration; if (!isTargetSizeFixed) { wn *= UnityEngine.Random.Range(minWhiteNoiseDuration, maxWhiteNoiseDuration); } // Request to the sensor script to start the white noise sensorScript.Tone(ToneFrequency.WhiteNoiseStart); dataInfo.WhiteNoiseOn = DateTime.Now; if (useLaser && (laserEvent == VGOLaserEvent.TrialStart)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.IsLaserActivated = true; dataInfo.LaserStartTime = DateTime.Now; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } // Wait for the amount of time chosen in GUI for the white noise while (wn > 0) { wn -= Time.deltaTime; yield return(0); } // Request to the sensor script to stop the white noise sensorScript.Tone(ToneFrequency.WhiteNoiseStop); dataInfo.WhiteNoiseOff = DateTime.Now; dataInfo.VisualTargetOnTime = DateTime.Now; if (useLaser && (laserEvent == VGOLaserEvent.TargetOn)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.IsLaserActivated = true; dataInfo.LaserStartTime = DateTime.Now; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } // Set the target size in the variable `actualTargetSize' float actualTargetSize = targetSize; if (!isTargetSizeFixed) { actualTargetSize *= UnityEngine.Random.Range(0.4f, 1.1f); //min size = 0.6; max size = 1.65 //Note: it should be always higher than the mouse indicator size } dataInfo.TargetSize = actualTargetSize; // Reset the `sucess variable' success = 0; float timeoutCounter = targetTimeout; // Set size and position and spawn a new target int gridPosIdx = UnityEngine.Random.Range(0, gridPositions.Length); Quaternion spawnRotation = Quaternion.Euler(gridPositions[gridPosIdx].Item2, gridPositions[gridPosIdx].Item1, gridPositions[gridPosIdx].Item3); dataInfo.TargetPos.Yaw = gridPositions[gridPosIdx].Item1; dataInfo.TargetPos.Pitch = gridPositions[gridPosIdx].Item2; dataInfo.TargetPos.Roll = gridPositions[gridPosIdx].Item3; dataInfo.TargetPos.X = 250f; dataInfo.TargetPos.Y = 1.6f; dataInfo.TargetPos.Z = 250f; Target.transform.localScale = new Vector3(actualTargetSize, actualTargetSize, 1); clone = Instantiate(Target, new Vector3(250f, 1.6f, 250f), spawnRotation); // Find and set the delegate to the target, in order to handle *here* if a collision occurs. clone.GetComponentInChildren <VGOTarget>().Delegate = this; // Set a destroy timer equals to the chose timeoutTarget. If the target has already been deallocated before its timeout, this function has no effect. Destroy(clone, targetTimeout); // Wait for at maximum the targetTimeout while ((timeoutCounter > 0) && (success != 1)) { timeoutCounter -= Time.deltaTime; yield return(0); } // Either for the timeout or for the collision, at this point, the target does not exist anymore. dataInfo.TargetDisappearingTime = DateTime.Now; if (useLaser && (laserEvent == VGOLaserEvent.TargetOff)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.IsLaserActivated = true; dataInfo.LaserStartTime = DateTime.Now; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } if (success == 1) // If there was a collision { dataInfo.ToneRewardDelay = targetRewardDelay; // Request to the sensor script to play the `Success' tone sensorScript.Tone(ToneFrequency.SuccessTone); if (useLaser && (laserEvent == VGOLaserEvent.RewardToneOn)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.IsLaserActivated = true; dataInfo.LaserStartTime = DateTime.Now; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } // Wait for the amount of time indicated from the setting `targetRewardDelay' yield return(new WaitForSecondsRealtime(targetRewardDelay)); // Save the current rewarded tile (if any) if (rewardTile != -1) { dataInfo.RewardedTile = new DataCollector.Position { Yaw = gridPositions[rewardTile].Item1, Pitch = gridPositions[rewardTile].Item2, Roll = gridPositions[rewardTile].Item3 } } ; if ((rewardTile == -1) || (gridPosIdx == rewardTile)) // If the reward should be delivered { // Request to the sensor script to deliver the reward sensorScript.Reward(); dataInfo.IsOutcomeDelivered = true; dataInfo.OutcomeTime = DateTime.Now; if (useLaser && (laserEvent == VGOLaserEvent.Reward)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.IsLaserActivated = true; dataInfo.LaserStartTime = DateTime.Now; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } Debug.Log("Reward delivered"); } // Check if in the remaining time of the trial (aka ITI-reward) the reward is consumed or not bool consumed = false; float iti = interTrialTimeReward; if (isInterTrialTimeRewardRandom) { iti = UnityEngine.Random.Range(minInterTrialTimeReward, maxInterTrialTimeReward); } dataInfo.ITIOn = DateTime.Now; while (iti > 0) { if (sensorScript.numberOfLicksAfterReward >= licksToReward) { consumed = true; } iti -= Time.deltaTime; yield return(0); } dataInfo.ITIOff = DateTime.Now; if (consumed) { // If a reward has been consumed, then request to the sensor script to send the signal of `Consumed' sensorScript.Consumed(); dataInfo.IsRewardConsumed = true; } } else // If there was NOT a collision { // Request to the sensor script to play the `Missed' tone sensorScript.Tone(ToneFrequency.MissedTone); if (useLaser && (laserEvent == VGOLaserEvent.MissToneOn)) { if (fractionOfTrialsStimulatedMask[trialCnt]) { sensorScript.TriggerLaser(); dataInfo.IsLaserActivated = true; dataInfo.LaserStartTime = DateTime.Now; dataInfo.fractionOfStimulatedTrials = fractionOfTrialsStimulated; } } // Wait for the ITI-miss dataInfo.ITIOn = DateTime.Now; if (isInterTrialTimeMissRandom) { yield return(new WaitForSecondsRealtime(UnityEngine.Random.Range(minInterTrialTimeMiss, maxInterTrialTimeMiss))); } else { yield return(new WaitForSecondsRealtime(interTrialTimeMiss)); } dataInfo.ITIOff = DateTime.Now; } // Save the licks timestamps and the head positions dataInfo.LicksTimeOptical = new List <DateTime>(sensorScript.licksTimeOptical); dataInfo.LicksTimeElectrical = new List <DateTime>(sensorScript.licksTimeElectrical); dataInfo.HeadPositions = new List <DataCollector.Position>(sensorScript.headPositions); dataInfo.TrialEnd = DateTime.Now; rewardTile = rewardTileNext; trialCnt++; } // Stop the imaging session (if available) sensorScript.StopRecording(); Debug.Log("Training completed"); }