functiongenerator module

Function generator and means to define waveforms.

This module contains classes that provide function generator functionality to the ALPACA. In the Studio Classroom, the Agilent 33220a Function / Waveform Generator is used to this end. This module intends to mimic a subset of the functionality of that device using the ALPACA. The desired output is created by modulating (stepping) the output of the MCP 4822 Digital-to-Analog Coverter (DAC).

To create a waveform, one must first define it. This can be done using a number of classes implemented in this module, e.g. Sine, Triangle, and Square, for sine, square, and triangular (sawtooth) waves respectively.

Such a waveform, once created, needs to be created using the DAC. To do so, this module implements a function generator FuncGen. FuncGen is intended to be used in a with-statement, i.e. as a so-called context manager.

Example

A typical usage example, in which a sine wave is defined and created using the function generator is shown below. The output of the function generator defaults to the A channel of the DAC. At the same time, the output of the function generator is measured using the analog input pins of the ALPACA:

>>> import time
>>> import machine
>>> from functiongenerator import FuncGen, Sine
>>>
>>> a0 = machine.ADC(26)
>>>
>>> sine = Sine(Vmin=0, Vmax=1, freq=1)
>>> with FuncGen(sine): # Turn on the function generator and create a sine.
    for i in range(250): # Take 250 samples
        print("a0: ", a0.read_u16() * 5.0354e-05) # Print result in volts
        time.sleep(0.01) # Delay such that sampling occurs at 100 Hz

This can be made more consise by creating and directly passing the waveform:

>>> with FuncGen(Sine(Vmin=1, Vmax=1, freq=1)):
>>>     pass # Measurement, etc.
class functiongenerator.Arbitrary(voltages: ndarray, freq: float = None, period: float = None, unsafe=False, hold=False)

Bases: Waveform

An abritrarily shaped wave.

An aribitrarily shaped wave consisting of discrete voltages. Initilized using a sequence of voltages to be created at the output of the DAC.

voltages

An sequence of voltages that make up the waveform.

Type:

np.ndarray

freq

See functiongenerator.Waveform.freq.

Type:

float

unsafe

See functiongenerator.Waveform.unsafe.

Type:

bool

hold

See functiongenerator.Waveform.hold.

Type:

bool

Parameters:

Note

Using the function generator, the waveform will be created in the background, i.e. in parallel with whatever code is run inside the while. Similar functionality can also be created using dac.DAC.write(). in a for loop. When using the DAC directly this way, this operation happens in series with the other code. This method might be more suitable if some measurement has to be synchronized with the output of the DAC. To conviently get an array to put into dac.DAC.write(), consider turning a Waveform into an array using functiongenerator.Waveform.get_voltages().

Examples

A waveform consisting of three voltages stepping up, i.e. from 0 to 4 volts (and then back to 0 volts) in steps of 1 volt with a frequency of 100 milli-Hertz.

>>> steps = [0, 1, 2, 3, 4]
>>> wf = Arbitrary(steps, freq=0.1)

For the arbitrary waveform, it might often make more sense to define the period of the whole wave.

>>> wf = Arbitrary(steps, period=10)

This can also be expressed as the delay between two steps: >>> step_delay = 2 >>> wf = Arbitrary(steps, period=len(steps) * step_delay)

class functiongenerator.DC(V=None, hold=False, unsafe=False)

Bases: Waveform

A constant voltage.

A waveform consisting of a constant voltage, i.e. a DC voltage

v

DC voltage in milli-volts.

Type:

int

unsafe

See functiongenerator.Waveform.unsafe.

Type:

bool

hold

See functiongenerator.Waveform.hold.

Type:

bool

Parameters:

Examples

A waveform of a DC current at 1 volt:

>>> wf = DC(V=1)
class functiongenerator.FuncGen(waveform: Waveform, channel: str = 'A')

Bases: object

A class for a function generator.

