Exemple #1
0
def test_noise_signal(f_samp):
    """
    Generate a white noise signal and a white noise with a fade-in and fade-out.
    Check the generated voltage with an oscilloscope.
    """
    try:
        gvs = GVS(max_voltage=1.0)
        gvs.connect(physical_channel_name="cDAQ1Mod1/ao0")
    except:
        return
    # white noise
    make_stim = GenStim(f_samp)
    make_stim.noise(10.0, 0.3)
    samples = make_stim.stim
    # gvs.write_to_channel(samples)

    # white noise with fade-in/fade-out
    make_stim.fade(f_samp * 3.0)
    faded_samples = make_stim.stim
    print("start galvanic stim")
    gvs.write_to_channel(faded_samples)
    print("end galvanic stim")
    gvs.quit()

    return samples, faded_samples
Exemple #2
0
def habituation_signal():
    """
    Generate a habituation signal with a slow ramp
    """
    amp = 2.0
    duration = 25.0
    f_samp = 1e3
    frequency = 1.0
    ramp_length = 10.0
    buffer_size = int(duration * f_samp)
    gvs = GVS(max_voltage=amp)
    timing = {"rate": f_samp, "samps_per_chan": buffer_size}
    connected = gvs.connect("cDAQ1Mod1/ao0", **timing)

    # step stimulus
    make_stim = GenStim(f_samp=f_samp)
    make_stim.step(duration, amp)
    make_stim.fade(f_samp * ramp_length)
    gvs_wave = make_stim.stim

    # make_stim = GenStim(f_samp=f_samp)
    # make_stim.sine(duration, amp, frequency)
    # gvs_wave = make_stim.stim

    if connected:
        print("start galvanic stim")
        gvs.write_to_channel(gvs_wave)
        print("end galvanic stim")
        gvs.quit()

    return gvs_wave
Exemple #3
0
def test_signal():
    """
    Generate a signal with an alternating step from 0 V to 1 V and to -1 V.
    Check the generated voltage with an oscilloscope.
    """
    gvs = GVS(max_voltage=3.0)
    connected = gvs.connect("cDAQ1Mod1/ao0")
    if connected:
        samples = np.concatenate((np.zeros(500), np.ones(1000), np.zeros(500)))
        samples = np.concatenate((samples, -samples, samples, -samples))
        gvs.write_to_channel(samples)
    gvs.quit()
Exemple #4
0
def test_signal():
    """
    Generate a signal with an alternating step from 0 V to 1 V and to -1 V.
    Check the generated voltage with an oscilloscope.
    """
    gvs = GVS(max_voltage=3.0)
    timing = {"rate": 1e3, "samps_per_chan": 8000}
    physical_channel_name = "cDAQ1Mod1/ao0"
    connected = gvs.connect(physical_channel_name, **timing)
    if connected:
        samples = np.concatenate((np.zeros(500), np.ones(1000), np.zeros(500)))
        samples = np.concatenate((samples, -samples, samples, -samples))
        gvs.write_to_channel(samples)
    gvs.quit()
Exemple #5
0
class TestNidaqConnection(unittest.TestCase):

    def test_connect_single_channel(self):
        self.gvs1 = GVS()
        physical_channel_name = "cDAQ1Mod1/ao0"
        timing = {"rate": 1e3, "samps_per_chan": 8000}
        connected = self.gvs1.connect(physical_channel_name, **timing)
        self.assertTrue(connected)
        self.gvs1.quit()

    def test_connect_two_channels(self):
        self.gvs2 = GVS()
        physical_channel_name = ["cDAQ1Mod1/ao0", "cDAQ1Mod1/ao1"]
        timing = {"rate": 1e3, "samps_per_chan": 8000}
        connected = self.gvs2.connect(physical_channel_name, **timing)
        self.assertTrue(connected)
        self.gvs2.quit()

    def test_connect_without_timing_args(self):
        self.gvs3 = GVS()
        physical_channel_name = "cDAQ1Mod1/ao0"
        connected = self.gvs3.connect(physical_channel_name)
        self.assertTrue(connected)
        self.gvs3.quit()
