Recipes for using picoDAQ as a pulse generator

Outputting a sequence of pulses

To output a sequence of monophasic square pulses:

from picodaq import *
from picodaq.stimulus import Pulse, Train

pulse = Pulse(amplitude=1*V, duration=50*ms)
train = Train(pulse, pulseperiod=200*ms, pulsecount=10)
with AnalogOut(rate=50*kHz) as ao:
    ao[0].stimulus(train)
    ao.run()

This outputs 10 pulses with amplitude 1 V and duration 50 ms to ao0, at a repetition rate of 5 Hz (programmed here as a period of 200 ms).

Note that it is not necessary to specify the output channel in the AnalogOut constructor: the stimulus() call takes care of that.

Often it is desirable to visualize the programmed pulse train before actually sending it out:

import matplotlib.pyplot as plt
from picodaq.mockstim import mockstim

plandata = mockstim(train, rate=50*kHz)
plt.plot(plandata)
plt.xlabel('Sample #')
plt.ylabel('Planned output (V)')
_images/recipe_po0b.png

Because PicoDAQ can simultaneously record and stimulate, we can readily verify that the output is indeed as advertised, by running the following code while a BNC cable connects ao0 to ai0:

with AnalogOut(rate=50*kHz) as ao:
    with AnalogIn(channel=0) as ai:
        ao[0].stimulus(train)
        data, times = ai.read(2*s, times=True)

plt.plot(times, data, '.-', markersize=1, linewidth=0.1)
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
_images/recipe_po0c.png

(Note the single-sample overshoots as the hardware attempts to make the sharpest possible square waves.)

The sampling rate is only set once on the first stream to be opened (i.e., AnalogOut in the example above). Any secondary streams automatically use the same sampling rate.

The act of reading from the input stream (ai.read(...)) automatically starts the output. Almost the same effect could be obtained with

with AnalogOut(rate=50*kHz) as ao:
        with AnalogIn(channel=0) as ai:
            ao[0].stimulus(train)
            ao.run()
            data, times = ai.readall(times=True)

except that this causes the acquisition to end immediately after the end of the final pulse.

Outputting pulses of different shapes

PicoDAQ can output pulses of a variety of shapes. For instance, here is how to generate asymmetric biphasic pulses:

from picodaq import *
from picodaq.stimulus import Square, Train

pulse = Square(amplitude=5*V, duration=100*ms,
               amplitude2=-2.5*V, duration2=200*ms)
train = Train(pulse, pulseperiod=500*ms, pulsecount=4)
with AnalogOut(rate=50*kHz) as ao:
    ao[0].stimulus(train)
    ao.run()

Again, we can visualize the pulses before actually generating output:

import matplotlib.pyplot as plt
from picodaq.mockstim import mockstim

plandata = mockstim(train, rate=50*kHz)
plt.plot(plandata)
plt.xlabel('Sample #')
plt.ylabel('Planned output (V)')
_images/recipe_po2b.png

Outputting a sequence of pulses of increasing amplitude

In the above examples, all of the pulse within a train were identical. PicoDAQ also allows us to vary them in a systematic fashion, for instance, increasing the amplitude by a constant amount for each subsequent pulse:

from picodaq import *
from picodaq.stimulus import Pulse, Train, Deltas

pulse = Pulse(amplitude=1*V, duration=50*ms)
delta = Deltas(amplitude=0.2*V)
train = Train(pulse, pulseperiod=200*ms, pulsecount=10,
              perpulse=delta)
with AnalogOut(rate=50*kHz) as ao:
    ao[0].stimulus(train)
    ao.run()

resulting in a stimulus sequence like this:

_images/recipe_po1b.png

Outputting a sequence of trains with increasing pulse counts

Instead of a single train, PicoDAQ also allows us to define stimuli that comprise a series of trains, in which case it is possible to systematically vary parameters between trains:

from picodaq import *
from picodaq.stimulus import Pulse, Train, Series, Deltas

pulse = Pulse(amplitude=1*V, duration=50*ms)
pudelta = Deltas(amplitude=0.2*V)
trdelta = Deltas(pulsecount=1, duration=-10*ms)
train = Train(pulse, pulseperiod=200*ms, pulsecount=2,
              perpulse=pudelta)
series = Series(train, trainperiod=1*s, traincount=3,
                pertrain=trdelta)
with AnalogOut(rate=50*kHz) as ao:
    ao[0].stimulus(train)
    ao.run()

resulting in a stimulus sequence like this:

_images/recipe_po3b.png

Note that both parameters pertaining to trains (e.g., the pulse count) and pertaining to the consituent pulses (e.g., the pulse duration) can be changed between trains. It is permissible to change a parameter both on a per-train and a per-pulse basis, in which case the changes are applied cumulatively.

Delaying the start of stimulation

Whether applying a single pulse, a train, or a series of trains, the start of stimulation may be delayed relative to the start of the run using the delay parameter to stimulus(...):

from picodaq import *
from picodaq.stimulus import Pulse, Train

pulse = Pulse(amplitude=1*V, duration=50*ms)
train = Train(pulse, pulseperiod=200*ms, pulsecount=10)
with AnalogOut(rate=10*kHz) as ao:
    ao[0].stimulus(train, delay=500*ms)
    ao.run()

The same delay parameter can also be passed to mockstim(...):

import matplotlib.pyplot as plt
from picodaq.mockstim import mockstim

plandata = mockstim(train, rate=10*kHz, delay=500*ms)
plt.plot(plandata)
plt.xlabel('Sample #')
plt.ylabel('Planned output (V)')
_images/recipe_po4b.png

Outputting TTL pulses to a digital output

Unlike analog outputs, which admit a variety of pulse shapes, digital outputs only admit a single shape, TTL:

from picodaq import *
from picodaq.stimulus import TTL, Train

pulse = TTL(duration=50*ms)
train = Train(pulse, pulseperiod=200*ms, pulsecount=10)
with DigitalOut(rate=50*kHz) as do:
    do[0].stimulus(train)
    do.run()
_images/recipe_po5b.png

Trains and series of trains can be constructed for digital outputs in the exact same way as for analog outputs, except that there are fewer pulse shape parameters that can be varied through Deltas.