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:
WaveformAn 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:
voltages (np.ndarray) – An sequence of voltages that make up the waveform.
freq (float) – See
functiongenerator.Waveform.period (float) – See
functiongenerator.Waveform.unsafe (bool) – See
functiongenerator.Waveform.hold (bool) – See
functiongenerator.Waveform.
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 intodac.DAC.write(), consider turning a Waveform into an array usingfunctiongenerator.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:
WaveformA 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:
V (float) – Constant voltage in volts.
unsafe (bool) – See
functiongenerator.Waveform.hold (bool) – See
functiongenerator.Waveform.
Examples
A waveform of a DC current at 1 volt:
>>> wf = DC(V=1)
- class functiongenerator.FuncGen(waveform: Waveform, channel: str = 'A')
Bases:
objectA 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.
- 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:
WaveformA 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:
Vpp (float) – See
functiongenerator.Waveform.Vp (float) – See
functiongenerator.Waveform.Vmin (float) – See
functiongenerator.Waveform.Vmax (float) – See
functiongenerator.Waveform.offset (float) – See
functiongenerator.Waveform.freq (float) – See
functiongenerator.Waveform.period (float) – See
functiongenerator.Waveform.unsafe (bool) – See
functiongenerator.Waveform.hold (bool) – See
functiongenerator.Waveform.
- 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:
WaveformA 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:
Vpp (float) – See
functiongenerator.Waveform.Vp (float) – See
functiongenerator.Waveform.Vmin (float) – See
functiongenerator.Waveform.Vmax (float) – See
functiongenerator.Waveform.offset (float) – See
functiongenerator.Waveform.duty_cycle (float) – A floating point number between 0 and 100 specifying the fraction of time which the wave is at the maximum voltage. Defaults to 50.
freq (float) – See
functiongenerator.Waveform.period (float) – See
functiongenerator.Waveform.unsafe (bool) – See
functiongenerator.Waveform.hold (bool) – See
functiongenerator.Waveform.
- 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:
WaveformA 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:
Vpp (float) – See
functiongenerator.Waveform.Vp (float) – See
functiongenerator.Waveform.Vmin (float) – See
functiongenerator.Waveform.Vmax (float) – See
functiongenerator.Waveform.offset (float) – See
functiongenerator.Waveform.symmetry (float) – 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. Defaults to 50.
freq (float) – See
functiongenerator.Waveform.period (float) – See
functiongenerator.Waveform.unsafe (bool) – See
functiongenerator.Waveform.hold (bool) – See
functiongenerator.Waveform.
- 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:
objectA 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()andfunctiongenerator.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