Exemple #6
0
def test_dual_channel():
    """
    Generate a signal with an alternating step from 0 V to 1 V and to -1 V
    and send it to two channels.
    Check the generated voltage with an oscilloscope.
    """
    gvs = GVS(max_voltage=3.0)
    timing = {"rate": 1e3, "samps_per_chan": 8000}
    physical_channel_name = ["cDAQ1Mod1/ao0", "cDAQ1Mod1/ao1"]
    connected = gvs.connect(physical_channel_name, **timing)
    if connected:
        samples = np.concatenate((np.zeros(500), np.ones(1000), np.zeros(500)))
        samples1 = np.concatenate((samples, -samples, samples, -samples))
        samples2 = np.concatenate((-samples, samples, -samples, samples))
        two_chan_samples = np.stack((samples1, samples2), axis=0)
        gvs.write_to_channel(two_chan_samples)
    gvs.quit()
Exemple #7
0
def habituation_signal():
    """
    Generate a habituation signal with a slow ramp
    """
    amp = 1.0
    duration = 25.0
    f_samp = 1e3
    buffer_size = int(duration * f_samp)
    gvs = GVS(max_voltage=amp)
    timing = {"rate": f_samp, "samps_per_chan": buffer_size}
    connected = gvs.connect("cDAQ1Mod1/ao0", **timing)
    if connected:
        # white noise with fade-in/fade-out
        make_stim = genStim(f_samp)
        make_stim.noise(25.0, amp)
        make_stim.fade(f_samp * 10.0)
        faded_samples = make_stim.stim
        print("start galvanic stim")
        gvs.write_to_channel(faded_samples)
        print("end galvanic stim")
    gvs.quit()
    return faded_samples
Exemple #8
0
                   start=(0, -200),
                   end=(0, 200),
                   lineWidth=6,
                   lineColor=(-0.84, -0.84, -0.84))
start_text = visual.TextStim(win=win,
                             text="press SPACE to start trial",
                             pos=(0.0, 0.0),
                             color=(-1, -1, 0.6),
                             units="pix",
                             height=40)

# GVS setup
buffer_size = int(duration_s * f_sampling)
timing = {"rate": f_sampling, "samps_per_chan": buffer_size}
gvs = GVS()
gvs.connect(physical_channel_name, **timing)

# create and send GVS signals
make_stim = GenStim(f_sampling)
for trial in range(n_trials):
    if stochastic_gvs:
        make_stim.noise(duration_s, current_mA)
    else:
        # positive current for even trials, negative current for uneven trials
        if (trial % 2) == 0:
            direction = 1
        else:
            direction = -1
        make_stim.stim = np.ones(int(duration_s * f_sampling))
        make_stim.stim = make_stim.stim * current_mA * direction
        make_stim.n_samp = len(make_stim.stim)