A class that creates a function generator and starts it. Take a waveform as an input and starts a process that takes control of the MCP 4822 Digital-to-Analog Coverter (DAC) on the ALPACA. The DAC is continuously updated to approximate the requested waveform. Note that this happens in a thread that is entirely seperate from the main thread, meaning that code in the main thread can be exectued simultaneously. This is advantageous for most measuring setups, where you want to simultaneously create some signal using the function generator, which you then pass through a circuit of some sort, and measure using the analog pins on the ALPACA (the latter being the part run in the main thread). This parallel operation can be handled using with-statement, i.e. as a so-called context manager.

waveform

The waveform to be created by the function generator.

Type:

Waveform

dac_A

A boolean specifying if the DAC A channel is used (rather than DAC B).

Type:

bool

Parameters:
  • waveform (Waveform) – The waveform to be created by the function generator.

  • channel (str) – A string specifying which channel to use. For ‘a’ or ‘A’, channel A on the DAC is used. For ‘b’ or ‘B’, channel B is used. Defaults to ‘A’.

Note

Depending on the waveform, the maximum frequency of the function genetator is approximately 800 Hz. For very low frequencies, i.e. <100 milli-Hertz the function generator might also behave erratically.

Note

dac.DAC.write() is not indended to be used inside the function generator with context. Unexpected behavior may occur.

Example

A typical usage example, in which a sine wave is defined and created using the function generator is shown below. The output of the function generator defaults to the A channel of the DAC:

>>> from functiongenerator import FuncGen, Sine
>>> sine = Sine(Vmin=0, Vmax=1, freq=1)
>>> with FuncGen(sine): # Turn on the function generator and create a sine.
        pass # Do something, e.g. measure while the function generator is on.

This can be made more consise by creating and directly passing the waveform:

>>> with FuncGen(Sine(Vmin=1, Vmax=1, freq=1)):
    pass

The function generator defaults to the A-channel of the DAC. The B-channel can also be used instead. Note that it is not yet possible to use both channels simultaneously:

>>> with FuncGen(Sine(Vmin=1, Vmax=1, freq=1), channel='B'):
        pass
update(waveform: Waveform)
Parameters:

waveform

Examples

Change to entirely new waveform:

>>> import time
>>> import machine
>>> from functiongenerator import FuncGen, Sine
>>>
>>> a0 = machine.ADC(26)
>>>
>>> with FuncGen(Sine(Vpp=2, offset=1, freq=1)) as fg:
        for i in range(250):
            if not i == 50:
                fg.update(Triangle(Vpp=2, offset=1, freq=1))

            print(a0.read_u16()*5.0354e-05)
            time.sleep(0.01)
Frequency sweep::
>>> with FuncGen(Sine(Vpp=2, offset=1, freq=1)) as fg:
    freq = 1
    for i in range(250):
        if not i % 50:
            freq += 1
            fg.update(Sine(Vpp=2, offset=1, freq=freq))

print(a0.read_u16()*5.0354e-05) time.sleep(0.01)

class functiongenerator.Sine(**kwargs)

Bases: Waveform

A sine wave.

A sine wave consisting of discrete voltages. Can be initilized using a set of parameters that fully define the wave. For more details, consult the documentation of the super class functiongenerator.Waveform.

v_min

See functiongenerator.Waveform.v_min.

Type:

int

v_max

See functiongenerator.Waveform.v_max.

Type:

int

freq

See functiongenerator.Waveform.freq.

Type:

float

unsafe

See functiongenerator.Waveform.unsafe.

Type:

bool

hold

See functiongenerator.Waveform.hold.

Type:

bool

Parameters:
Raises:

ValueError – If no pair of parameters that fully defines the sine wave was given.

Examples

A sine wave with a mean voltage of 1.5 volts, an amplitude of 1.5 volts, and a frequency of 10 Hz and can be created in many ways. Using a mean voltage, an amplitude, and a frequency:

>>> wf = Sine(Vp=1.5, offset=1.5, freq=10)

For more information on various ways of defining a waveform, see: functiongenerator.Waveform.

class functiongenerator.Square(duty_cycle: float = 50, **kwargs)

Bases: Waveform

A square wave.

A square wave consisting of discrete voltages. Can be initialized using a set of parameters that fully define the wave. For more details, consult the documentation of the super class functiongenerator.Waveform. Can be used to create a square wave with a specified duty cycle.

