from __future__ import division
from builtins import range
from math import fabs, cos, sin
from pyfabil.plugins.firmwareblock import FirmwareBlock
from pyfabil.base.definitions import *
import logging
import time
__author__ = 'cbelli'
[docs]
class TpmTestGenerator(FirmwareBlock):
""" Test signal generator
The test generator produces up to 4 signals. Each signal has an associated
amplitude, that is initialized to 0 and stored internally. Specifying
an amplitude of -1 (default) uses the internally stored value
Methods set_tone, set_pulse_frequency, enable_prdg set the parameters
for the two tones, the pulse generator and the pseudorandom white noise
Each ADC channel has a settable delay, in steps of 1 sample and
range of -123 to 127 samples. Delay is applied after the generator
and is positive for increased delay.
test_generator.set_tone(dds, frequency, ampl=-1.0, phase=0.0)
dds = [0 1] DDS number
frequency: in Hz
ampl: -1 to keep existing, [0.0:1.0] with respect to maximum
phase: in turns
test_generator.set_delay(delay)
delay: array[16] of [-123 to 127] in samples
test_generator.disable_prdg()
test_generator.enable_prdg(ampl=-1.0)
ampl: -1 to keep existing, [0.0:1.0] with respect to maximum
test_generator.set_pulse_frequency(freq_code, ampl=-1)
freq_code: [0 to 7] selects # of pulses per frame (0.925925 kHz)
0=18 ppf, 1=12, 2=8, 3=6, 4=4, 5=3, 6=2, 7=1,
ampl: -1 to keep existing, [0.0:1.0] with respect to maximum
test_generator.channel_select(channel_select)
channel_select: int16 with 1 bit per channel
"""
@compatibleboards(BoardMake.TpmBoard, BoardMake.Tpm16Board)
@friendlyname('test_generator')
@maxinstances(2)
def __init__(self, board, fsample=800e6, **kwargs):
""" TpmTestGenerator initialiser
:param board: Pointer to board instance
"""
super(TpmTestGenerator, self).__init__(board)
if 'device' not in list(kwargs.keys()):
raise PluginError("TpmTestGenerator: Require a node instance")
self._device = kwargs['device']
if self._device == Device.FPGA_1:
self._device = 'fpga1'
elif self._device == Device.FPGA_2:
self._device = 'fpga2'
else:
raise PluginError("TpmTestGenerator: Invalid device %s" % self._device)
# sampling frequency
self.fsample = fsample
# Cable delay
self.delay = [0] * 16
# Generator parameters
self.gain = [0] * 4
############################################################################
[docs]
def initialise(self):
""" Initialise Test Generator"""
# Load Delay
self.board['%s.test_generator.delay_0' % self._device] = self.delay
# Disable test input on all signals
self.board['%s.test_generator.channel_select' % self._device] = 0
# Set DDS frequency and phase to zero
self.set_tone(0, 0.0, 0.0)
self.set_tone(1, 0.0, 0.0)
# Disable PRDG, set pulse frequency to minimum, gains to 0, to minimize power
self.disable_prdg()
self.set_pulse_frequency(7, 0.0)
self.set_delay(16 * [0])
return True
############################################################################
# Set tone frequency for DDS 0 and 1
############################################################################
[docs]
def set_tone(self, dds, frequency, ampl=-1.0, phase=0.0, timestamp=None):
""" Set frequency, amplitude and phase for specified DDS
Frequency in Hz, amplitude normalized to maximum (32 units), phase in turns
If amplitude is negative, use last specified one """
freq_hw = int(round(frequency / self.fsample * 2 ** 31))
phase_hw = int(round(phase * 2 ** 16))
if (phase_hw < 0) | (phase_hw >= 2 ** 16):
phase_hw = 0
if ampl < 0:
ampl_hw = self.gain[dds & 1]
else:
ampl_hw = int(round(ampl * 255))
if ampl_hw > 255:
ampl_hw = 255
# Disable autoload while programming
self.board['%s.test_generator.control.load_dds0' % self._device] = 0
self.board['%s.test_generator.control.load_dds1' % self._device] = 0
self.board['%s.test_generator.control.trig_req' % self._device] = 0
self.board['%s.test_generator.control.trig_force' % self._device] = 0
if dds == 0:
self.gain[0] = ampl_hw
self.board['%s.test_generator.frequency_0' % self._device] = freq_hw
self.board['%s.test_generator.phase_0' % self._device] = phase_hw
self.board['%s.test_generator.gain.dds0' % self._device] = ampl_hw
if dds == 1:
self.gain[1] = ampl_hw
self.board['%s.test_generator.frequency_1' % self._device] = freq_hw
self.board['%s.test_generator.phase_1' % self._device] = phase_hw
self.board['%s.test_generator.gain.dds1' % self._device] = ampl_hw
# Synchronize the load operation if requested
# If no timestamp is specified, load immediately
if timestamp is None:
self.board['%s.test_generator.control.load_dds%d' % (self._device, dds)] = 1
self.board['%s.test_generator.control.trig_force' % self._device] = 1
self.board['%s.test_generator.control.trig_force' % self._device] = 0
else:
self.board['%s.test_generator.control.load_dds%d' % (self._device, dds)] = 1
self.board['%s.test_generator.timestamp_req' % self._device] = timestamp
self.board['%s.test_generator.control.trig_req' % self._device] = 1
return True
############################################################################
# Set coarse zenith delay
# Delay specified in integer samples, nominal is 0, range [-124:127:]
############################################################################
[docs]
def set_delay(self, delay):
""" Set delay for input ADC streams
Delay in samples, positive delay adds delay to the signal stream """
delay_hw = [0]*len(delay)
for i in range(len(delay)):
del1 = delay[i] + 128
if del1 > 255:
del1 = 255
if del1 < 4:
del1 = 4
delay_hw[i] = del1
self.delay[i] = del1 - 128
self.board['%s.test_generator.delay_0' % self._device] = delay_hw
return True
############################################################################
# Disables and enables pseudo random signal generator
# Saves current setting of gain
############################################################################
[docs]
def disable_prdg(self):
self.board['%s.test_generator.control.load_prdg' % self._device] = 0
self.gain[2] = self.board['%s.test_generator.gain.prdg' % self._device]
self.board['%s.test_generator.gain.prdg' % self._device] = 0
self.board['%s.test_generator.control.load_prdg' % self._device] = 1
self.board['%s.test_generator.control.trig_force' % self._device] = 1
self.board['%s.test_generator.control.trig_force' % self._device] = 0
return True
############################################################################
[docs]
def enable_prdg(self, ampl=-1.0, timestamp=None):
if ampl < 0:
ampl_hw = self.gain[2] # restore old gain
else:
ampl_hw = int(round(ampl * 255))
if ampl_hw > 255:
ampl_hw = 255
self.gain[2] = ampl_hw
self.board['%s.test_generator.gain.prdg' % self._device] = ampl_hw
self.board['%s.test_generator.control.load_prdg' % self._device] = 0
self.board['%s.test_generator.control.trig_force' % self._device] = 0
self.board['%s.test_generator.control.trig_req' % self._device] = 0
if timestamp == None:
self.board['%s.test_generator.control.load_prdg' % self._device] = 1
self.board['%s.test_generator.control.trig_force' % self._device] = 1
self.board['%s.test_generator.control.trig_force' % self._device] = 0
else:
self.board['%s.test_generator.control.load_prdg' % self._device] = 1
self.board['%s.test_generator.timestamp_req' % self._device] = timestamp
self.board['%s.test_generator.control.trig_req' % self._device] = 1
return True
############################################################################
# Sets pulse generator repetition rate
# Codes 0-7 correspond to period (samples) and frequency spacing of
# 0: 48 16.666667 MHz
# 1: 72 11.111111 MHz
# 2: 108 7.407407 MHz
# 3: 144 5.555555 MHz
# 4: 216 3.703704 MHz
# 5: 288 2.777778 MHz
# 6: 432 1.851852 MHz
# 7: 864 0.925925 MHz
#
############################################################################
[docs]
def set_pulse_frequency(self, freq_code, ampl=-1):
""" Sets pulse generator repetition rate
Codes 0-7 correspond to period (samples) and frequency spacing of
0: 48 16.666667 MHz
1: 72 11.111111 MHz
2: 108 7.407407 MHz
3: 144 5.555555 MHz
4: 216 3.703704 MHz
5: 288 2.777778 MHz
6: 432 1.851852 MHz
7: 864 0.925925 MHz """
self.board['%s.test_generator.control.pulse_period' % self._device] = freq_code
if ampl < 0:
ampl_hw = self.gain[3]
else:
ampl_hw = int(round(ampl * 255))
if ampl_hw > 255:
ampl_hw = 255
self.gain[3] = ampl_hw
self.board['%s.test_generator.gain.pulse' % self._device] = ampl_hw
self.board['%s.test_generator.control.pulse_period' % self._device] = freq_code
return True
############################################################################
# Selects channels substitued with test generator output
# Input is 16 bit mask
############################################################################
[docs]
def channel_select(self, channel_select):
self.board['%s.test_generator.channel_select' % self._device] = channel_select
return
[docs]
def status_check(self):
logging.info("TpmTestGenerator : Checking status")
return Status.OK
[docs]
def clean_up(self):
""" Perform cleanup
:return: Success
"""
logging.info("TpmTestGenerator : Cleaning up")
return True