Biofeedback

See how physiological data streaming in from BIOPAC acqknowledge can control an object in the scene. In this case a ball will change in size and color based on the physiological signal.  You may need to adjust the position of the ball down if using 360 media.

import vizshape, vizinfo, viztask

import sightlab_utils.sightlab as sl

from sightlab_utils.settings import *

from sightlab_utils import biopacndt

import sys

import struct

import time


sightlab = sl.SightLab(biopac=True)


sightlab.setTrialCount(1)


# Load the object that we shall regulate

ball = vizshape.addSphere(radius=0.2)

ball.setScale(1.2,1.2,1.2)

ball.alpha(0.5)

ball.color(viz.BLUE)

ball.setPosition(0, 1.5, 2)


sightlab.addSceneObject('ball', ball)


allchannels = [] # allchannels will hold all the data

BUFFER_LENGTH = 2 # length of the buffer in samples

UPDATE_FREQUENCY = 0.1 # in seconds, how frequenty we will update

# the onscreen information. If the value is 0.1, this means 10 times a second

SERVER = 0 # Keep track of whether the data server has been started. It can 


# Define thresholds and baseline

UPPER_THRESHOLD = 1.4  # Adjust based on typical amplitude data for deep breaths

LOWER_THRESHOLD = 1.0  # Adjust based on typical amplitude data for shallow breaths

BASELINE_Y = 1.5

MOVEMENT_SCALE = 0.5


def outputToScreen(index, frame, channelsInSlice):

    global allchannels, BUFFER_LENGTH

    

#    print(f"Received frame: {frame}")


    i = 0

    for each in allchannels:

        each.append(float(frame[i]))

#        movement_scale = 0.3  # Adjust this scale as needed


        # Adjust the ball position based on data deviation

        move = (frame[i] - 0.5) * MOVEMENT_SCALE

        new_y = BASELINE_Y + move

        ball.setPosition((0, new_y, 2))


        # Change color based on breath depth

        if new_y > UPPER_THRESHOLD:

            ball.color(viz.GREEN)  # Good deep breath

        elif new_y < LOWER_THRESHOLD:

            ball.color(viz.RED)    # Too shallow breath

        else:

            ball.color(viz.BLUE)   # Normal breath

            

#        print(f"Ball Y position: {new_y}")  # Print new Y position for debugging


        if len(each) > BUFFER_LENGTH:

            each.pop(0)

        i += 1


acqServer = biopacndt.AcqNdtQuickConnect()


if acqServer.getDataConnectionMethod() != "single":

acqServer.changeDataConnectionMethod("single")

print("Data Connection Method Changed to: single")


enabledChannels = acqServer.DeliverAllEnabledChannels()

singleConnectPort = acqServer.getSingleConnectionModePort()

dataServer = biopacndt.AcqNdtDataServer(singleConnectPort, enabledChannels )

dataServer.RegisterCallback("OutputToScreen",outputToScreen)


for each in enabledChannels:

# Here we define the data buffer and fill it up with zeros up to the

# BUFFER_LENGTH so that we have some data to start with

temp_buffer = []

for k in range (0,BUFFER_LENGTH):

temp_buffer.append(0)


allchannels.append(temp_buffer)

print(allchannels)



def sightLabExperiment():

    global SERVER

    yield viztask.waitKeyDown(' ')

    yield sightlab.startTrial()

    

    if SERVER == 0:

        dataServer.Start()

        SERVER = 1

        print("Data server started.")

    

    yield viztask.waitKeyDown(' ')

    yield sightlab.endTrial()

    dataServer.Stop()

    SERVER = 0

    print("Data server stopped.")


viztask.schedule(sightlab.runExperiment)

viztask.schedule(sightLabExperiment)