/**
     * 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");
    }
Ejemplo n.º 2
0
    /**
     * 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");
    }