v_min

See functiongenerator.Waveform.v_min.

Type:

int

v_max

See functiongenerator.Waveform.v_max.

Type:

int

freq

See functiongenerator.Waveform.freq.

Type:

float

duty_cycle

A floating point number between 0 and 100 specifying the fraction of time which the wave is at the maximum voltage.

Type:

float

unsafe

See functiongenerator.Waveform.unsafe.

Type:

bool

hold

See functiongenerator.Waveform.hold.

Type:

bool

Parameters:
Raises:

ValueError – If no pair of parameters that fully defines the square wave was given.

Note

If the amplitude of the waveform is irrelevant to the application, also consider using PWM on a digital pin instead, as this a much faster and more efficient implementation. PWM is however limited to the square wave between 0 and 3.3 V (the output values of the digital pins).

Examples

A square wave with a mean voltage of 1.5 volts, an amplitude of 1.5 volts, and a frequency of 10 Hz and can be created in many ways. Using a mean voltage, an amplitude, and a frequency:

>>> wf = Square(Vp=1.5, offset=1.5, freq=10)

The duty cycle of the square wave can be set from 0 to 100:

>>> wf = Square(Vp=1.5, offset=1.5, duty_cycle=33.3, freq=10)

For more information on various ways of defining a waveform, see: functiongenerator.Waveform.

class functiongenerator.Triangle(symmetry=50, **kwargs)

Bases: Waveform

A triangle wave.

A triangle wave consisting of discrete voltages. Can be initialized using a set of parameters that fully define the wave. For more details, consult the documentation of the super class functiongenerator.Waveform. Can be used to create a symmetric triangle wave, a sawtooth wave (in both orientations), and other asymetric triangle waves.

v_min

See functiongenerator.Waveform.v_min.

Type:

int

v_max

See functiongenerator.Waveform.v_max.

Type:

int

freq

See functiongenerator.Waveform.freq.

Type:

float

symmetry

A floating point number between 0 and 100. The skew of the triangle is determined by the percentile value of symmetry. If set to 50, the triangle is symmetric, i.e. the rise time is exactly equal to the fall time. When set to 100, a rising sawtooth wave is created. Conversely, when set to 0, a falling sawtooth wave is created. Intermediate values result in triangle waves with a difference between the rise and fall times.

Type:

float

unsafe

See functiongenerator.Waveform.unsafe.

Type:

bool

hold

See functiongenerator.Waveform.hold.

Type:

bool

Parameters:
Raises:

ValueError – If no pair of parameters that fully defines the triangle wave was given.

Examples

A symmetric triangle wave with a mean voltage of 1.5 volts, an amplitude of 1.5 volts, and a frequency of 10 Hz and can be created in many ways. Using a mean voltage, an amplitude, and a frequency:

>>> wf = Triangle(Vp=1.5, offset=1.5, freq=10)

A rising sawtooth wave can be created as follows:

>>> wf = Triangle(Vp=1.5, offset=1.5, symmetry=100, freq=10)

Likewise, a falling sawtooth wave can be created using:

>>> wf = Triangle(Vp=1.5, offset=1.5, symmetry=0, freq=10)

Other values for symmetry between 0 and 100 are also possible:

>>> wf = Triangle(Vp=1.5, offset=1.5, symmetry=33.3, freq=10)

For more information on various ways of defining a waveform, see: functiongenerator.Waveform.

class functiongenerator.Waveform(Vpp=None, Vp=None, Vmin=None, Vmax=None, offset=0, freq=None, period=None, unsafe=False, hold=False)

Bases: object

A generic waveform.

A generic waveform consisting of discrete voltages. Intended to make it convenient to use the function generator or to create sequences of voltages, like sine wave, square waves, or sawtooth waves. Can be initialized based on a set of parameters that fully define the wave.

For defining the amplitude of the waveform this can for example be a peak-to-peak voltage (Vpp) along with an offset voltage (offset), a peak-to-mean voltage (Vp) along with an offset voltage, or a minimum voltage (Vmin) along with a maximum voltage (Vmax).