Exemple #9
0
class GVSHandler:
    def __init__(self, param_queue, status_queue, logging_queue, buffer_size):
        PHYSICAL_CHANNEL_NAME = "cDAQ1Mod1/ao0"
        SAMPLING_FREQ = 1e3

        # I/O queues
        self.param_queue = param_queue
        self.status_queue = status_queue
        self.logging_queue = logging_queue
        self.stimulus = None

        # set up logger
        worker = Worker(logging_queue, formatter, default_logging_level,
                        "GVSHandler")
        self.logger = worker.logger
        # second logger to pass to GVS object
        subworker = Worker(logging_queue, formatter, default_logging_level,
                           "GVS")
        self.sublogger = subworker.logger

        # GVS control object
        self.gvs = GVS(logger=self.sublogger)
        self.buffer_size = int(buffer_size)
        timing = {"rate": SAMPLING_FREQ, "samps_per_chan": self.buffer_size}
        connected = self.gvs.connect(PHYSICAL_CHANNEL_NAME, **timing)
        if connected:
            self.logger.info("NIDAQ connection established")
            self.status_queue.put({"connected": True})
        else:
            self.logger.info("NIDAQ connection failed")
            self.status_queue.put({"connected": False})

        # GVSHandler can't be a subclass of multiprocessing.Process, as the
        # GVS object contains ctypes pointers and can't be pickled.
        # GVSHandler's methods can't be accessed from the parent process.
        # As a workaround, the event loop is started by calling the run method
        # here at the end of the initialisation.
        self.run()

    def run(self):
        """
        Event loop that listens for queue input. Input of type *dict* is used
        for stimulus creation, input of type *int* is used to trigger onset of
        GVS stimulation. Input "STOP" to exit the method.
        This event loop is automatically started after a GVSHandler object
        is initialised.
        """
        while True:
            data = self.param_queue.get()
            if isinstance(data, str) and (data == "STOP"):
                quit_gvs = self.gvs.quit()
                if quit_gvs:
                    self.status_queue.put({"quit": True})
                else:
                    self.status_queue.put({"quit": False})
                break

            else:
                if isinstance(data, np.ndarray):
                    self.stimulus = data
                    if self.stimulus is None:
                        self.status_queue.put({"stim_created": False})
                    else:
                        self.status_queue.put({"stim_created": True})

                elif isinstance(data, bool) and (data is True):
                    self._send_stimulus()

                else:
                    self.logger.error("Incorrect input to GVSHandler parameter"
                                      " queue. Input must be a numpy array "
                                      "with samples, a boolean, or a "
                                      "string STOP to quit.")
                    self.status_queue.put({"stim_created": False})

    def _analog_feedback_loop(self, gvs_wave, start_end_blip_voltage=2.5):
        """
        Add a copy of the GVS signal to send to a second channel via the NIDAQ.
        The copy (but not the GVS signal) has a 2.5 V blip of a single sample
        at the start and the end, to signal the onset and end of the
        stimulation. In the signal that is sent to the GVS channel (here:
        channel A0), the first and last sample are zero.
        Also, an extra zero sample is added to the end of both signals,
        to reset the voltage to baseline.

        :param gvs_wave: GVS signal
        :param start_end_blip_voltage: voltage to give to first and last
        as a signal. Voltage should not be present in the rest of the waveform.
        :return: stacked signal, with second row being the original GVS signal,
        the first row being the copied signal with first and last sample
        changed to 2.5 V.
        """
        duplicate_wave = gvs_wave[:]
        # blip at start and end of copied GVS wave
        duplicate_wave[0] = start_end_blip_voltage
        duplicate_wave[-1] = -start_end_blip_voltage

        # add voltage reset (0 sample) at the end
        gvs_wave = np.append(gvs_wave, 0)
        duplicate_wave = np.append(duplicate_wave, 0)
        stimulus = np.stack((duplicate_wave, gvs_wave), axis=0)
        return stimulus

    def _send_stimulus(self):
        """
        Send the stimulus to the GVS channel, check whether all samples
        were successfully written
        """
        n_samples = None
        samps_written = 0

        # only try to send if there is a stimulus available
        if self.stimulus is not None:
            try:
                # send the GVS onset time to the main process
                t_start_gvs = time.time()
                self.status_queue.put({"t_start_gvs": t_start_gvs})
                samps_written = self.gvs.write_to_channel(
                    self.stimulus, reset_to_zero_volts=True)
                t_end_gvs = time.time()

                if self.stimulus.ndim == 1:
                    n_samples = len(self.stimulus)
                else:
                    n_samples = np.shape(self.stimulus)[1]

                # delete stimulus after sending, so that it can only be sent once
                self.stimulus = None
            except AttributeError as err:
                self.logger.error(
                    "Error: tried to send invalid stimulus to NIDAQ."
                    "\nNote that a stimulus instance can only be"
                    " sent once.\nAttributeError: {}".format(err))
            self.logger.info("GVS: {} samples written".format(samps_written))

        if n_samples == samps_written:
            self.status_queue.put({"stim_sent": True})
            self.status_queue.put({"t_end_gvs": t_end_gvs})
        else:
            self.status_queue.put({"stim_sent": False})
            self.status_queue.put({"t_end_gvs": time.time()})