The time characteristic of the waveform can be defined either as a frequency (freq) or as a period (period).

v_min

An integer expressing the minimum voltage of the waveform in milli-volts.

Type:

int

v_max

An integer expressing the maximum voltage of the waveform in milli-volts.

Type:

int

freq

A float indicating the frequency of the wave.

Type:

float

unsafe

A boolean indicating whether the waveform is allowed to exceed a maximum voltage of 3.3 volts (the maximum voltage of the analog inputs on the Raspberry Pi Pico on the ALPACA). Defaults to False.

Type:

bool

hold

A boolean indicating whether the current voltage of the waveform should be held when used for the function generator. Defaults to False, causing the DAC to shut off.

Type:

bool

Parameters:
  • Vpp (float) – The peak-to-peak voltage of waveform in volts.

  • Vp (float) – The peak-to-mean voltage of waveform in volts.

  • Vmin (float) – The minimum voltage of the waveform in volts.

  • Vmax (float) – The maximum voltage of the waveform in volts.

  • offset (float) – The offset voltage of the mean of the waveform in volts.

  • freq (float) – The frequency of the waveform in Hertz.

  • period (float) – The period of the waveform in seconds.

  • unsafe (bool) – A boolean indicating whether the waveform is allowed to exceed a maximum voltage of 3.3 volts (the maximum voltage of the analog inputs on the Raspberry Pi Pico on the ALPACA). Defaults to False.

  • hold (bool) – A boolean indicating whether the current voltage of the waveform should be held when used for the function generator. Defaults to False, causing the DAC to shut off after the function generator stops.

Raises:

ValueError – If no pair of parameters that fully defines the waveform was given.

Note

By default, the voltage range of the waveform is limited to 0-3.3 V, the range that is safe for input to the analog input pins of the Raspberry Pi Pico in the ALPACA. Voltages of a waveform that fall outside this range are clipped, i.e. set to either 0 or 3.3 V. This behavior can be turned off using the unsafe parameter, increasing the range of the waveform to that of the MCP 4822 DAC, i.e. to 0-4.096 V. Note that in both cases, this clipping will occur silently.

Note

A good example for when to set the hold paramater is when using the DAC Assistant on the ALPACA. If the DAC is connected to the DAC Assistant and turned off (0 V) after use, the voltage at the output of the assistant will shoot to a large negative voltage. This might be undesirable for a certain circuit connected to this output. By setting hold to True, the DAC will hold the voltage to whatever voltage the function generator happened to end on. Depending on the voltage range of the waveform, this can guarantee a desirable output at the DAC Assistant output.

Examples

A generic waveform with a mean voltage of 1.5 volts, an amplitude of 1.5 volts, and a frequency of 10 Hz and can be created in many ways. Using a mean voltage, an amplitude, and a frequency:

>>> wf = Waveform(Vp=1.5, offset=1.5, freq=10)

Using a mean voltage and a peak-to-peak voltage:

>>> wf = Waveform(Vpp=3, offset=1.5, freq=10)

Using a minimum and maximum voltage:

>>> wf = Waveform(Vmin=0, Vmax=3, freq=10)

Using a period rather than a frequency: >>> wf = Waveform(Vmin=0, Vmax=3, freq=0.1)

get_voltages(n: int) ndarray

Express the waveform as an array of voltages.

Create an array of N voltages from a waveform. One period of the waveform is sampled to N discrete points equally spaced in time.

Parameters:

n (int) – The number of voltages to which to convert the waveform.

Returns:

The waveform converted to an array of N voltages.

Return type:

np.ndarray

Note

The voltages obtained using this method will not undergo the clipping process described in the initilization method of functiongenerator.Waveform().

Note

This function is not defined for the generic functiongenerator.Waveform() class, but only works for its sub-classes, e.g. functiongenerator.Sine() and functiongenerator.Triangle().

Examples

Converting a generic waveform into an array using 100 discrete points.

>>> wf = Waveform(Vp=1.5, offset=1.5, freq=10)
>>> a = wf.get_voltages(100)
>>> print(len(a))
100