Exemple #10
0
class GVSHandler():
    def __init__(self, param_queue, status_queue, logging_queue, buffer_size):
        PHYSICAL_CHANNEL_NAME = "cDAQ1Mod1/ao0"
        SAMPLING_FREQ = 1e3

        # I/O queues
        self.param_queue = param_queue
        self.status_queue = status_queue
        self.logging_queue = logging_queue

        # stimulus generation
        self.makeStim = GenStim(f_samp=SAMPLING_FREQ)
        self.stimulus = self.makeStim.stim

        # set up logger
        worker = Worker(logging_queue, formatter, default_logging_level,
                        "GVSHandler")
        self.logger = worker.logger
        # second logger to pass to GVS object
        subworker = Worker(logging_queue, formatter, default_logging_level,
                           "GVS")
        self.sublogger = subworker.logger

        # GVS control object
        self.gvs = GVS(logger=self.sublogger)
        self.buffer_size = int(buffer_size)
        timing = {"rate": SAMPLING_FREQ, "samps_per_chan": self.buffer_size}
        connected = self.gvs.connect(PHYSICAL_CHANNEL_NAME, **timing)
        if connected:
            self.logger.info("NIDAQ connection established")
            self.status_queue.put({"connected": True})
        else:
            self.logger.info("NIDAQ connection failed")
            self.status_queue.put({"connected": False})

        # GVSHandler can't be a subclass of multiprocessing.Process, as the
        # GVS object contains ctypes pointers and can't be pickled.
        # GVSHandler's methods can't be accessed from the parent process.
        # As a workaround, the event loop is started by calling the run method
        # here at the end of the initialisation.
        self.run()

    def run(self):
        """
        Event loop that listens for queue input. Input of type *dict* is used
        for stimulus creation, input of type *int* is used to trigger onset of
        GVS stimulation. Input "STOP" to exit the method.
        This event loop is automatically started after a GVSHandler object
        is initialised.
        """
        while True:
            data = self.param_queue.get()
            if data == "STOP":
                quitted = self.gvs.quit()
                if quitted:
                    self.status_queue.put({"quit": True})
                else:
                    self.status_queue.put({"quit": False})
                break

            else:
                if type(data).__name__ == "dict":
                    self._create_stimulus(params=data)

                elif (type(data).__name__ == "bool") & (data is True):
                    self._send_stimulus()

                else:
                    self.logger.error("Incorrect input to GVSHandler parameter"
                                      " queue. Input must be a dict with "
                                      "parameters specified in GVS.py, a "
                                      "boolean, or a string STOP to quit.")
                    self.status_queue.put({"stim_created": False})

    def _create_stimulus(self, params=dict):
        """
        Create stimulus array with parameters defined in *params*
        :param params: (dict)
        """
        if self._check_args(["duration", "amp"], params):
            options = deepcopy(params)
            del options["duration"]
            del options["amp"]
            if "fade_samples" in options:
                del options["fade_samples"]
            self.makeStim.noise(params["duration"], params["amp"], **options)
        else:
            self.status_queue.put({"stim_created": False})
            return

        if self._check_args(["fade_samples"], params):
            self.makeStim.fade(params["fade_samples"])

        self.stimulus = self.makeStim.stim
        self.status_queue.put({"stim_created": True})

    def _send_stimulus(self):
        """
        Send the stimulus to the GVS channel, check whether all samples
        were successfully written
        """
        n_samples = None
        samps_written = 0
        try:
            samps_written = self.gvs.write_to_channel(self.stimulus,
                                                      reset_to_zero_volts=True)
            n_samples = len(self.stimulus)
            # delete stimulus after sending, so that it can only be sent once
            self.stimulus = None
        except AttributeError as err:
            self.logger.error("Error: tried to send invalid stimulus to NIDAQ."
                              "\nNote that a stimulus instance can only be"
                              " sent once.\nAttributeError: {}".format(err))
        self.logger.info("GVS: {} samples written".format(samps_written))

        if n_samples == samps_written:
            self.status_queue.put({"stim_sent": True})
        else:
            self.status_queue.put({"stim_sent": False})

    def _check_args(self, keylist, check_dict):
        return all(key in check_dict for key in keylist)