diff --git a/GHzDACs/.project b/GHzDACs/.project
deleted file mode 100644
index 8d3ed867..00000000
--- a/GHzDACs/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- GHzFPGA_python
-
-
-
-
-
- org.python.pydev.PyDevBuilder
-
-
-
-
-
- org.python.pydev.pythonNature
-
-
diff --git a/GHzDACs/.pydevproject b/GHzDACs/.pydevproject
deleted file mode 100644
index 793da044..00000000
--- a/GHzDACs/.pydevproject
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-Default
-python 2.6
-
diff --git a/GHzDACs/FPGA_simulation.py b/GHzDACs/FPGA_simulation.py
deleted file mode 100644
index 866b7173..00000000
--- a/GHzDACs/FPGA_simulation.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# Copyright (C) 2007 Daniel Sank
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-"""
-### BEGIN NODE INFO
-[info]
-name = FPGA Simulation
-version = 1.0.0
-description = Simulate ethernet communication with FPGA boards
-
-[startup]
-cmdline = %PYTHON% %FILE%
-timeout = 20
-
-[shutdown]
-message = 987654321
-timeout = 5
-### END NODE INFO
-"""
-
-from twisted.internet.defer import inlineCallbacks, returnValue
-
-from labrad.types import Error
-from labrad.server import LabradServer, setting
-
-import numpy as np
-
-DAC_SRAM_LEN = 10240 #words
-DAC_SRAM_PAGE_LEN = 256 #words
-DAC_SRAM_PAGES = DAC_SRAM_LEN/DAC_SRAM_PAGE_LEN
-DAC_SRAM_DTYPE = np.uint8
-
-class FPGAWrapper(object):
- def __init__(self):
- pass
-
-class DACWrapper(FPGAWrapper):
- """ Represents a GHzDAC board.
- ATTRIBUTES
- sram - numpy array representing the board's SRAM.
- each element is of type .
-
-"""
-### BEGIN NODE INFO
-[info]
-name = DAC Emulator
-version = 1.0
-description = A DAC emulator.
-
-[startup]
-cmdline = %PYTHON% %FILE%
-timeout = 20
-
-[shutdown]
-message = 987654321
-timeout = 20
-### END NODE INFO
-"""
-
-'''
-How to define DACs to be emulated:
--- In the registry, under >> Servers >> DAC Emulator
--- One folder for each DAC, with the DAC's name as folder name
--- Recognized keys (* = can be changed with server function)
- address *(MAC address)
- device (what ethernet device (NIC) number to emulate on)
- build (DAC build number)
- SRAM Length *(in bytes)
- plotting *(boolean--whether to plot SRAM on run.)
-'''
-
-# doo dee doop, just a dac emulator
-
-import time
-import sys
-import string
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from twisted.internet import defer, reactor, task
-from twisted.internet.defer import inlineCallbacks, returnValue
-from labrad.server import LabradServer, setting
-from labrad.devices import DeviceServer, DeviceWrapper
-
-import ethernet_spoofer as es
-
-REGISTRY_PATH = ['', 'Servers', 'DAC Emulator']
-
-DEFAULT_DEVICE = 0
-DEFAULT_MAC = '11:22:33:44:55:66'
-DEFAULT_BUILD = 0
-DEFAULT_SRAM_LENGTH = 10240
-
-def _convertTwosComplement(data):
- ''' convert from 14-bits-in-32-bit-unsigned
- to 14-bits-2s-complement-in-32-bit-unsigned. '''
- d = data.astype(np.int32)
- d[d > 2**13 - 1] = d[d > 2**13 - 1] - 2**14
- return d
-
-class DacEmulator (DeviceWrapper):
-
- def connect(self, *args, **kwargs):
- print "Creating emulator with %s" % str(kwargs)
- # set up spoofer
- address = kwargs.get("address", DEFAULT_MAC)
- device = kwargs.get("device", DEFAULT_DEVICE)
- self.spoof = es.EthernetSpoofer(address, device)
- # set up DAC
- self.build = kwargs.get("build", DEFAULT_BUILD)
- self.sram_length = kwargs.get("SRAM Length", DEFAULT_SRAM_LENGTH)
- self.initRegister()
- self.initSRAM()
-
- # other options
- self.plotting = kwargs.get("plotting", False)
-
- # run the loop
- self.loop = task.LoopingCall(self.next_packet)
- self.loopDone = self.loop.start(0.1)
-
- def shutdown(self):
- self.loop.stop()
- yield self.loopDone
- del self.loop
-
- def next_packet(self):
- ''' handle next packet '''
- packet = self.spoof.getPacket()
- if not packet:
- return
- if packet['length'] == 1026:
- # we have an SRAM write packet
- adrstart = 256*ord(packet['data'][0]) + \
- 256*256*ord(packet['data'][1])
- if adrstart > self.sram_length - 256:
- print "Bad SRAM packet: adrstart too big: %s" % adrstart
- return
- print "Writing SRAM at derp %s" % (adrstart / 256)
- self.sram[adrstart:adrstart+256] = \
- np.fromstring(packet['data'][2:], '> 14 & 0x3FFF # second 14 bits
- dacA, dacB = _convertTwosComplement(dacA), _convertTwosComplement(dacB)
- ax = plt.figure().add_subplot(111)
- ax.plot(dacA, 'b.')
- ax.plot(dacB, 'r.')
- ax.set_xlim(0, 10240)
- ax.set_ylim(-2**13 -1, 2**13-1)
- plt.show()
-
- def send(self, dest, data):
- return self.spoof.sendPacket(dest, data)
-
-class DacEmulatorServer(DeviceServer):
- name = "DAC Emulator"
- deviceName = "DAC Emulator"
- deviceWrapper = DacEmulator
-
- @inlineCallbacks
- def findDevices(self):
- '''Create DAC emulators.'''
- reg = self.client.registry
- yield reg.cd(REGISTRY_PATH)
- resp = yield reg.dir()
- names = resp[0].aslist
- devs = []
- for n in names:
- yield reg.cd(n)
- keys = yield reg.dir()
- keys = keys[1].aslist
- p = reg.packet()
- for k in keys:
- p.get(k, key=k)
- a = yield p.send()
- devs.append((n, [], dict([(k, a[k]) for k in keys])))
- reg.cd(1)
- returnValue(devs)
-
- @setting(10, 'Address', mac='s', returns='s')
- def address(self, c, mac=''):
- '''get/set the MAC address'''
- dev = self.selectedDevice(c)
- if mac:
- dev.spoof.setAddress(mac)
- return dev.spoof.mac
-
- @setting(11, 'Send', dest='s', data='s', returns='?')
- def send(self, c, dest, data):
- '''
- Send a packet, to destination mac "dest", with data "data". returns 0
- for success, error message otherwise
- '''
- return self.selectedDevice(c).send(dest, data)
-
- @setting(20, 'Plotting', on='b', returns='b')
- def plotting(self, c, on=None):
- ''' get / set plotting mode '''
- dev = self.selectedDevice(c)
- if on is not None:
- dev.plotting = on
- return dev.plotting
-
- @setting(21, "SRAM Length", len='w', returns='w')
- def sram_length(self, c, len=None):
- '''Get or set(!) the SRAM length. Note that this clears the SRAM.'''
- dev = self.selectedDevice(c)
- if len:
- dev.sram_length = len
- dev.initSRAM()
- return dev.sram_length
-
- @setting(22, "Plot SRAM")
- def plot_sram(self, c):
- '''Plots the SRAM.'''
- self.selectedDevice(c).plotSRAM()
-
-__server__ = DacEmulatorServer()
-
-if __name__ == '__main__':
- from labrad import util
- util.runServer(__server__)
diff --git a/GHzDACs/direct_ethernet_proxy.py b/GHzDACs/direct_ethernet_proxy.py
deleted file mode 100644
index a2c0eb55..00000000
--- a/GHzDACs/direct_ethernet_proxy.py
+++ /dev/null
@@ -1,372 +0,0 @@
-# Copyright (C) 2007 Daniel Sank
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-import random
-import time
-
-import numpy as np
-from twisted.internet import defer, reactor
-from twisted.internet.defer import inlineCallbacks, returnValue
-
-from labrad.server import LabradServer, setting
-
-
-class EthernetAdapter(object):
- """Proxy for an ethernet adapter."""
- def __init__(self, name, mac):
- self.name = name
- self.mac = mac
- self.listeners = []
-
- def send(self, pkt):
- """Send a packet on this adapter."""
- for listener in self.listeners:
- listener(pkt)
-
- def addListener(self, listener):
- """Add a listener to be called for each sent packet."""
- self.listeners.append(listener)
-
- def removeListener(self, listener):
- """Remove a listener from this adapter."""
- self.listeners.remove(listener)
-
-
-class LossyEthernetAdapter(EthernetAdapter):
- """Proxy for a lossy ethernet adapter that can drop packets."""
- def __init__(self, name, mac, pLoss=0.01):
- EthernetAdapter.__init__(self, name, mac)
- self.pLoss = pLoss
-
- def send(self, pkt):
- """Send a packet on this adapter."""
- for listener in listeners:
- if random.random() < self.pLoss:
- continue # simulate dropped packet
- listener(pkt)
-
-
-class EthernetListener(object):
- """Listens for packets on an adapter.
-
- Filters can be added which examine incoming packets and
- return a boolean result. Only if all filters match will
- a given packet be passed on.
- """
- def __init__(self, packetFunc):
- self.packetFunc = packetFunc
- self.listening = False
- self.filters = []
-
- def __call__(self, packet):
- if self.listening and all(filter(packet) for filter in self.filters):
- self.packetFunc(packet)
-
- def addFilter(self, filter):
- self.filters.append(filter)
-
-
-class DeferredBuffer(object):
- """Buffer for packets/triggers received in a given context."""
- def __init__(self):
- self.buf = []
- self.waiter = None
- self.waitCount = 0
-
- def put(self, packet):
- self.buf.append(packet)
- if self.waiter and len(self.buf) >= self.waitCount:
- d = self.waiter
- self.waiter = None
- d.callback(None)
-
- def collect(self, n=1, timeout=None):
- assert (self.waiter is None), 'already waiting'
- if len(self.buf) >= n:
- return defer.succeed()
- else:
- d = defer.Deferred()
- if timeout is not None:
- timeoutCall = reactor.callLater(timeout, d.errback, Exception('timeout'))
- d.addCallback(self._cancelTimeout, timeoutCall)
- self.waiter = d
- self.waitCount = n
- return d
-
- def _cancelTimeout(self, result, timeoutCall):
- if timeoutCall.active():
- timeoutCall.cancel()
- return result
-
- def get(self, n=1, timeout=None):
- def _get(result):
- pkts = self.buf[:n]
- self.buf = self.buf[n:]
- return pkts
- d = self.collect(n)
- d.addCallback(_get)
- return d
-
- def discard(self, n=1, timeout=None):
- def _discard(result):
- self.buf = self.buf[n:]
- d = self.collect(n)
- d.addCallback(_discard)
- return d
-
- def clear(self):
- self.buf = []
-
-
-def parseMac(mac):
- """Convert a string or tuple of words into a valid mac address."""
- if isinstance(mac, str):
- mac = tuple(int(s, 16) for s in mac.split(':'))
- return '%02X:%02X:%02X:%02X:%02X:%02X' % mac
-
-
-class DirectEthernetProxy(LabradServer):
- name = 'Direct Ethernet Proxy'
-
- def __init__(self, adapters=[]):
- LabradServer.__init__(self)
-
- # make a dictionary of adapters, indexable by id or name
- d = {}
- for i, adapter in enumerate(adapters):
- d[i] = d[adapter.name] = adapter
- self.adapters = d
-
- def initServer(self):
- pass
-
- def initContext(self, c):
- c['triggers'] = DeferredBuffer()
- c['buf'] = DeferredBuffer()
- c['timeout'] = None
- c['listener'] = EthernetListener(c['buf'].put)
- c['listening'] = False
- c['src'] = None
- c['dest'] = None
- c['typ'] = -1
-
- def expireContext(self, c):
- if c['listening']:
- c['adapter'].removeListener(c['listener'])
-
- def getAdapter(self, c):
- """Get the selected adapter in this context."""
- try:
- return c['adapter']
- except KeyError:
- raise Exception('Need to connect to an adapter')
-
-
- # adapters
-
- @setting(1, 'Adapters', returns='*(ws)')
- def adapters(self, c):
- """Retrieves a list of network adapters"""
- adapterList = sorted((id, a.name) for id, a in self.adapters)
- return adapterList
-
- @setting(2, 'Connect', key=['s', 'w'], returns='s')
- def connect(self, c, key):
- try:
- adapter = self.adapters[key]
- except KeyError:
- raise Exception('Adapter not found: %s' % key)
- if 'adapter' in c:
- c['adapter'].removeListener(listener)
- adapter.addListener(c['listener'])
- c['adapter'] = adapter
- return adapter.name
-
- @setting(3, 'Listen', returns='')
- def listen(self, c):
- """Starts listening for SRAM packets"""
- c['listener'].listening = True
-
-
- # packet control and buffering
-
- @setting(10, 'Timeout', t='v[s]', returns='')
- def timeout(self, c, t):
- c['timeout'] = float(t)
-
- @setting(11, 'Collect', num='w', returns='')
- def collect(self, c, num=1):
- yield c['buf'].collect(num, timeout=c['timeout'])
-
- @setting(12, 'Discard', num=['w'], returns='')
- def discard(self, c, num=1):
- yield c['buf'].discard(num, timeout=c['timeout'])
-
- @setting(13, 'Read', num=['w'], returns=['(ssis)', '*(ssis)'])
- def read(self, c, num=1):
- def toStr(pkt):
- src, dest, typ, data = pkt
- data = np.tostring(data)
- return (src, dest, typ, data)
- return self._read(c, num, toStr)
-
- @setting(14, 'Read as Words', num=['w'], returns=['(ssi*w)', '*(ssi*w)'])
- def read_as_words(self, c, num):
- def toWords(pkt):
- src, dest, typ, data = pkt
- data = np.fromstring(data, dtype='uint8').astype('uint32')
- return (src, dest, typ, data)
- return self._read(c, num, toWords)
-
- @inlineCallbacks
- def _read(self, c, num, func=None):
- pkts = yield c['buf'].read(num, timeout=c['timeout'])
- if func is None:
- pkts = [func(pkt) for pkt in pkts]
- if num == 1:
- returnValue(pkts[0])
- else:
- returnValue(pkts)
-
- @setting(15, 'Clear', returns='')
- def clear(self, c):
- c['buf'].clear()
-
-
- # writing packets
-
- @setting(20, 'Source MAC', mac=['s', 'wwwwww'], returns='s')
- def source_mac(self, c, mac=None):
- if mac is not None:
- mac = parseMac(mac)
- c['src'] = mac
- return mac
-
- @setting(21, 'Destination MAC', mac=['s', 'wwwwww'], returns='s')
- def destination_mac(self, c, mac=None):
- if mac is not None:
- mac = parseMac(mac)
- c['dest'] = mac
- return mac
-
- @setting(22, "Ether Type", typ='i', returns='')
- def ether_type(self, c, typ):
- c['typ'] = typ
-
- @setting(23, 'Write', data=['s', '*w'], returns='')
- def write(self, c, data):
- adapter = self.getAdapter(c)
- src, dest, typ = c['src'], c['dest'], c['typ']
- if src is None:
- src = adapter.mac
- if dest is None:
- raise Exception('no destination mac specified!')
- if isinstance(data, str):
- data = np.fromstring(data, dtype='uint8')
- else:
- data = data.asarray.astype('uint8')
- pkt = (src, dest, typ, data)
- adapter.send(pkt)
-
-
- # packet filters
-
- @setting(100, 'Require Source MAC', mac=['s', 'wwwwww'], returns='s')
- def require_source_mac(self, c, mac):
- mac = parseMac(mac)
- c['listener'].addFilter(lambda pkt: pkt[0] == mac)
- return mac
-
- @setting(101, 'Reject Source MAC', mac=['s', 'wwwwww'], returns='s')
- def reject_source_mac(self, c, mac):
- mac = parseMac(mac)
- c['listener'].addFilter(lambda pkt: pkt[0] != mac)
- return mac
-
- @setting(110, 'Require Destination MAC', mac=['s', 'wwwwww'], returns='s')
- def require_destination_mac(self, c, mac):
- mac = parseMac(mac)
- c['listener'].addFilter(lambda pkt: pkt[1] == mac)
- return mac
-
- @setting(111, 'Reject Destination MAC', mac=['s', 'wwwwww'], returns='s')
- def reject_destination_mac(self, c, mac):
- mac = parseMac(mac)
- c['listener'].addFilter(lambda pkt: pkt[1] != mac)
- return mac
-
- @setting(120, 'Require Length', length='w', returns='')
- def require_length(self, c, length):
- c['listener'].addFilter(lambda pkt: len(pkt[3]) == length)
-
- @setting(121, 'Reject Length', length='w', returns='')
- def reject_length(self, c, length):
- c['listener'].addFilter(lambda pkt: len(pkt[3]) != length)
-
- @setting(130, 'Require Ether Type', typ='i', returns='')
- def require_ether_type(self, c, typ):
- c['listener'].addFilter(lambda pkt: pkt[2] == typ)
-
- @setting(131, 'Reject Ether Type', typ='i', returns='')
- def reject_ether_type(self, c, typ):
- c['listener'].addFilter(lambda pkt: pkt[2] != typ)
-
- @setting(140, 'Require Content', offset='w', data=['s', '*w'], returns='')
- def require_content(self, c, offset, data):
- raise Exception('not implemented')
-
- @setting(141, 'Reject Content', offset='w', data=['s', '*w'], returns='')
- def reject_content(self, c, offset, data):
- raise Exception('not implemented')
-
-
- # triggers
-
- @setting(200, 'Send Trigger', context='ww', returns='')
- def send_trigger(self, c, context):
- # have to send this to another context
- # this next line is a bit of a hack that depends on internal labrad details
- # should probably use an external bus object, like the ethernet adapter itself
- other = self.contexts[context].data['triggers'].put('trigger from %s' % c.ID)
-
- @setting(201, 'Wait For Trigger', num='w', returns='v[s]: Elapsed wait time')
- def wait_for_trigger(self, c, num=1):
- start = time.time()
- yield c['triggers'].discard(num) # does the real direct ethernet server have timeouts here?
- end = time.time()
- returnValue(end - start)
-
-
-
-if __name__ == '__main__':
- from labrad import util
- import adc
- import dac
-
- # create ethernet
- adapter0 = EthernetAdapter('proxy0', '01:23:45:67:89:00')
- adapter1 = EthernetAdapter('proxy1', '01:23:45:67:89:01')
-
- # create devices
- dev00 = dac.DACProxy(0, adapter0)
- dev01 = dac.ADCProxy(1, adapter0)
- dev02 = dac.DACProxy(2, adapter0)
-
- dev10 = dac.DACProxy(0, adapter1)
- dev11 = dac.DACProxy(1, adapter1)
- dev12 = dac.ADCProxy(2, adapter1)
-
- server = DirectEthernetProxy([adapter0, adapter1])
- util.runServer(server)
diff --git a/GHzDACs/ethernet_spoofer.py b/GHzDACs/ethernet_spoofer.py
deleted file mode 100644
index e704c4e1..00000000
--- a/GHzDACs/ethernet_spoofer.py
+++ /dev/null
@@ -1,140 +0,0 @@
-"""
-ethernet_spoofer.py
-
-set of functions for listening and replying to ethernet packets, by MAC
-address.
-
-usage:
-
-spoofer = EthernetSpoofer('11:22:33:44:55:66')
-packet = None
-while packet is None:
- packet = spoofer.getPacket()
-print "From %s: %s" % (packet['src'], packet['data'])
-
-"""
-
-import winpcapy as wp
-
-class EthernetSpoofer(object):
- def __init__(self, address, device=0):
- '''
- Create a new spoofer.
-
- required: MAC address, in form 11:22:33:44:55:66
- optional: device number (i.e. NIC index), default=0
- '''
- self.adhandle = self.openDevice(device)
- self.setAddress(address)
-
- def openDevice(self, device):
- '''Open our ethernet device for listening'''
- # set up variables
- alldevs = wp.POINTER(wp.pcap_if_t)()
- errbuf = wp.create_string_buffer(wp.PCAP_ERRBUF_SIZE)
- # get all devices
- if (wp.pcap_findalldevs(wp.byref(alldevs), errbuf) == -1):
- raise RuntimeError("Error in pcap_findalldevs: %s\n" \
- % errbuf.value)
- # find our device
- d = alldevs
- # d is a linked list, but not wrapped as a python list. Therefore, to
- # get element number device we can't do d[device]. Instead we just do
- # .next as many times as need to arrive at the device^th element.
- # These elements are all pointers, so to finally get the actual object
- # we call d.contents.
- for i in range(device):
- d = d.contents.next
- d = d.contents
- # open our device
- adhandle = wp.pcap_open_live(d.name, 65536,
- wp.PCAP_OPENFLAG_PROMISCUOUS, 1000,
- errbuf)
- wp.pcap_freealldevs(alldevs)
- if (adhandle == None):
- raise RuntimeError("Unable to open adapter %s" % d.contents.name)
- return adhandle
-
- def setFilter(self):
- ''' set the mac address filter. '''
- program = wp.bpf_program()
- filter = "ether dst %s" % self.mac
- # no idea what optimize should be, couldn't find doc, saw 1 in example code
- # netmask: 0xffffff ??
- if wp.pcap_compile(self.adhandle, wp.byref(program), filter, 1, 0xffffff) < 0:
- raise RuntimeError("Failed to compile filter: %s" % filter)
- if wp.pcap_setfilter(self.adhandle, wp.byref(program)) < 0:
- raise RuntimeError("Failed to set filter: %s" % filter)
- wp.pcap_freecode(wp.byref(program))
-
- def setAddress(self, address):
- ''' change the MAC address '''
- self.mac = address
- self.setFilter()
-
- def getPacket(self, returnErrors=False):
- ''' returns the data from the next packet, or None, if no packet is received.
- if returnErrors, then return -1 for error (or -2 if EOF reached, should never
- happen)
- data is returned as dict:
- { "dest" : "11:22:33:44:55:66",
- "src" : "aa:bb:cc:dd:ee:ff",
- "length" : 12,
- "data" : "hello there!"
- }
- '''
- header = wp.POINTER(wp.pcap_pkthdr)()
- data = wp.POINTER(wp.c_ubyte)()
- r = wp.pcap_next_ex(self.adhandle, wp.byref(header), wp.byref(data))
- if r == 1:
- # process this packet
- pystr = wp.string_at(data, header.contents.len)
- # parse the header
- dest = self._macToString(pystr[0:6])
- src = self._macToString(pystr[6:12])
- length = ord(pystr[12])*256 + ord(pystr[13])
- return { "dest": dest, "src": src, "length": length, "data": pystr[14:] }
- elif r == 0:
- return None
- else:
- return r if returnErrors else None
-
- def sendPacket(self, destMac, data):
- ''' send a packet to destMat with given data.
- source mac is self.mac.
- returns 0 for success, otherwise returns error'''
- packet = self._macToData(destMac) # bytes 0-5: destination address
- packet += self._macToData() # 6-11: source address
- packet += chr(len(data) / 256) # 12, 13 are packet length (big endian?)
- packet += chr(len(data) % 256)
- packet += data
- packet_c = (wp.c_ubyte*len(packet))()
- for i in range(len(packet)):
- packet_c[i] = ord(packet[i])
- if wp.pcap_sendpacket(self.adhandle, packet_c, len(packet)) != 0:
- return wp.pcap_geterr(self.adhandle)
- else:
- return 0
-
- def _macToData(self, mac=None):
- ''' Take a MAC address in form "11:22:33:44:55:66"
- and read each byte as hex numbers, return as 6-byte string.
- if mac=None, use self.mac '''
- if mac is None:
- mac = self.mac
- bytes = ''.join([chr(int(x, 16)) for x in mac.split(':')])
- if len(bytes) != 6:
- raise ValueError("MAC address is not 6 bytes: %s" % mac)
- return bytes
-
- def _macToString(self, bytes):
- ''' inverse of _macToData: take a 6-byte string (of numbers)
- and return string of form "11:22:33:44:55:66. '''
- if len(bytes) != 6:
- raise ValueError("MAC address is not 6 bytes: %s" % bytes)
- return ':'.join(['%02X' % ord(x) for x in bytes])
-
- def __del__ (self):
- ''' closes the device handle. __del__ is a bit untrustworthy,
- should probably come up with a better way to do this. '''
- wp.pcap_close(self.adhandle)
\ No newline at end of file
diff --git a/GHzDACs/sequencer.py b/GHzDACs/sequencer.py
deleted file mode 100644
index abf5fc2c..00000000
--- a/GHzDACs/sequencer.py
+++ /dev/null
@@ -1,607 +0,0 @@
-# Copyright (C) 2007 Matthew Neeley
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-"""
-### BEGIN NODE INFO
-[info]
-name = Sequencer
-version = 1.0
-description = Talks to old DAC sequencer board
-
-[startup]
-cmdline = %PYTHON% %FILE%
-timeout = 20
-
-[shutdown]
-message = 987654321
-timeout = 20
-### END NODE INFO
-"""
-
-from labrad import types as T, util
-from labrad.devices import DeviceWrapper, DeviceServer
-from labrad.server import setting
-from twisted.python import log
-from twisted.internet import defer, reactor
-from twisted.internet.defer import inlineCallbacks, returnValue
-
-from array import array
-from datetime import datetime, timedelta
-from math import sin, cos
-
-#NUMRETRIES = 1
-SRAM_LEN = 7936
-MEM_LEN = 256
-BUILD = 1
-
-class FPGADevice(DeviceWrapper):
- @inlineCallbacks
- def connect(self, de, port, board, build):
- print 'connecting to: %s (build #%d)' % (boardMAC(board), build)
-
- self.server = de
- self.ctx = de.context()
- self.port = port
- self.board = board
- self.build = build
- self.MAC = boardMAC(board)
- self.serverName = de.name
-
- self.sram = [0L] * SRAM_LEN
- self.sramAddress = 0
- self.mem = [0L] * MEM_LEN
- self.seqTime = 0 # estimated sequence runtime in seconds
- self.DACclocks = 0
- self.timeout = T.Value(1000, 'ms')
-
- # set up the direct ethernet server for this board
- # in our own context
- p = de.packet()
- p.connect(port)
- p.require_length(70)
- p.destination_mac(self.MAC)
- p.require_source_mac(self.MAC)
- p.timeout(self.timeout)
- p.listen()
- yield p.send(context=self.ctx)
-
- @inlineCallbacks
- def sendRegisters(self, packet, asWords=True):
- """Send a register packet and readback answer."""
- # do we need to clear waiting packets first?
- p = self.server.packet()
- p.write(words2str(packet))
- p.read(1)
- ans = yield p.send(context=self.ctx)
- src, dst, eth, data = ans.read[0]
- if asWords:
- data = [ord(c) for c in data]
- returnValue(data)
-
- def sendRegistersNoReadback(self, packet):
- """Send a register packet but don't readback anything."""
- d = self.server.write(words2str(packet), context=self.ctx)
- return d.addCallback(lambda r: True)
-
- @inlineCallbacks
- def runSequence(self, slave, delay, reps, getTimingData=True):
- server, ctx = self.server, self.ctx
-
- pkt = [1, 3] + [0]*54
- #pkt[13:15] = reps & 0xFF, (reps >> 8) & 0xFF
- #pkt[43:45] = int(slave), int(delay)
- r = yield self.sendRegistersNoReadback(pkt)
-
- if not getTimingData:
- returnValue(r)
-
- # TODO: handle multiple timers per cycle
- npackets = reps/30
- totalTime = 2 * (self.seqTime * reps + 1)
- p = server.packet()
- p.timeout(T.Value(totalTime * 1000, 'ms'))
- p.read(npackets)
- ans = yield p.send(context=ctx)
- sdata = [ord(data[j*2+3]) + (ord(data[j*2+4]) << 8)
- for j in range(30)
- for src, dst, eth, data in ans.read]
- returnValue(sdata)
-
- @inlineCallbacks
- def sendSRAM(self, data):
- """Write SRAM data to the FPGA.
-
- The data is written at the position specified by self.sramAddress.
- Data is only written if it needs to be changed.
- """
- totallen = len(data)
- adr = startadr = (self.sramAddress + 255) & SRAM_LEN
- endadr = startadr + totallen
- current = self.sram[startadr:endadr]
- if data != current: # only send if the SRAM is new
- p = self.server.packet()
- while len(data) > 0:
- page, data = data[:256], data[256:]
- if len(page) < 256:
- page += [0]*(256-len(page))
- pkt = [(adr >> 8) & 31, 0] + \
- [(n >> j) & 255 for n in page for j in [0, 8, 16, 24]]
- p.write(words2str(pkt))
- adr += 256
- start = datetime.now()
- yield p.send(context=self.ctx)
- #print 'update time:', datetime.now() - start
- self.sram[startadr:endadr] = data
- self.sramAddress = endadr
- returnValue((startadr, endadr))
-
- @inlineCallbacks
- def sendMemory(self, data, cycles):
- """Write Memory data to the FPGA.
-
- At most one page of memory is written at address 0.
- Data is only written if it needs to be changed.
- """
- # try to estimate the time in seconds
- # to execute this memory sequence
- self.seqTime = sequenceTime(data)
-
- data = data[0:256] # only one page supported
- totallen = len(data)
- adr = startadr = 0
- endadr = startadr + totallen
- current = self.mem[startadr:endadr]
- if data != current: # only send if the MEM is new
- p = self.server.packet()
- while len(data) > 0:
- page, data = data[:256], data[256:]
- if len(page) < 256:
- page += [0]*(256-len(page))
- pkt = [cycles & 0xFF, (cycles >> 8) & 0xFF, (adr >> 8)] + \
- [(n >> j) & 0xFF for n in page for j in (0, 8, 16)]
- p.write(words2str(pkt))
- adr += 256
- yield p.send(context=self.ctx)
- self.mem[startadr:endadr] = data
- returnValue((startadr, endadr))
-
- @inlineCallbacks
- def runI2C(self, data):
- pkt = [0, 2] + [0]*54
- answer = []
- while data[:1] == [258]:
- data = data[1:]
-
- while len(data):
- cnt = min(8, len(data))
- i = 0
- while i < cnt:
- if data[i] == 258:
- cnt = i
- i += 1
-
- stopI2C, readwriteI2C, ackI2C = (256 >> cnt) & 255, 0, 0
- cur = 128
- for i in range(cnt):
- if data[i] in [256, 257]:
- readwriteI2C |= cur
- elif data[i] == 256:
- ackI2C |= cur
- elif data[i] < 256:
- pkt[12-i] = data[i]
- else:
- pkt[12-i] = 0
- cur >>= 1
-
- pkt[2:5] = stopI2C, readwriteI2C, ackI2C
-
- r = yield self.sendRegisters(pkt)
-
- for i in range(cnt):
- if data[i] in [256, 257]:
- answer += [r[61+cnt-i]]
-
- data = data[cnt:]
- while data[:1] == [258]:
- data = data[1:]
-
- returnValue(answer)
-
-
-class FPGAServer(DeviceServer):
- name = 'Sequencer'
- deviceWrapper = FPGADevice
- possibleLinks = []
-
- #retryStats = [0] * NUMRETRIES
- @inlineCallbacks
- def initServer(self):
- print 'loading config info...',
- yield self.loadConfigInfo()
- print 'done.'
- yield DeviceServer.initServer(self)
-
- @inlineCallbacks
- def loadConfigInfo(self):
- """Load configuration information from the registry."""
- reg = self.client.registry
- p = reg.packet()
- p.cd(['', 'Servers', 'GHz DACs'], True)
- p.get('sequencer', key='links')
- ans = yield p.send()
- self.possibleLinks = ans['links'] #[(, , port),...]
-
-
- def initContext(self, c):
- c.update(daisy_chain=[], start_delay=[])
-
- @inlineCallbacks
- def findDevices(self):
- cxn = self.client
- found = []
- print self.possibleLinks
- for name, server, port in self.possibleLinks:
- if server not in cxn.servers:
- continue
-
- print 'Checking %s...' % name
- de = cxn.servers[server]
- ctx = cxn.context()
- adapters = yield de.adapters(context=ctx)
- if len(adapters):
- ports, names = zip(*adapters)
- else:
- ports, names = [], []
- if port not in ports:
- continue
- #MAC = names[list(ports).index(port)][-17:]
-
- # make a list of the boards currently known
- skips = {}
- for dname in self.devices:
- dev = self.devices[dname]
- if dev.serverName == de.name and dev.port == port:
- skips[dev.board] = dev
- print 'skipping:', skips.keys()
-
- p = de.packet()
- p.connect(port)
- p.require_length(70)
- p.listen()
-
- # ping all boards
- for i in xrange(256):
- if i in skips:
- found.append(skips[i].name)
- else:
- p.destination_mac(boardMAC(i))
- p.write(words2str([0, 1] + [0] * 54))
- yield p.send(context=ctx)
-
- # get ID packets from all boards
- for i in xrange(256):
- try:
- ans = yield de.read(1, context=ctx)
- src, dst, eth, data = ans[0]
- board, build = int(src[-2:], 16), ord(data[51])
- if build != BUILD:
- continue
- devName = '%s FPGA %d' % (name, board)
- args = de, port, board, build
- found.append((devName, args))
- except T.Error:
- #print 'timeout', i
- pass # probably a timeout error
-
- # expire this context to stop listening
- yield cxn.manager.expire_context(de.ID, context=ctx)
- returnValue(found)
-
-
- @setting(20, 'SRAM Address', addr=['w'], returns=['w'])
- def sram_address(self, c, addr=None):
- """Sets the next SRAM address to be written to by SRAM."""
- dev = self.selectedDevice(c)
- if addr is None:
- addr = dev.sramAddress
- else:
- dev.sramAddress = addr
- return long(addr)
-
-
- @setting(21, 'SRAM', data=['*w: SRAM Words to be written'],
- returns=['(ww): Start address, Length'])
- def sram(self, c, data):
- """Writes data to the SRAM at the current starting address."""
- dev = self.selectedDevice(c)
- return dev.sendSRAM(data)
-
-
- @setting(31, 'Memory', data=['*w: Memory Words to be written'],
- returns=[''])
- def memory(self, c, data):
- """Writes data to the Memory at the current starting address."""
- dev = self.selectedDevice(c)
- c['mem'] = data
- #return dev.sendMemory(data)
-
-
- @setting(40, 'Run Sequence', reps=['w'], getTimingData=['b'],
- returns=['*2w', ''])
- def run_sequence(self, c, reps=30, getTimingData=True):
- """Executes a sequence on one or more boards."""
- # Round stats up to multiple of 30
- reps += 29
- reps -= reps % 30
-
- if len(c['daisy_chain']) != len(c['start_delay']):
- raise Exception('daisy_chain and start_delay must be same length.')
-
- if len(c['daisy_chain']):
- # run multiple boards, with first board as master
- devs = [self.getDevice(c, n) for n in c['daisy_chain']]
- delays = c['start_delay']
- else:
- # run the selected device only
- devs = [self.selectedDevice(c)]
- delays = [0]
- slaves = [i != 0 for i in range(len(devs))]
- devices = zip(devs, delays, slaves)
-
- devs[0].sendMemory(c['mem'], reps)
-
- # run boards in reverse order to ensure synchronization
- #print 'starting to run sequence.'
- start = datetime.now()
- attempts = [dev.runSequence(slave, delay, reps,
- getTimingData=getTimingData)
- for dev, delay, slave in reversed(devices)]
- #print 'trying on boards:', self.daisy_chain
- results = yield defer.DeferredList(attempts)
- #print 'runtime:', datetime.now() - start
- #print 'all boards done.'
- okay = True
- switches = []
- failures = []
- for dev, (success, result) in zip(devs, results):
- if success:
- switches.append(result)
- else:
- print 'Board %d timed out.' % dev.board
- failures.append(dev.board)
- okay = False
- if not okay:
- raise Exception('Boards %s timed out.' % failures)
- if getTimingData: # send data back in daisy chain order
- #print list(reversed(switches))
- returnValue(list(reversed(switches)))
-
- @setting(42, 'Daisy Chain', boards=['*s'], returns=['*s'])
- def daisy_chain(self, c, boards=None):
- """Set or get daisy chain board order.
-
- Set this to an empty list to run the selected board only.
- """
- if boards is None:
- boards = c['daisy_chain']
- else:
- c['daisy_chain'] = boards
- return boards
-
- @setting(43, 'Start Delay', delays=['*w'], returns=['*w'])
- def start_delay(self, c, delays=None):
- """Set start delays in ns for SRAM in the daisy chain.
-
- Must be the same length as daisy_chain for sequence to execute.
- """
- if delays is None:
- delays = c['start_delay']
- else:
- c['start_delay'] = delays
- return delays
-
-
- @setting(50, 'Debug Output', data=['(wwww)'], returns=[''])
- def debug_output(self, c, data):
- """Outputs data directly to the output bus."""
- dev = self.selectedDevice(c)
- pkt = [2, 1] + [0]*11
- for d in data:
- pkt += [d & 0xFF,
- (d >> 8) & 0xFF,
- (d >> 16) & 0xFF,
- (d >> 24) & 0xFF]
- pkt += [0]*16 + [0]*11
- yield dev.sendRegisters(pkt)
-
-
- @setting(51, 'Run SRAM', data=['*w'], loop=['b'], returns=['(ww)'])
- def run_sram(self, c, data, loop=False):
- """Loads data into the SRAM and executes.
-
- If loop is True, the sequence will be repeated forever,
- otherwise it will be executed just once. Sending
- an empty list of data will clear the SRAM.
- """
- dev = self.selectedDevice(c)
-
- pkt = [0, 1] + [0]*54
- yield dev.sendRegisters(pkt)
-
- if not len(data):
- returnValue((0, 0))
-
- if loop:
- # make sure data is at least 20 words long by repeating it
- data *= (20-1)/len(data) + 1
- hdr = 3
- else:
- # make sure data is at least 20 words long by repeating first value
- data += [data[0]] * (20-len(data))
- hdr = 4
-
- dev.sramAddress = 0
- r = yield dev.sendSRAM(data)
-
- encode = lambda a: [a & 0xFF, (a>>8) & 0xFF, (a>>16) & 0xFF]
- pkt = [hdr, 0] + [0]*11 + encode(r[0]) + encode(r[1]-1) + [0]*37
- yield dev.sendRegistersNoReadback(pkt)
- returnValue(r)
-
-
- @setting(100, 'I2C', data=['*w'], returns=['*w'])
- def i2c(self, c, data):
- """Runs an I2C Sequence
-
- The entries in the WordList to be sent have the following meaning:
- 0..255 : send this byte
- 256: read back one byte without acknowledging it
- 257: read back one byte with ACK
- 258: sent data and start new packet
- For each 256 or 257 entry in the WordList to be sent, the read-back byte is appended to the returned WordList.
- In other words: the length of the returned list is equal to the count of 256's and 257's in the sent list.
- """
- dev = self.selectedDevice(c)
- return dev.runI2C(data)
-
-
- @setting(110, 'LEDs', data=['w', '(bbbbbbbb)'], returns=['w'])
- def leds(self, c, data):
- """Sets the status of the 8 I2C LEDs."""
- dev = self.selectedDevice(c)
-
- if isinstance(data, tuple):
- # convert to a list of digits, and interpret as binary int
- data = long(''.join(str(int(b)) for b in data), 2)
-
- yield dev.runI2C([192 , 68, data & 255])
- returnValue(data)
-
-
- @setting(120, 'Reset Phasor', returns=['b: phase detector output'])
- def reset_phasor(self, c):
- """Resets the clock phasor."""
- dev = self.selectedDevice(c)
-
- pkt = [152, 0, 127, 0, 258, # set I to 0 deg
- 152, 34, 254, 0, 258, # set Q to 0 deg
- 112, 65, 258, # set enable bit high
- 112, 193, 258, # set reset high
- 112, 65, 258, # set reset low
- 112, 1, 258, # set enable low
- 113, 256] # read phase detector
-
- r = yield dev.runI2C(pkt)
- returnValue((r[0] & 1) > 0)
-
-
- @setting(121, 'Set Phasor',
- data=[': poll phase detector only',
- 'v[rad]: set angle (in rad, deg, \xF8, \', or ")'],
- returns=['b: phase detector output'])
- def set_phasor(self, c, data=None):
- """Sets the clock phasor angle and reads the phase detector bit."""
- dev = self.selectedDevice(c)
-
- if data is None:
- pkt = [112, 1, 258, 113, 256]
- else:
- sn = int(round(127 + 127*sin(data))) & 255
- cs = int(round(127 + 127*cos(data))) & 255
- pkt = [152, 0, sn, 0, 258,
- 152, 34, cs, 0, 258,
- 112, 1, 258, 113, 256]
-
- r = yield dev.runI2C(pkt)
- returnValue((r[0] & 1) > 0)
-
- def getCommand(self, cmds, chan):
- """Get a command from a dictionary of commands.
-
- Raises a helpful error message if the given channel is not allowed.
- """
- try:
- return cmds[chan]
- except:
- raise Exception("Allowed channels are %s." % sorted(cmds.keys()))
-
- @setting(130, 'Vout', chan=['s'], V=['v[V]'], returns=['w'])
- def vout(self, c, chan, V):
- """Sets the output voltage of any Vout channel, A, B, C or D."""
- cmd = self.getCommand({'A': 16, 'B': 18, 'C': 20, 'D': 22}, chan)
- dev = self.selectedDevice(c)
- val = int(max(min(round(V*0x3333), 0x10000), 0))
- pkt = [154, cmd, (val >> 8) & 0xFF, val & 0xFF]
- yield dev.runI2C(pkt)
- returnValue(val)
-
-
- @setting(135, 'Ain', returns=['v[V]'])
- def ain(self, c):
- """Reads the voltage on Ain."""
- dev = self.selectedDevice(c)
- pkt = [144, 0, 258, 145, 257, 256]
- r = yield dev.runI2C(pkt)
- returnValue(T.Value(((r[0]<<8) + r[1])/819.0, 'V'))
-
-
-# some helper methods
-
-def bistChecksum(data):
- bist = [0, 0]
- for i in range(0, len(data), 2):
- for j in range(2):
- if data[i+j] & 0x3FFF != 0:
- bist[j] = (((bist[j] << 1) & 0xFFFFFFFE) | ((bist[j] >> 31) & 1)) ^ ((data[i+j] ^ 0x3FFF) & 0x3FFF)
- return bist
-
-def boardMAC(board):
- """Get the MAC address of a board as a string."""
- return '00:01:CA:AA:00:' + ('0'+hex(int(board))[2:])[-2:].upper()
-
-def listify(data):
- return data if isinstance(data, list) else [data]
-
-def words2str(list):
- return array('B', list).tostring()
-
-def sequenceTime(sequence):
- """Conservative estimate of the length of a sequence in seconds."""
- cycles = sum([cmdTime(c) for c in sequence])
- return cycles * 40e-9
-
-def cmdTime(cmd):
- """A conservative estimate of the number of cycles a given command takes."""
- opcode = (cmd & 0xF00000) >> 20
- abcde = (cmd & 0x0FFFFF)
- xy = (cmd & 0x00FF00) >> 8
- ab = (cmd & 0x0000FF)
-
- if opcode in [0x0, 0x1, 0x2, 0x4, 0x8, 0xA]:
- return 1
- if opcode == 0xF:
- return 2
- if opcode == 0x3:
- return abcde + 1 # delay
- if opcode == 0xC:
- return 250*8 # maximum SRAM length is 8us
-
-
-
-__server__ = FPGAServer()
-
-if __name__ == '__main__':
- from labrad import util
- util.runServer(__server__)
diff --git a/GHz_DAC_bringup.py b/GHz_DAC_bringup.py
deleted file mode 100644
index 4247c82a..00000000
--- a/GHz_DAC_bringup.py
+++ /dev/null
@@ -1,291 +0,0 @@
-"""
-Version Info
-version = 3.0
-server: ghz_fpgas
-server version: 3.3.0
-"""
-
-# CHANGELOG:
-#
-# 2013 September 2013 - Daniel Sank
-#
-# Major code clean up and simplification
-#
-# 2011 November 4 - Jim Wenner
-#
-# Revised calls to ghz_fpga server to match v3.3.0 call signatures and outputs.
-# Incorporating usage of new bringup functions. Revised print outputs. Added
-# ability to bring up all devices on a board group.
-
-from __future__ import with_statement
-
-import random
-import time
-
-import labrad
-from math import sin, pi
-
-FPGA_SERVER = 'ghz_fpgas'
-DACS = ['A','B']
-NUM_TRIES = 2
-
-def bringupReportMessage(dac, data):
- """Build a report string from bringup data
-
- data is a dictionary of bringup results that should be built as follows:
- resp = fpga.dac_bringup(...)
- data = dict(resp[i]) (i=0 for DAC A, i=1 for DAC B)
- """
- report = ''
- #LVDS
- report += '\r\n'.join(['DAC %s LVDS parameters:'%dac,
- ' SD: %d'%data['lvdsSD'],
- ' Check: %d - What is this?'%data['lvdsCheck'],
- ' Plot MSD: ' + ''.join('_-'[i] for i in data['lvdsTiming'][1]),
- ' Plot MHD: ' + ''.join('_-'[i] for i in data['lvdsTiming'][2]),
- '', ''])
- #FIFO
- report += '\r\n'.join(['DAC %s FIFO Parameters:' %dac,
- ' FIFO calibration succeeded after %d tries'%data['fifoTries'],
- ''])
- if data['fifoSuccess']:
- report += '\r\n'.join([\
- ' FIFO PHOF: %d'%data['fifoPHOF'],
- ' Clk polarity: %d' %data['fifoClockPolarity'],
- ' FIFO counter: %d (should be 3)'%data['fifoCounter'],
- '', ''])
- else:
- report += '\r\n'.join(['FIFO failure', '', ''])
- #BIST
- report += '\r\n'.join(['DAC %s BIST:' %dac,
- ' Success: %s'%str(data['bistSuccess']),
- '', ''])
- return report
-
-def bringupBoard(fpga, board, printOutput=True, optimizeSD=False, sdVal=None,fullOutput=False):
- """Bringup a single FPGA board
-
- This is the main bringup routine. All other bringup routines should call
- this one.
-
- RETURNS
- (boardType, results, {'bist':bool, 'fifo':bool, 'lvds':bool})
-
- results maps DAC -> data where DAC is 'A' or 'B' and data is another dict
- that maps parameterName -> value. For a complete specifications of all
- parameterName,value pairs, see the documentation for dac_bringup setting
- in the GHz FPGA server.
- """
- fpga.select_device(board)
-
- if board in fpga.list_adcs():
- fpga.adc_bringup()
- if printOutput:
- print('')
- print('ADC bringup on %s complete.' %board)
- print('No feedback information available %s' %board)
- return ('ADC', None, {})
-
- elif board in fpga.list_dacs():
- if sdVal is None:
- resp = fpga.dac_bringup(optimizeSD)
- else:
- resp = fpga.dac_bringup(False, sdVal)
- results={}
- #pass/fail indicators, one entry per DAC, ie. DAC A and DAC B
- fifoOkay = []
- bistOkay = []
- lvdsOkay = []
- for dacdata in resp:
- dacDict = dict(dacdata)
- dac = dacDict.pop('dac')
- results[dac] = dacDict
-
- fifoOkay.append(dacDict['fifoSuccess'])
- bistOkay.append(dacDict['bistSuccess'])
- lvdsOkay.append(dacDict['lvdsSuccess'])
-
- if fullOutput:
- print(bringupReportMessage(dac, dacDict))
-
- if printOutput:
- if all(fifoOkay) and all(bistOkay):
- print('%s ok' %board)
- if not all(lvdsOkay):
- print('but LVDS warning')
- else:
- print('%s ---Bringup Failure---' %board)
-
- return ('DAC', results, {'bist':all(bistOkay), 'fifo':all(fifoOkay),
- 'lvds':all(lvdsOkay)})
-
- else:
- raise RuntimeError("Board type not recognized. Something BAD happened")
-
-def bringupBoards(fpga, boards, noisy=False):
- """Bringup a list of boards and return the Bist/FIFO and lvds success for
- each one.
-
- TODO:
- Put in code to keep track of retries and report this to the user
- """
- successes = {}
- triesDict = {}
- for board in boards:
- if noisy:
- print 'bringing up %s' %board
- #Temporarily set a value to False so while loop will run at least once
- allOk = False
- tries = 0
- #Try to bring up the board until it succeeds or we exceed maximum
- #number of tries.
- while tries < NUM_TRIES and not allOk:
- tries += 1
- triesDict[board] = tries
- boardType, result, successDict = bringupBoard(fpga, board,
- fullOutput=False,
- printOutput=True)
- allOk = all(successDict.values())
- successes[board] = successDict
- failures = []
- for board in boards:
- #Warn the user if a board took more than one try to bring up.
- if triesDict[board]>1:
- print 'WARNING: Board %s took %d tries to succeed' %(board,triesDict[board])
- #If this board failed, add it to the list of failures
- if not all(successes[board].values()):
- failures.append(board)
- if not failures:
- print 'All boards successful!'
- else:
- print 'The following boards failed:'
- for board in failures:
- print board
-
-def interactiveBringup(fpga, board):
- """
- """
- boardType,_,_ = bringupBoard(fpga, board,fullOutput=True)
-
- if boardType == 'DAC':
- ccset = 0
- while True:
- print
- print
- print 'Choose:'
- print
- print ' [1] : Output 0x0000s'
- print ' [2] : Output 0x1FFFs'
- print ' [3] : Output 0x2000s'
- print ' [4] : Output 0x3FFFs'
- print
- print ' [5] : Output 100MHz sine wave'
- print ' [6] : Output 200MHz sine wave'
- print ' [7] : Output 100MHz and 175MHz sine wave'
- print
- print ' Current Cross Controller Setting: %d' % ccset
- print ' [+] : Increase Cross Controller Adjustment by 1'
- print ' [-] : Decrease Cross Controller Adjustment by 1'
- print ' [*] : Increase Cross Controller Adjustment by 10'
- print ' [/] : Decrease Cross Controller Adjustment by 10'
- print
- print ' [I] : Reinitialize'
- print
- print ' [Q] : Quit'
-
- k = getChoice('1234567+-*/IQ')
-
- # run various debug sequences
- if k in '1234567':
- if k == '1': fpga.dac_debug_output(0xF0000000, 0, 0, 0)
- if k == '2': fpga.dac_debug_output(0xF7FFDFFF, 0x07FFDFFF, 0x07FFDFFF, 0x07FFDFFF)
- if k == '3': fpga.dac_debug_output(0xF8002000, 0x08002000, 0x08002000, 0x08002000)
- if k == '4': fpga.dac_debug_output(0xFFFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF)
-
- def makeSines(freqs, T):
- """Build sram sequence consisting of superposed sine waves."""
- wave = [sum(sin(2*pi*t*f) for f in freqs) for t in range(T)]
- sram = [(long(round(0x1FFF*y/len(freqs))) & 0x3FFF)*0x4001 for y in wave]
- sram[0] = 0xF0000000 # add triggers at the beginning
- return sram
-
- if k == '5': fpga.dac_run_sram(makeSines([0.100], 40), True)
- if k == '6': fpga.dac_run_sram(makeSines([0.200], 40), True)
- if k == '7': fpga.dac_run_sram(makeSines([0.100, 0.175], 40), True)
-
- print 'running...'
-
- if k in '+-*/':
- if k == '+': ccset += 1
- if k == '-': ccset -= 1
- if k == '*': ccset += 10
- if k == '/': ccset -= 10
-
- if ccset > +63: ccset = +63
- if ccset < -63: ccset = -63
- fpga.dac_cross_controller('A', ccset)
- fpga.dac_cross_controller('B', ccset)
-
- if k == 'I': bringupBoard(fpga, board,fullOutput=True)
- if k == 'Q': break
-
-# User interface utilities
-
-def getChoice(keys):
- """Get a keypress from the user from the specified keys."""
- r = ''
- while not r or r not in keys:
- r = raw_input().upper()
- return r
-
-def selectFromList(options, title):
- """Get a user-selected option
-
- Returns an element from options.
- """
- print
- print
- print 'Select %s:' % title
- print
- keys = {}
- for i, opt in enumerate(options):
- key = '%d' % (i+1)
- keys[key] = opt
- print ' [%s] : %s' % (key, opt)
- keys['A'] = 'All'
- print ' [A] : All'
- keys['Q'] = None
- print ' [Q] : Quit'
-
- k = getChoice(keys)
-
- return keys[k]
-
-def selectBoardGroup(fpga):
- """Gets user selected board group"""
- groups = fpga.list_board_groups()
- group = selectFromList(groups, 'Board Group')
- return group
-
-def doBringup():
- with labrad.connect() as cxn:
- fpga = cxn[FPGA_SERVER]
- group = selectBoardGroup(fpga)
- while True:
- if group is None:
- break
- else:
- boards = fpga.list_devices(group) \
- if group in fpga.list_board_groups() \
- else fpga.list_devices()
- boardSelect = selectFromList(boards, 'FPGA Board')
- if boardSelect is None:
- break
- elif boardSelect == 'All':
- bringupBoards(fpga,[board[1] for board in boards],noisy=True)
- else:
- board = boardSelect[1]
- interactiveBringup(fpga, board)
-
-if __name__ == '__main__':
- doBringup()
diff --git a/ghz_fpga_server.py b/ghz_fpga_server.py
deleted file mode 100644
index c6dd9a94..00000000
--- a/ghz_fpga_server.py
+++ /dev/null
@@ -1,2221 +0,0 @@
-# Copyright (C) 2007, 2008, 2009, 2010 Matthew Neeley
-# Copyright (C) 2010, 2011, 2012, 2013
-# 2014 Daniel Sank, James Wenner
-#
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-# CHANGELOG:
-#
-# 2011 November 30 - Daniel Sank / Jim Wenner
-#
-# Run Sequence setting now only tries to get ADC demodulation ranges when
-# getTimingData is True. If getTimingData is False, no data is extracted,
-# so it's impossible to store the I and Q ranges.
-#
-# 2011 November 29 - Jim Wenner
-#
-# Removed adc_recalibrate from adc_bringup since this may randomize order of
-# I, Q outputs.
-#
-# 2011 November 16 - Dan Sank/Jim Wenner
-#
-# Fixed documentation for dac_lvds and dac_fifo.
-# Changed return type tag for dac_bringup. Now an array of clusters instead of
-# a cluster of clusters.
-#
-# For dac and adc device objects, changed device.params to device.buildParams.
-# This was done because we now have _build_ specific, and _board_ specific
-# parameters stored in the registry and we need to distinguish these in the
-# variable names.
-#
-# In other words, build specific data are in device.buildParams
-# and board specific data are in device.boardParams
-#
-# 2011 November 10 - Jim Wenner
-#
-# Fixed bug where, in list_dacs and list_adcs, looked for (name, id) in devices
-# when checking board groups even though only name present by this point.
-#
-# 2011 November 4 - Daniel Sank
-#
-# The code around line 1172 which read "c[runner.dev]['ranges']=runner.ranges"
-# didn't work because I had never assigned runner.ranges. This is now assigned
-# near line 601.
-#
-# 2011 November 2 - Jim Wenner
-#
-# Moved bringup code into the server. This was done to help keep bringup and
-# server code synchronized with each other.
-#
-# DAC bringup now has signed data as default. Added
-# functions to return list of DACs or of ADCs.
-#
-# In setFIFO, removed reset of LVDS sample delay. Changed how check FIFO
-# counter to ensure final value same as what thought setting to. In both
-# setFIFO and setLVDS, added success/ failure checks and modified return
-# parameters. Changed default FIFO counter from 3 to value in registry provided
-# for each board. Changed default setLVDS behavior from optimizing LVDS SD to
-# getting SD from board-specific registry key while adding option to use
-# optimal SD instead. setLVDS returns MSD, MHD even if sample delay specified.
-#
-# Board specific registry keys are located in ['Servers', 'GHz FPGAs'] and are
-# of form:
-# dacN=[('fifoCounter', 3), ('lvdsSD', 3), ('lvdsPhase', 180)]
-#
-# 2011 February 9 - Daniel Sank
-# Removed almost all references to hardcoded hardware parameters, for example
-# the various SRAM lengths. These values are now board specific.
-# As an example of how this is implemented, we used to have something like
-# this:
-# def adc_filter_func(self, c, bytes, stretchLen=0, stretchAt=0):
-# assert len(bytes) <= FILTER_LEN, 'Filter function max length is %d' \
-# % FILTER_LEN
-# dev = self.selectedADC(c)
-# ...
-# where FILTER_LEN was a global constant. We now instead have this:
-# def adc_filter_func(self, c, bytes, stretchLen=0, stretchAt=0):
-# dev = self.selectedADC(c)
-# assert len(bytes) <= dev.buildParams['FILTER_LEN'], 'Filter function max\
-# length is %d' % dev.buildParams['FILTER_LEN']
-# ...
-# so that the filter length is board specific. These board specific parameters
-# are loaded by the board objects when they are created, See dac.py and adc.py
-# for details on how these parameters are loaded.
-
-
-# + DOCUMENTATION
-#
-# Communication between the computer and the FPGA boards works over ethernet.
-# This server and the associated board type definition files dac.py and adc.py
-# abstract away this ethernet communication. This means that you don't have
-# to explicitly tell the direct ethernet server to send packets to the boards.
-# Instead you call, for example, this server's Memory command and the server
-# will build the appropriate packets for the board you've selected and the
-# memory sequence you want to send. No packets are actually sent to the boards
-# until you tell them to turn using one of the following commands:
-# DAC Run SRAM - Runs SRAM on one board without waiting for a daisychain
-# pulse.
-# ADC Run Demod - Runs ADC demod mode on one board without waiting for a
-# daisychain pulse.
-# ADC Run Average - Runs ADC average mode on one board without waiting for a
-# daisychain pulse.
-# Run Sequence - Runs multiple boards synchronously using the daisychain
-# (DACs and ADCs).
-# When one of the one-off (no daisychain) commands is sent, whichever DAC
-# or ADC you have selected in your context will run and return data as
-# appropriate. The use of Run Sequence is slightly more complicated. See below.
-#
-# ++ USING RUN SEQUENCE
-# The Run Sequence command is used to run multiple boards synchronously using
-# daisychain pulses to trigger SRAM execution. Here are the steps to use it:
-# 1. "Daisy Chain" to specify what boards will run
-# 2. "Timing Order" to specify which boards' data you will collect
-# 3. Set up DACS - for each DAC you want to run call Select Device and then:
-# a. SRAM or SRAM dual block
-# b. Memory
-# c. Start Delay
-# 4. Set up ADCs - for each ADC you want to run call Select Device and then:
-# a. ADC Run Mode
-# b. ADC Filter Func (set this even if you aren't using demodulation mode)
-# c. ADC Demod Phase
-# d. ADC Trig Magnitude
-# e. Start Delay
-# For information on the format of the data returned by Run Sequence see its
-# docstring.
-#
-# ++ REGISTRY KEYS
-# In order for the server to set up the board groups and fpga devices properly
-# there are a couple of registry entries that need to be set up. Registry keys
-# for this server live in ['', 'Servers', 'GHz FPGAs']
-#
-# boardGroups: *(ssw*(sw)), [(groupName, directEthernetServername, portNumber,
-# [(boardName, daisychainDelay), ...]), ...]
-# This key tells the server what boards groups should exist, what direct
-# ethernet server controlls that group, which ethernet port is connected to the
-# boards (via an ethernet switch) and what boards exist on each group. Board
-# names should be of the form "DAC N" or "ADC N" where N is the number
-# determined by the DIP switches on the board. The number after each board name
-# is the number of clock cycles that board should wait after receiving a
-# daisychain pulse before starting to run its SRAM.
-#
-# dacBuildX: *(s?), [(parameterName, value), (parameterName, value), ...]
-# adcBuildX: *(s?), [(parameterName, value), (parameterName, value), ...]
-# When FPGA board objects are created they read the registry to find hardware
-# parameter values. For example, the DAC board objects need to know how long
-# their SRAM memory is, and each board may have a different value depending on
-# its specific FPGA chip. Details, lists of necessary parameters and example
-# values for each board type are given in dac.py and adc.py
-#
-# dacN: *(s?), [(parameterName, value), (parameterName, value), ...] Parameters
-# which are specific to individual boards. This is used for the default FIFO
-# counter, LVDS SD, etc. See examples in dac.py
-
-from __future__ import with_statement
-
-"""
-TODO
-cmdTime_cycles does not properly estimate sram length
-
-
-"""
-
-
-"""
-### BEGIN NODE INFO
-[info]
-name = GHz FPGAs
-version = 5.2.0
-description = Talks to DAC and ADC boards
-
-[startup]
-cmdline = %PYTHON% %FILE%
-timeout = 20
-
-[shutdown]
-message = 987654321
-timeout = 20
-### END NODE INFO
-"""
-
-import itertools
-import logging
-import os
-import random
-import struct
-import sys
-import time
-
-import numpy as np
-
-from twisted.internet import defer
-from twisted.internet.defer import inlineCallbacks, returnValue
-
-from labrad import types as T, units as U
-from labrad.devices import DeviceServer
-from labrad.server import setting
-from labrad.units import Unit, Value
-import labrad.util
-
-import fpgalib.adc as adc
-import fpgalib.dac as dac
-import fpgalib.fpga as fpga
-from fpgalib.util import TimedLock, LoggingPacket
-
-
-# The logging level is set at the bottom of the file where the server starts.
-# To get additional info about what the server is doing (i.e. to see if it
-# gets to a certain part of run_sequence), change the logging level to
-# logging.INFO. To print the Direct Etherenet packets, set it to
-# logging.DEBUG.
-
-def timeString():
- ts = ('{0.tm_year} {0.tm_mon} {0.tm_mday} {0.tm_hour} {0.tm_min} {0.tm_sec}'
- .format(time.localtime()))
- return ts
-
-
-LOGGING_PACKET = False
-
-
-NUM_PAGES = 2
-
-I2C_RB = 0x100
-I2C_ACK = 0x200
-I2C_RB_ACK = I2C_RB | I2C_ACK
-I2C_END = 0x400
-
-# TODO: Remove the constants from above and put them in the registry to be
-# read by individual DAC board instances. See DacDevice.connect to see how
-# this is done
-# TODO: make sure paged operations (datataking) don't conflict with e.g.
-# bringup
-# - want to do this by having two modes for boards, either 'test' mode
-# (when a board does not belong to a board group) or 'production' mode
-# (when a board does belong to a board group). It would be nice if boards
-# could be dynamically moved between groups, but we'll see about that...
-# TODO: store memory and SRAM as numpy arrays, rather than lists and strings,
-# respectively
-# TODO: run sequences to verify the daisy-chain order automatically
-# TODO: when running adc boards in demodulation (streaming mode), check
-# counters to verify that there is no packet loss
-# TODO: think about whether page selection and pipe semaphore can interact
-# badly to slow down pipelining
-
-
-class TimeoutError(Exception):
- """Error raised when boards timeout."""
-
-
-class BoardGroup(object):
- """Manages a group of GHz DAC boards that can be run simultaneously.
-
- All the fpga boards must be daisy-chained to allow for synchronization,
- and also must be connected to the same network card. Currently, one
- board group is created automatically for each detected network card.
- Only one sequence at a time can be run on a board group, but memory
- and SRAM updates can be pipelined so that while a sequence is running
- on some set of the boards in the group, new sequence data for the next
- point can be uploaded.
- """
- def __init__(self, fpgaServer, directEthernetServer, port):
- self.fpgaServer = fpgaServer
- self.directEthernetServer = directEthernetServer
- self.port = port
- self.ctx = None
- self.pipeSemaphore = defer.DeferredSemaphore(NUM_PAGES)
- self.pageNums = itertools.cycle(range(NUM_PAGES))
- self.pageLocks = [TimedLock() for _ in range(NUM_PAGES)]
- self.runLock = TimedLock()
- self.readLock = TimedLock()
- self.setupState = set()
- self.runWaitTimes = []
- self.prevTriggers = 0
-
- @inlineCallbacks
- def init(self):
- """Set up the direct ethernet server in our own context."""
- self.ctx = self.directEthernetServer.context()
- p = self.directEthernetServer.packet(context=self.ctx)
- p.connect(self.port)
- yield p.send()
-
- @inlineCallbacks
- def shutdown(self):
- """Clean up when this board group is removed."""
- # expire our context with the manager
- cxn = self.directEthernetServer._cxn
- servers = yield cxn.manager.servers()
- server_ids = set(id for id, name in servers)
- if self.directEthernetServer.ID in server_ids:
- yield cxn.manager.expire_context(
- self.directEthernetServer.ID, context=self.ctx)
-
- def configure(self, name, boards):
- """Update configuration for this board group."""
- self.name = name
- self.boardOrder = ['{} {}'.format(name, boardName) for
- (boardName, delay) in boards]
- self.boardDelays = [delay for (boardName, delay) in boards]
-
- @inlineCallbacks
- def detectBoards(self):
- """Detect boards on the ethernet adapter managed by this board group.
-
- The autodetect operation is guarded by board group locks so that it
- will not conflict with sequences running on this board group.
- """
- try:
- # Acquire all locks so we can ping boards without interfering with
- # board group operations.
- for i in xrange(NUM_PAGES):
- yield self.pipeSemaphore.acquire()
- for pageLock in self.pageLocks:
- yield pageLock.acquire()
- yield self.runLock.acquire()
- yield self.readLock.acquire()
-
- # Detect each board type in its own context.
- detections = [self.detectDACs(), self.detectADCs()]
- answer = yield defer.DeferredList(detections, consumeErrors=True)
- found = []
- for success, result in answer:
- if success:
- found.extend(result)
- else:
- print 'autodetect error:'
- result.printTraceback()
-
- # Clear detection packets which may be buffered in device contexts.
- # TODO: check that this actually clears packets.
- devices = self.devices()
- clears = []
- for dev in devices:
- clears.append(dev.clear().send())
-
- returnValue(found)
- finally:
- # Release all locks once we're done with autodetection.
- for i in xrange(NUM_PAGES):
- self.pipeSemaphore.release()
- for pageLock in self.pageLocks:
- pageLock.release()
- self.runLock.release()
- self.readLock.release()
-
- def detectDACs(self, timeout=1.0):
- """Try to detect DAC boards on this board group."""
- def callback(src, data):
- board = int(src[-2:], 16)
- build = dac.DAC.readback2BuildNumber(data)
- devName = '{} DAC {}'.format(self.name, board)
- args = (devName, self, self.directEthernetServer, self.port, board,
- build)
- return (devName, args)
- macs = [dac.DAC.macFor(board) for board in range(256)]
- return self._doDetection(macs, dac.DAC.regPing(),
- dac.DAC.READBACK_LEN, callback)
-
- def detectADCs(self, timeout=1.0):
- """Try to detect ADC boards on this board group."""
- def callback(src, data):
- board = int(src[-2:], 16)
- build = adc.ADC.readback2BuildNumber(data)
- devName = '{} ADC {}'.format(self.name, board)
- args = (devName, self, self.directEthernetServer, self.port, board,
- build)
- return (devName, args)
- macs = [adc.ADC.macFor(board) for board in range(256)]
- return self._doDetection(macs, adc.ADC.regPing(),
- adc.ADC.READBACK_LEN, callback)
-
- @inlineCallbacks
- def _doDetection(self, macs, packet, respLength, callback, timeout=1.0):
- """
- Try to detect a boards at the specified mac addresses.
-
- For each response of the correct length received within the timeout
- from one of the given mac addresses, the callback function will be
- called and should return data to be added to the list of found
- devices.
- """
- try:
- ctx = self.directEthernetServer.context()
-
- # Prepare and send detection packets.
- p = self.directEthernetServer.packet()
- p.connect(self.port)
- p.require_length(respLength)
- p.timeout(T.Value(timeout, 's'))
- p.listen()
- for mac in macs:
- p.destination_mac(mac)
- p.write(packet.tostring())
- yield p.send(context=ctx)
- # Listen for responses.
- start = time.time()
- found = []
- while (len(found) < len(macs)) and (time.time() - start < timeout):
- try:
- ans = yield self.directEthernetServer.read(1, context=ctx)
- src, dst, eth, data = ans[0]
- if src in macs:
- devInfo = callback(src, data)
- found.append(devInfo)
- except T.Error as e:
- logging.error('timeout exception: {}'.format(e))
- break # Read timeout.
- returnValue(found)
- finally:
- # Expire the detection context.
- cxn = self.directEthernetServer._cxn
- yield cxn.manager.expire_context(self.directEthernetServer.ID,
- context=ctx)
-
- def devices(self):
- """
- Return a list of known device objects belonging to this board group.
- """
- return [dev for dev in self.fpgaServer.devices.values()
- if dev.boardGroup == self]
-
- @inlineCallbacks
- def testMode(self, func, *a, **kw):
- """
- Call a function in test mode.
-
- This makes sure that all currently-executing pipeline stages
- are finished by acquiring the pipe semaphore for all pages,
- then runs the function, and finally releases the semaphore
- to allow the pipeline to continue.
- """
- for i in xrange(NUM_PAGES):
- yield self.pipeSemaphore.acquire()
- try:
- ans = yield func(*a, **kw)
- returnValue(ans)
- finally:
- for i in xrange(NUM_PAGES):
- self.pipeSemaphore.release()
-
-
- def makePackets(self, runners, page, reps, timingOrder, sync=249):
- """Make packets to run a sequence on this board group.
-
- Running a sequence has 4 stages:
- - Load memory and SRAM into all boards in parallel.
- If possible, this is done in the background using a separate
- page while another sequence is running.
-
- - Run sequence by firing a single packet that starts all boards.
- To ensure synchronization the slaves are started first, in
- daisy-chain order, followed at the end by the master.
-
- - Collect timing data to ensure that the sequence is finished.
- We instruct the direct ethernet server to collect the packets
- but not send them yet. Once collected, direct ethernet triggers
- are used to immediately start the next sequence if one was
- loaded into the next page.
-
- - Read timing data.
- Having started the next sequence (if one was waiting) we now
- read the timing data collected by the direct ethernet server,
- process it and return it.
-
- This function prepares the LabRAD packets that will be sent for
- each of these steps, but does not actually send anything. By
- preparing these packets in advance we save time later when we
- are in the time-critical pipeline sections
-
-
- loadPkts: list of packets, one for each board
- setupPkts: list of (packet, setup state). Only for ADC
- runPkts: wait, run, both. These packets are sent in the master
- context, and are placed carefully in order so that the
- master board runs last.
- collectPkts: list of packets, one for each board. These packets
- tell the direct ethernet server to collect, and then
- if successful, send triggers to the master context.
- readPkts: list of packets. Simply read back data from direct
- ethernet buffer for each board's context.
-
- Packets generated by dac and adc objects are make with the
- context set to that device's context. This ensures that the
- packets have the right destination MAC and therefore arrive in
- the right place.
- """
- # Dictionary of devices to be run.
- runnerInfo = dict((runner.dev.devName, runner) for runner in runners)
-
- # Upload sequence data (pipelined).
- loadPkts = []
- for board in self.boardOrder:
- if board in runnerInfo:
- runner = runnerInfo[board]
- isMaster = len(loadPkts) == 0
- p = runner.loadPacket(page, isMaster)
- if p is not None:
- loadPkts.append(p)
-
- # Setup board state (not pipelined).
- # Build a list of (setupPacket, setupState).
- setupPkts = []
- for board in self.boardOrder:
- if board in runnerInfo:
- runner = runnerInfo[board]
- p = runner.setupPacket()
- if p is not None:
- setupPkts.append(p)
- # Run all boards (master last).
- # Set the first board which is both in the boardOrder and also in the
- # list of runners for this sequence as the master. Any subsequent boards
- # for which we have a runner are set to slave mode, while subsequent
- # unused boards are set to idle mode. For example:
- # All boards: 000000
- # runners: --XX-X
- # mode: msis (i: idle, m: master, s: slave) -DTS
- boards = [] # List of (, ).
- for board, delay in zip(self.boardOrder, self.boardDelays):
- if board in runnerInfo:
- runner = runnerInfo[board]
- slave = len(boards) > 0
- regs = runner.runPacket(page, slave, delay, sync)
- boards.append((runner.dev, regs))
- elif len(boards):
- # This board is after the master, but will not itself run, so
- # we put it in idle mode.
- dev = self.fpgaServer.devices[board] # Look up device wrapper.
- if isinstance(dev, dac.DAC):
- regs = dev.regIdle(delay)
- boards.append((dev, regs))
- elif isinstance(dev, adc.ADC):
- # ADC boards always pass through signals, so no need for
- # Idle mode.
- pass
- boards = boards[1:] + boards[:1] # move master to the end.
- runPkts = self.makeRunPackets(boards)
- # Collect and read (or discard) timing results.
- seqTime = max(runner.seqTime for runner in runners)
- collectPkts = [runner.collectPacket(seqTime, self.ctx)
- for runner in runners]
- readPkts = [runner.readPacket(timingOrder) for runner in runners]
-
- return loadPkts, setupPkts, runPkts, collectPkts, readPkts
-
- def makeRunPackets(self, data):
- """Create packets to run a set of boards.
-
- There are two options as to how this can work, depending on
- whether the setup state from the previous run is the same as
- for this run. If no changes to the setup state are required,
- then we can wait for triggers and immediately start the next
- run; this is what the 'both' packet does. If the setup state
- has changed, we must wait for triggers, then send setup packets,
- and then start the next run. This two-stage operation is what
- the 'wait' and 'run' packets do. We create both here because
- we can't tell until it is our turn in the pipe which method
- will be used.
- """
-
- wait = self.directEthernetServer.packet(context=self.ctx)
- run = self.directEthernetServer.packet(context=self.ctx)
- both = self.directEthernetServer.packet(context=self.ctx)
- if LOGGING_PACKET:
- wait = LoggingPacket(wait, name='run=wait')
- run = LoggingPacket(run, name='run=run')
- both = LoggingPacket(both, name='run=both')
- # Wait for triggers and discard them. The actual number of triggers to
- # wait for will be decide later. The 0 is a placeholder here.
- wait.wait_for_trigger(0, key='nTriggers')
- both.wait_for_trigger(0, key='nTriggers')
- # Run all boards.
- for dev, regs in data:
- bytes = regs.tostring()
- # We must switch to each board's destination MAC each time we write
- # data because our packets for the direct ethernet server is in the
- # main context of the board group, and therefore does not have a
- # specific destination MAC.
- run.destination_mac(dev.MAC).write(bytes)
- both.destination_mac(dev.MAC).write(bytes)
- return wait, run, both
-
- @inlineCallbacks
- def run(self, runners, reps, setupPkts, setupState, sync, getTimingData,
- timingOrder):
- """Run a sequence on this board group."""
-
- # Check whether this sequence will fit in just one page.
- if all(runner.pageable() for runner in runners):
- # Lock just one page.
- page = self.pageNums.next()
- pageLocks = [self.pageLocks[page]]
- else:
- # Start on page 0 and set pageLocks to all pages.
- print 'Paging off: SRAM too long.'
- page = 0
- pageLocks = self.pageLocks
-
- # Prepare packets.
- logging.info('making packets')
- pkts = self.makePackets(runners, page, reps, timingOrder, sync)
- loadPkts, boardSetupPkts, runPkts, collectPkts, readPkts = pkts
-
- # Add setup packets from boards (ADCs) to that provided in the args:
- # setupPkts is a list.
- # setupState is a set.
- setupPkts.extend(pkt for pkt, state in boardSetupPkts)
- setupState.update(state for pkt, state in boardSetupPkts)
-
- try:
- yield self.pipeSemaphore.acquire()
- logging.info('pipe semaphore acquired')
- try:
- # Stage 1: load.
- for pageLock in pageLocks: # Lock pages to be written.
- yield pageLock.acquire()
- logging.info('page locks acquired')
- # Send load packets. Do not wait for response. We already
- # acquired the page lock, so sending data to SRAM and memory is
- # kosher at this time.
- # TODO: Need to check what 'load packets' is for ADC and make
- # sure sending load packets here is ok.
- loadDone = self.sendAll(loadPkts, 'Load')
- # stage 2: run
- # Send a request for the run lock, do not wait for response.
- runNow = self.runLock.acquire()
- try:
- yield loadDone # wait until load is finished.
- yield runNow # Wait for acquisition of the run lock.
- logging.info('run lock acquired')
- # Set the number of triggers needed before we can actually
- # run. We expect to get one trigger for each board that
- # had to run and return data. This is the number of
- # runners in the previous sequence.
- logging.info(
- 'num prev triggers: {}'.format(self.prevTriggers))
- waitPkt, runPkt, bothPkt = runPkts
- waitPkt['nTriggers'] = self.prevTriggers
- bothPkt['nTriggers'] = self.prevTriggers
- # store the number of triggers for the next run
- self.prevTriggers = len(runners)
- logging.info('num runners: {}'.format(len(runners)))
- # If the passed in setup state setupState, or the current
- # actual setup state, self.setupState are empty, we need
- # to set things up. Also if the desired setup state isn't
- # a subset of the actual one, we need to set up.
- # XXX Check what setup state means for ADC. Should this
- # include the trigger/demodulator tables or not?
- needSetup = ((not setupState) or (not self.setupState) or
- (not (setupState <= self.setupState)))
- if needSetup:
- logging.info('needSetup = True')
- # we require changes to the setup state so first, wait
- # for triggers indicating that the previous run has
- # collected.
- # If this fails, something BAD happened!
- r = yield waitPkt.send()
- logging.info('waitPkt sent')
- try:
- # Then set up
- logging.info('sending setupPkts...')
- yield self.sendAll(setupPkts, 'Setup')
- logging.info('...setupPkts sent')
- self.setupState = setupState
- except Exception as e:
- # if there was an error, clear setup state
- logging.info('catching setupPkts exception')
- self.setupState = set()
- logging.error(
- 'Exception in setupPkts: {}'.format(e))
- raise e
- # and finally run the sequence
- logging.info('sending runPkt...')
- yield runPkt.send()
- logging.info('...runPkt sent')
- else:
- # if this fails, something BAD happened!
- logging.info('need setup = false')
- r = yield bothPkt.send()
-
- # Keep track of how long the packet waited before being
- # able to run.
- # XXX How does this work? Why is r['nTriggers'] the wait
- # time?
- # print "fpga server: r['nTriggers']: %s" % (r['nTriggers'])
- self.runWaitTimes.append(r['nTriggers']['s'])
- if len(self.runWaitTimes) > 100:
- self.runWaitTimes.pop(0)
-
- yield self.readLock.acquire() # wait for our turn to read
- logging.info('read lock acquired')
- # stage 3: collect
- # Collect appropriate number of packets and then trigger
- # the master context.
- collectAll = defer.DeferredList(
- [p.send() for p in collectPkts], consumeErrors=True)
- logging.info('waiting for collect packets')
- finally:
- # by releasing the runLock, we allow the next sequence to
- # send its run packet. if our collect fails due to a
- # timeout, however, our triggers will not all be sent to
- # the run context, so that it will stay blocked until
- # after we cleanup and send the necessary triggers
-
- # We now release the run lock. Other users will now be
- # able to send their run packet to the direct ethernet,
- # but the direct ethernet will not actually send run
- # commands to the FPGA boards until the master context
- # receives all expected triggers. These triggers are sent
- # along with the collect packets, and succeed only if the
- # collect commands do not time out. This means that the
- # boards won't run until either our collect succeeds,
- # meaning we're finished running and got all expected
- # data, or we clean up from a timeout and manually send
- # the necessary number of triggers. Note that we release
- # the run lock IMMEDIATELY after sending the request to
- # collect so that other users can get going ASAP.
- # The direct ethernet server will allow other run commands
- # to go as soon as our triggers are received, but only if
- # that run command has been sent!
- self.runLock.release()
- logging.info('run lock released')
- # Wait for data to be collected.
- results = yield collectAll
- logging.info('results collected')
- finally:
- for pageLock in pageLocks:
- pageLock.release()
- logging.info('page lock released')
-
- # check for a timeout and recover if necessary
- if not all(success for success, result in results):
- for success, result in results:
- if not success:
- result.printTraceback()
- yield self.recoverFromTimeout(runners, results)
- self.readLock.release()
- raise TimeoutError(self.timeoutReport(runners, results))
-
- # stage 4: read
- # no timeout, so go ahead and read data
- boardOrder = [runner.dev.devName for runner in runners]
- readAll = self.sendAll(readPkts, 'Read', boardOrder)
- self.readLock.release()
- # This line scales really badly with incrasing stats
- # At 9600 stats the next line takes 10s out of 20s per
- # sequence.
- results = yield readAll # wait for read to complete
-
- if getTimingData:
- answers = []
- # Cache of already-parsed data from a particular board.
- # Prevents un-flattening a packet more than once.
- extractedData = {}
- for dataChannelName in timingOrder:
- if '::' in dataChannelName:
- # If dataChannelName has :: in it, it's an ADC
- # with specified demod channel
- boardName, channel = dataChannelName.split('::')
- channel = int(channel)
- elif 'DAC' in dataChannelName:
- raise RuntimeError('DAC data readback not supported')
- elif 'ADC' in dataChannelName:
- # ADC average mode
- boardName = dataChannelName
- channel = None
- else:
- raise RuntimeError('channel format not understood')
-
- if boardName in extractedData:
- # If we have already parsed the packet for this
- # board, fetch the cached result.
- extracted = extractedData[boardName]
- else:
- # Otherwise, extract data, cache it, and add
- # relevant part to the list of returned data
- idx = boardOrder.index(boardName)
- runner = runners[idx]
- result = [data for src, dest, eth, data in
- results[idx]['read']]
- # Array of all timing results (DAC)
- extracted = runner.extract(result)
- extractedData[boardName] = extracted
- # Add extracted data to list of data to be returned
- if channel != None:
- # If this is an ADC demod channel, grab that
- # channel's data only
- extractedChannel = extracted[0][channel]
- else:
- extractedChannel = extracted
- answers.append(extractedChannel)
- returnValue(tuple(answers))
- finally:
- self.pipeSemaphore.release()
-
- @inlineCallbacks
- def sendAll(self, packets, info, infoList=None):
- """Send a list of packets and wrap them up in a deferred list."""
- # Remove packets which contain no actual requests.
- packets = [p for p in packets if p._packet]
- results = yield defer.DeferredList([p.send() for p in packets],
- consumeErrors=True) # [(success, result)...]
- if all(s for s, r in results):
- # return the list of results
- returnValue([r for s, r in results])
- else:
- # create an informative error message
- msg = 'Error(s) occured during {}:\n'.format(info)
- if infoList is None:
- msg += (''.join(r.getBriefTraceback()
- for s, r in results if not s))
- else:
- for i, (s, r) in zip(infoList, results):
- m = 'OK' if s else ('error!\n' + r.getBriefTraceback())
- msg += '{} : {}\n\n'.format(i, m)
- raise Exception(msg)
-
- def extractTiming(self, packets):
- """Extract timing data coming back from a readPacket."""
- data = ''.join(data[3:63] for data in packets)
- return np.fromstring(data, dtype=' 1:
- raise Exception('Can only run multiboard sequence if all boards '
- 'are in the same board group!')
- bg = devs[0].boardGroup
-
- # build a list of runners which have necessary sequence information
- # for each board
- # print "fpga server: buildRunner reps: %s" % (reps, )
- runners = [dev.buildRunner(reps, c.get(dev, {})) for dev in devs]
-
- # build setup requests
- setupReqs = _process_setup_packets(self.client, setupPkts)
- logging.debug('Setup Reqs: {}'.format(setupReqs))
-
- # run the sequence, with possible retries if it fails
- retries = self.retries
- attempt = 1
- while True:
- try:
- ans = yield bg.run(runners, reps, setupReqs, set(setupState),
- c['master_sync'], getTimingData,
- timingOrder)
- # For ADCs in demodulate mode, store their I and Q ranges to
- # check for possible clipping.
- for runner in runners:
- if (getTimingData and isinstance(runner, adc.AdcRunner) and
- runner.runMode == 'demodulate' and
- runner.dev.devName in timingOrder):
- c[runner.dev]['ranges'] = runner.ranges
- if ans is not None:
- ans = np.asarray(ans)
- returnValue(ans)
- except TimeoutError as err:
- # log attempt to stdout and file
- userpath = os.path.expanduser('~')
- logpath = os.path.join(userpath, 'dac_timeout_log.txt')
- with open(logpath, 'a') as logfile:
- t = timeString()
- msg = '{}: attempt {} - error: {}'.format(t, attempt, err)
- print(msg)
- logfile.write(msg+'\n')
- if attempt == retries:
- logfile.write('FAIL\n')
- # TODO: notify users via SMS.
- raise
- else:
- print('retrying...')
- logfile.write('retrying...')
- attempt += 1
-
- @setting(52, 'Daisy Chain', boards='*s', returns='*s')
- def sequence_boards(self, c, boards=None):
- """
- Set or get the boards to run.
-
- The actual daisy chain order is determined automatically, as
- configured in the registry for each board group. This setting controls
- which set of boards to run, but does not determine the order. Set
- daisy_chain to an empty list to run the currently-selected board only.
-
- DACs not listed here will be set to idle mode, and will pass the
- daisychain pulse through to the next board.
-
- ADCs always pass the daisychain pulse.
- """
- if boards is None:
- boards = c['daisy_chain']
- else:
- c['daisy_chain'] = boards
- return boards
-
- @setting(54, 'Timing Order', boards='*s', returns='*s')
- def sequence_timing_order(self, c, boards=None):
- """Set or get the timing order for boards.
-
- This specifies the boards from which you want to receive timing
- data, and the order in which the timing data should be returned.
- In addition, this will determine what kind of boards will return
- data (ADCs or DACs). For ADC boards, the data specified here must
- agree with the selection made for run mode for that board.
-
- To get DAC timing data or ADC data in average mode, specify the
- device name as a string. To get ADC data in demodulation mode,
- specify a string in the form "::" where
- channel is the demodulation channel number.
-
- Note that you can get data from more than one demodulation channel,
- so that a given ADC board can appear multiple times in the timing
- order, however each ADC board must be run either in average mode
- or demodulation mode, not both.
-
- Note that the boards parameter must be a list of strings, *s! If you
- send in a single string, pylabrad will accept it as a *s but it will
- be treated as a list of single character strings and you'll get
- unexpected behavior. For example, if you send in 'abcde' it will be
- treated like ['a', 'b', 'c', 'd', 'e'].
-
- Parameters:
- boards: INSERT EXAMPLE!!!
- """
- if boards is None:
- # Get timing order.
- boards = c['timing_order']
- else:
- # Set timing order.
- c['timing_order'] = boards
-
- return boards
-
- @setting(55, 'Master Sync', sync='w', returns='w')
- def sequence_master_sync(self, c, sync=None):
- """Set or get the master sync.
-
- This specifies a counter that determines when the master
- is allowed to start, to control the microwave phase. The
- default is 249, which sets the master to start only every 1 us.
- """
- if sync is None:
- sync = c['master_sync']
- else:
- c['master_sync'] = sync
- return sync
-
- @setting(59, 'Performance Data', returns='*((sw)(*v, *v, *v, *v, *v))')
- def sequence_performance_data(self, c):
- """Get data about the pipeline performance.
-
- For each board group (as defined in the registry),
- this returns times for:
- page lock (page 0)
- page lock (page 1)
- run lock
- run packet (on the direct ethernet server)
- read lock
-
- If the pipe runs dry, the first time that will go to zero will
- be the run packet wait time. In other words, if you have non-zero
- times for the run-packet wait, then the pipe is saturated,
- and the experiment is running at full capacity.
- """
- ans = []
- for (server, port), group in sorted(self.boardGroups.items()):
- pageTimes = [lock.times for lock in group.pageLocks]
- runTime = group.runLock.times
- runWaitTime = group.runWaitTimes
- readTime = group.readLock.times
- ans.append(((server, port), (pageTimes[0], pageTimes[1], runTime,
- runWaitTime, readTime)))
- return ans
-
- @setting(200, 'PLL Init', returns='')
- def pll_init(self, c, data):
- """Sends the initialization sequence to the PLL. (DAC and ADC)
-
- The sequence is [0x1FC093, 0x1FC092, 0x100004, 0x000C11].
- """
- dev = self.selectedDevice(c)
- yield dev.initPLL()
-
- @setting(201, 'PLL Reset', returns='')
- def pll_reset(self, c):
- """Resets the FPGA internal GHz serializer PLLs. (DAC only)"""
- dev = self.selectedDAC(c)
- yield dev.resetPLL()
-
- @setting(202, 'PLL Query', returns='b')
- def pll_query(self, c):
- """
- Checks the FPGA internal GHz serializer PLLs for lock failures.
- (DAC and ADC)
-
- Returns True if the PLL has lost lock since the last reset.
- """
- dev = self.selectedDevice(c)
- unlocked = yield dev.queryPLL()
- returnValue(unlocked)
-
- @setting(203, 'Build Number', returns='s')
- def build_number(self, c):
- """Gets the build number of selected device (DAC and ADC)"""
- dev = self.selectedDevice(c)
- buildNumber = yield dev.buildNumber()
- returnValue(buildNumber)
-
- @setting(204, 'Execution count', returns='i')
- def execution_counter(self, c):
- """Query sequence executions since last start command"""
- dev = self.selectedDevice(c)
- count = yield dev.executionCount()
- returnValue(int(count))
-
- @setting(1080, 'DAC Debug Output', data='wwww', returns='')
- def dac_debug_output(self, c, data):
- """Outputs data directly to the output bus. (DAC only)"""
- dev = self.selectedDAC(c)
- yield dev.debugOutput(*data)
-
- @setting(1081, 'DAC Run SRAM',
- data='*w', loop='b', blockDelay='w', returns='')
- def dac_run_sram(self, c, data, loop=False, blockDelay=0):
- """Loads data into the SRAM and executes as master. (DAC only)
-
- If loop is True, the sequence will be repeated forever,
- otherwise it will be executed just once. Sending
- an empty list of data will clear the SRAM. The blockDelay
- parameters specifies the number of microseconds to delay
- for a multiblock sequence.
- """
- if len(data) < 20:
- raise ValueError('Cannot play less than 20 ns of data.')
-
- dev = self.selectedDAC(c)
- yield dev.runSram(data, loop, blockDelay)
-
- @setting(2081, 'DAC Write SRAM', data='*w')
- def dac_write_sram(self, c, data):
- """Write data to SRAM.
-
- Args:
- data(iterable of int): List-like series of SRAM data. The data must
- already be packed.
-
- The data is written immediately, although no start commands are sent.
- This command just writes data into the board's SRAM buffer, that's it.
- """
- dev = self.selectedDAC(c)
- yield dev._sendSRAM(np.array(data, dtype=' 0:
- return [l[:i]] + rest
- else:
- return rest
- except ValueError: # No more sentinels.
- return [l]
-
- # Split data into packets delimited by I2C_END.
- pkts = partition(data, I2C_END)
- return dev.runI2C(pkts)
-
- @setting(1110, 'DAC LEDs', data=['w', 'bbbbbbbb'], returns='w')
- def dac_leds(self, c, data):
- """Sets the status of the 8 I2C LEDs. (DAC only)"""
- dev = self.selectedDAC(c)
-
- if isinstance(data, tuple):
- # convert to a list of digits, and interpret as binary int
- data = long(''.join(str(int(b)) for b in data), 2)
-
- pkts = [[200, 68, data & 0xFF]] # 192 for build 1
- yield dev.runI2C(pkts)
- returnValue(data)
-
- @setting(1120, 'DAC Reset Phasor', returns='b: phase detector output')
- def dac_reset_phasor(self, c):
- """Resets the clock phasor. (DAC only)"""
- dev = self.selectedDAC(c)
-
- pkts = [[152, 0, 127, 0], # set I to 0 deg
- [152, 34, 254, 0], # set Q to 0 deg
- [112, 65], # set enable bit high
- [112, 193], # set reset high
- [112, 65], # set reset low
- [112, 1], # set enable low
- [113, I2C_RB]] # read phase detector
-
- r = yield dev.runI2C(pkts)
- returnValue((r[0] & 1) > 0)
-
- @setting(1121, 'DAC Set Phasor',
- data=[': poll phase detector only',
- 'v[rad]: set angle (in rad, deg, \xF8, \', or ")'],
- returns='b: phase detector output')
- def dac_set_phasor(self, c, data=None):
- """
- Sets the clock phasor angle and reads the phase detector bit.
- (DAC only)
- """
- dev = self.selectedDAC(c)
-
- if data is None:
- pkts = [[112, 1],
- [113, I2C_RB]]
- else:
- sn = int(round(127 + 127*np.sin(data))) & 0xFF
- cs = int(round(127 + 127*np.cos(data))) & 0xFF
- pkts = [[152, 0, sn, 0],
- [152, 34, cs, 0],
- [112, 1],
- [113, I2C_RB]]
-
- r = yield dev.runI2C(pkts)
- returnValue((r[0] & 1) > 0)
-
- @setting(1130, 'DAC Vout', chan='s', V='v[V]', returns='w')
- def dac_vout(self, c, chan, V):
- """Sets the output voltage of any Vout channel, A, B, C or D. (DAC only)
- """
- cmd = dac.DAC.getCommand({'A': 16, 'B': 18, 'C': 20, 'D': 22}, chan)
- dev = self.selectedDAC(c)
- val = int(max(min(round(V*0x3333), 0x10000), 0))
- pkts = [[154, cmd, (val >> 8) & 0xFF, val & 0xFF]]
- yield dev.runI2C(pkts)
- returnValue(val)
-
- @setting(1135, 'DAC Ain', returns='v[V]')
- def dac_ain(self, c):
- """Reads the voltage on Ain. (DAC only)"""
- dev = self.selectedDAC(c)
- pkts = [[144, 0],
- [145, I2C_RB_ACK, I2C_RB]]
- r = yield dev.runI2C(pkts)
- returnValue(T.Value(((r[0] << 8) + r[1]) / 819.0, 'V'))
-
- @setting(1200, 'DAC PLL', data='*w', returns='*w')
- def dac_pll(self, c, data):
- """Sends a sequence of commands to the PLL. (DAC only)
-
- The returned WordList contains any read-back values.
- It has the same length as the sent list.
- """
- dev = self.selectedDAC(c)
- return dev.runSerial(1, data)
-
- @setting(1204, 'DAC Serial Command', chan='s', data='*w', returns='*w')
- def dac_cmd(self, c, chan, data):
- """Send a sequence of commands to either DAC. (DAC only)
-
- The DAC channel must be either 'A' or 'B'.
- The returned list of words contains any read-back values.
- It has the same length as the sent list.
- """
- cmd = dac.DAC.getCommand({'A': 2, 'B': 3}, chan)
- dev = self.selectedDAC(c)
- return dev.runSerial(cmd, data)
-
- @setting(1206, 'DAC Clock Polarity', chan='s', invert='b', returns='b')
- def dac_pol(self, c, chan, invert):
- """Sets the clock polarity for either DAC. (DAC only)"""
- dev = self.selectedDAC(c)
- yield dev.setPolarity(chan, invert)
- returnValue(invert)
-
- @setting(1220, 'DAC Init', chan='s', signed='b', returns='b')
- def dac_init(self, c, chan, signed=False):
- """Sends an initialization sequence to either DAC. (DAC only)
-
- For unsigned data, this sequence is 0026, 0006, 1603, 0500
- For signed data, this sequence is 0024, 0004, 1603, 0500
- """
- cmd = dac.DAC.getCommand({'A': 2, 'B': 3}, chan)
- dev = self.selectedDAC(c)
- pkt = ([0x0024, 0x0004, 0x1603, 0x0500] if signed else
- [0x0026, 0x0006, 0x1603, 0x0500])
- yield dev.runSerial(cmd, pkt)
- returnValue(signed)
-
- @setting(1221, 'DAC LVDS', chan='s', optimizeSD='b', data='w',
- returns='biii(*w*b*b)w')
- def dac_lvds(self, c, chan, optimizeSD=False, data=None):
- """Calibrate LVDS Phase Shift. (DAC Only)
-
- Align DAC clocks for LVDS phase shift, varying SD (sample delay).
-
- If optimizeSD=False but sd is None, SD will be set to a value retrieved
- from the registry entry for this board.
-
- Args:
- chan: Which DAC channel ('A','B')
- optimizeSD: Whether to follow data sheet procedure to determine SD
- data: If int, SD value to be set. If None, SD set to value in
- registry. Ignored if optimizeSD=True.
-
- Returns:
- success: If LVDS bringup successful. MSD and MHD should only flip
- once with flip locations within one bit of each other. If
- varies, could mean clock noise.
- MSD: Measured sample delay. If optimizeSD, where MSD flips when
- MHD=SD=0. Else -1.
- MHD: Measured hold delay. If optimizeSD, where MHD flips when
- MSD=SD=0. Else -1.
- SD: SD value set
- timing profile: Cluster (SDs 0-15, MSD(SDs), MHD(SDs))
- checkHex: In binary, '0bABC', where
- A=1: An LVDS input was above specification
- B=1: An LVDS input was below specification
- C=1: Sampling in correct data cycle
- """
- cmd = dac.DAC.getCommand({'A': 2, 'B': 3}, chan)
- dev = self.selectedDAC(c)
- ans = yield dev.setLVDS(cmd, data, optimizeSD)
- returnValue(ans)
-
- @setting(1222, 'DAC FIFO', chan='s', targetFifo='w', returns='bbiww')
- def dac_fifo(self, c, chan, targetFifo=None):
- """Adjust FIFO buffer. (DAC only)
-
- Adjust PHOF (phase offset) so FIFO (first-in-first-out) counter equals
- targetFifo. If FIFO counter equals targetFifo, this PHOF is written and
- the FIFO counter read back; the PHOF is deemed successful only if this
- last FIFO counter is targetFifo.
-
- If no PHOF can be found to get an acceptable FIFO counter after 5 tries,
- success=False. Here, return PHOF=-1 if the initial check failed and
- otherwise the PHOF where the FIFO counter was targetFifo initially.
-
- Args:
- chan: Which DAC channel ('A','B')
- targetFifo: Desired targetFifo. If None, use value from board
- registry entry.
-
- Returns:
- success, clock polarity, PHOF, number of tries, FIFO counter
- """
- op = dac.DAC.getCommand({'A': 2, 'B': 3}, chan)
- dev = self.selectedDAC(c)
- ans = yield dev.setFIFO(chan, op, targetFifo)
- returnValue(ans)
-
- @setting(1223, 'DAC Cross Controller', chan='s', delay='i', returns='i')
- def dac_xctrl(self, c, chan, delay=0):
- """Sets the cross controller delay on either DAC. (DAC only)
-
- Range for delay is -63 to 63.
- """
- dev = self.selectedDAC(c)
- cmd = dac.DAC.getCommand({'A': 2, 'B': 3}, chan)
- if delay < -63 or delay > 63:
- raise T.Error(11, 'Delay must be between -63 and 63')
-
- seq = ([0x0A00, 0x0B00 - delay] if delay < 0
- else [0x0A00 + delay, 0x0B00])
- yield dev.runSerial(cmd, seq)
- returnValue(delay)
-
- @setting(1225, 'DAC BIST', chan='s', data='*w', returns='b(ww)(ww)(ww)')
- def dac_bist(self, c, chan, data):
- """Run a Built-In Self Test on the given SRAM sequence. (DAC only)
-
- Returns success, theory, LVDS, FIFO
- """
- cmd, shift = dac.DAC.getCommand({'A': (2, 0), 'B': (3, 14)}, chan)
- dev = self.selectedDAC(c)
- ans = yield dev.runBIST(cmd, shift, data)
- # This is coming back with 64-bit ints, the coercing of which needs to
- # be fixed in pylabrad for now we manually cast to 32-bit (long)
- # See pylabrad github issue #43.
-
- def coerce(xs):
- return tuple(long(x) for x in xs)
- returnValue((ans[0], coerce(ans[1]), coerce(ans[2]), coerce(ans[3])))
-
- @setting(1300, 'DAC Bringup',
- lvdsOptimize='b',
- lvdsSD='w',
- signed='b',
- targetFifo='w',
- returns=('*((ss)(sb)(si)(si)(sw)(s(*w*b*b))(sw)(sb)(sb)(si)(sw)'
- '(sw)(sb)(s(ww))(s(ww))(s(ww)))'))
- def dac_bringup(self, c, lvdsOptimize=False, lvdsSD=None, signed=True,
- targetFifo=None):
- """
- Runs the bringup procedure.
-
- This code initializes the PLL, initializes the DAC, sets the LVDS SD,
- sets the FIFO, and runs the BIST test on each DAC channel. The output
- is (in tuple format) a list of two (one for each DAC) pairs of
- (string, data) with all the calibration parameters.
- """
- dev = self.selectedDAC(c)
- ans = []
- yield dev.initPLL()
- time.sleep(0.100)
- yield dev.resetPLL()
- for dac in ['A', 'B']:
- ansDAC = [('dac', dac)]
- cmd, shift = {'A': (2, 0), 'B': (3, 14)}[dac]
- # Initialize DAC.
- # See HardRegProgram.txt for byte sequence definition.
- pkt = ([0x0024, 0x0004, 0x1603, 0x0500] if signed else
- [0x0026, 0x0006, 0x1603, 0x0500])
- yield dev.runSerial(cmd, pkt)
- lvdsAns = yield dev.setLVDS(cmd, lvdsSD, lvdsOptimize)
- lvdsKeys = ['lvdsSuccess', 'lvdsMSD', 'lvdsMHD', 'lvdsSD',
- 'lvdsTiming', 'lvdsCheck']
- for key, val in zip(lvdsKeys, lvdsAns):
- ansDAC.append((key, val))
- fifoAns = yield dev.setFIFO(dac, cmd, targetFifo)
- fifoKeys = ['fifoSuccess', 'fifoClockPolarity', 'fifoPHOF',
- 'fifoTries', 'fifoCounter']
- for key, val in zip(fifoKeys, fifoAns):
- ansDAC.append((key, val))
- bistData = [random.randint(0, 0x3FFF) for i in range(1000)]
- bistAns = yield dev.runBIST(cmd, shift, bistData)
- bistKeys = ['bistSuccess', 'bistTheory', 'bistLVDS', 'bistFIFO']
- for key, val in zip(bistKeys, bistAns):
- ansDAC.append((key, val))
- ans.append(tuple(ansDAC))
- returnValue(ans)
-
- @setting(1313, 'DAC Serial', cmd='w', pkts='*w', returns='?')
- def dac_serial(self, c, cmd, pkts):
- dev = self.selectedDAC(c)
- ans = yield dev.runSerial(cmd, pkts)
- returnValue(ans)
-
- @setting(1100000, 'Debug Print Context')
- def debug_print_context(self, c):
- """Prints the context to the server's stdout."""
- print c
-
- @setting(1100001, 'Debug Clear Ethernet')
- def debug_clear_ethernet(self, c):
- for dev in self.devices.values():
- dev.clear().send()
-
- @setting(2500, 'ADC Recalibrate', returns='')
- def adc_recalibrate(self, c):
- """Recalibrate the analog-to-digital converters. (ADC only)"""
- dev = self.selectedADC(c)
- yield dev.recalibrate()
-
- @setting(2501, 'ADC Register Readback', returns='s')
- def adc_register_readback(self, c):
- """Register Readback. (ADC only)"""
- dev = self.selectedADC(c)
- ans = yield dev.registerReadback()
- returnValue(str(ans))
-
- @setting(2502, 'ADC Monitor Outputs', mon0=['s', 'w'], mon1=['s', 'w'])
- def adc_monitor_outputs(self, c, mon0, mon1):
- """Specify monitor outputs. (ADC only)"""
- dev = self.selectedADC(c)
- info = c.setdefault(dev, {})
- print 'monitor outputs: ', mon0, mon1
- info['mon0'] = mon0
- info['mon1'] = mon1
-
- @setting(2600, 'ADC Run Average', returns='*i{I},*i{Q}')
- def adc_run_average(self, c):
- """Run the selected ADC board once in average mode. (ADC only)
-
- The board will start immediately using the trig lookup and demod
- settings already specified in this context (although these settings
- have no effect in average mode). Returns the acquired I and Q
- waveforms.
-
- Returns:
- (I: np.array(int), Q: np.array(int))
- """
- dev = self.selectedADC(c)
- info = c.setdefault(dev, {})
- # demods = dict((i, info[i])
- # for i in range(dev.DEMOD_CHANNELS) if i in info)
- ans = yield dev.runAverage()
- returnValue(ans)
-
- @setting(2601, 'ADC Run Calibrate', returns='')
- def adc_run_calibrate(self, c):
- """Recalibrate the ADC chips"""
- raise Exception('Depricated. Use ADC Recalibrate instead')
- dev = self.selectedADC(c)
- info = c.setdefault(dev, {})
- filterFunc = info.get('filterFunc', np.array([255], dtype='.
-
-#
-# Version 1.1.0
-#
-# History
-#
-# 1.1.0 2008/06/17 added recalibrations and possibility to load several
-# calibration files
-# 1.0.0 first stable version
-
-
-from __future__ import with_statement
-
-from numpy import shape, array, size
-import numpy as np
-
-import labrad
-
-from correction import (DACcorrection, IQcorrection,
- cosinefilter, gaussfilter, flatfilter)
-import keys
-import calibrate
-import logging
-
-
-def aequal(a, b):
- return (shape(a) == shape(b)) and (all(a == b))
-
-
-def getDataSets(cxn, boardname, caltype, errorClass=None):
- reg = cxn.registry
- ds = cxn.data_vault
- reg.cd(['', keys.SESSIONNAME, boardname], True)
- if caltype in (reg.dir())[1]:
- calfiles = (reg.get(caltype))
- else:
- calfiles = array([])
-
- if not size(calfiles):
- if isinstance(errorClass, Exception):
- raise errorClass(caltype)
- elif errorClass != 'quiet':
- print 'Warning: No %s calibration loaded.' % caltype
- print ' No %s correction will be performed.' % caltype
-
- return (calfiles)
-
-
-def IQcorrector(fpganame, connection,
- zerocor=True, pulsecor=True, iqcor=True,
- lowpass=cosinefilter, bandwidth=0.4, errorClass='quiet'):
- """
- Returns a DACcorrection object for the given DAC board.
- The argument has the same form as the
- dms.python_fpga_server.connect argument
- """
-
- if connection:
- cxn = connection
- logging.debug("using received cxn: {}".format(cxn))
- else:
- cxn = labrad.connect()
-
- ds = cxn.data_vault
- ds.cd(['', keys.SESSIONNAME, fpganame], True)
- corrector = IQcorrection(fpganame, lowpass, bandwidth)
- # Load Zero Calibration
- if zerocor:
- datasets = getDataSets(cxn, fpganame, keys.ZERONAME, errorClass)
- logging.debug('datasets: {}'.format(datasets))
- for dataset in datasets:
- filename = ds.open(long(dataset))
- logging.debug('Loading zero calibration from: {}'.format(filename[1]))
- datapoints = ds.get()
- datapoints = np.array(datapoints)
- corrector.loadZeroCal(datapoints, dataset)
- # Load pulse response
- if pulsecor:
- dataset = getDataSets(cxn, fpganame, keys.PULSENAME, errorClass)
- if dataset != []:
- dataset = dataset[0]
- filename = ds.open(long(dataset))
- logging.debug('Loading pulse calibration from: {}'.format(filename[1]))
- setupType = ds.get_parameter(keys.IQWIRING)
- logging.info('setupType: {}'.format(setupType))
- IisB = (setupType == keys.SETUPTYPES[2])
- datapoints = ds.get()
- datapoints = np.array(datapoints)
- carrierfreq = (ds.get_parameter(keys.PULSECARRIERFREQ))['GHz']
- corrector.loadPulseCal(datapoints, carrierfreq, dataset, IisB)
- # Load Sideband Calibration
- if iqcor:
- datasets = getDataSets(cxn, fpganame, keys.IQNAME, errorClass)
- for dataset in datasets:
- filename = ds.open(long(dataset))
- logging.debug('Loading sideband calibration from: {}'.format(filename[1]))
- sidebandStep = \
- (ds.get_parameter('Sideband frequency step'))['GHz']
- sidebandCount = \
- ds.get_parameter('Number of sideband frequencies')
- datapoints = ds.get()
- datapoints = np.array(datapoints)
- corrector.loadSidebandCal(datapoints, sidebandStep, dataset)
- if not connection:
- cxn.disconnect()
- return corrector
-
-
-def DACcorrector(fpganame, channel, connection=None,
- lowpass=gaussfilter, bandwidth=0.13, errorClass='quiet', maxfreqZ=0.45):
- """
- Returns a DACcorrection object for the given DAC board.
- The argument has the same form as the
- dms.python_fpga_server.connect argument
- """
- if connection:
- cxn = connection
- else:
- cxn = labrad.connect()
-
- ds = cxn.data_vault
-
- ds.cd(['', keys.SESSIONNAME, fpganame], True)
-
- corrector = DACcorrection(fpganame, channel, lowpass, bandwidth)
-
- if not isinstance(channel, str):
- channel = keys.CHANNELNAMES[channel]
-
- dataset = getDataSets(cxn, fpganame, channel, errorClass)
- if dataset != []:
- logging.debug("Dataset - fpganame: {} channel: {}".format(fpganame, channel))
- dataset = dataset[0]
- logging.debug("Loading pulse calibration from: {}".format(dataset))
- ds.open(dataset)
- datapoints = ds.get()
- datapoints = np.array(datapoints)
- corrector.loadCal(datapoints, maxfreqZ=maxfreqZ)
- if not connection:
- cxn.disconnect()
-
- return corrector
-
-
-def recalibrate(boardname, carrierMin, carrierMax, zeroCarrierStep=0.025,
- sidebandCarrierStep=0.05, sidebandMax=0.35, sidebandStep=0.05,
- corrector=None):
- cxn = labrad.connect()
- ds = cxn.data_vault
- reg = cxn.registry
- reg.cd(['', keys.SESSIONNAME, boardname])
- anritsuID = reg.get(keys.ANRITSUID)
- anritsuPower = (reg.get(keys.ANRITSUPOWER))['dBm']
- if corrector is None:
- corrector = IQcorrector(boardname, cxn)
- if corrector.board != boardname:
- logging.info('Provided corrector is not for: {}'.format(boardname))
- logging.info('Loading new corrector. Provided corrector will not be updated.')
- corrector = IQcorrector(boardname, cxn)
-
- if zeroCarrierStep is not None:
- # check if a corrector has been provided and if it is up to date
- # or if we have to load a new one.
- if not aequal(corrector.zeroCalFiles,
- (getDataSets(cxn, boardname, keys.ZERONAME, 'quiet'))):
- logging.info('Provided corrector is outdated.')
- logging.info('Loading new corrector. Provided corrector will not be updated.')
- corrector = IQcorrector(boardname, cxn)
-
- # do the zero calibration
- dataset = calibrate.zeroScanCarrier(cxn,
- {'carrierMin': carrierMin,
- 'carrierMax': carrierMax,
- 'carrierStep': zeroCarrierStep},
- boardname)
- # load it into the corrector
- ds.open(dataset)
- datapoints = (ds.get()).asarray
- corrector.loadZeroCal(datapoints, dataset)
- # eliminate obsolete zero calibrations
- datasets = corrector.eliminateZeroCals()
- # and save which ones are being used now
- reg.cd(['', keys.SESSIONNAME, boardname], True)
- reg.set(keys.ZERONAME, datasets)
- if sidebandCarrierStep is not None:
- # check if a corrector has been provided and if it is up to date
- # or if we have to load a new one.
- if not (aequal(corrector.sidebandCalFiles,
- (getDataSets(cxn, boardname, keys.IQNAME, 'quiet'))) and \
- aequal(array([corrector.pulseCalFile]),
- (getDataSets(cxn, boardname, keys.PULSENAME, 'quiet')))):
- logging.info('Provided correcetor is outdated.')
- logging.info('Loading new corrector. Provided corrector will not be updated.')
- corrector = IQcorrector(boardname, cxn)
-
- # do the pulse calibration
- dataset = calibrate.sidebandScanCarrier(cxn,
- {'carrierMin': carrierMin,
- 'carrierMax': carrierMax,
- 'sidebandCarrierStep': sidebandCarrierStep,
- 'sidebandFreqStep': sidebandStep,
- 'sidebandFreqCount': int(sidebandMax / sidebandStep + 0.5) * 2},
- boardname, corrector)
- # load it into the corrector
- ds.open(dataset)
- sidebandStep = \
- (ds.get_parameter('Sideband frequency step'))['GHz']
- sidebandCount = \
- ds.get_parameter('Number of sideband frequencies')
- datapoints = (ds.get()).asarray
- corrector.loadSidebandCal(datapoints, sidebandStep, dataset)
- # eliminate obsolete zero calibrations
- datasets = corrector.eliminateSidebandCals()
- # and save which ones are being used now
- reg.cd(['', keys.SESSIONNAME, boardname], True)
- reg.set(keys.IQNAME, datasets)
- cxn.disconnect()
- return corrector
-
-
-def runsequence(sram, cor):
- with labrad.connection() as cxn:
- fpga = cxn.ghz_dacs
- fpga.select_device(cor.board)
- if hasattr(cor, 'channel') and cor.channel == 1:
- sram = sram << 14
- sram[0:4] |= 0xF << 28
- fpga.run_sram(sram, True)
-
-
-
-
diff --git a/ghzdac/calibrate.py b/ghzdac/calibrate.py
deleted file mode 100644
index 2edaadfb..00000000
--- a/ghzdac/calibrate.py
+++ /dev/null
@@ -1,683 +0,0 @@
-# Copyright (C) 2007-2008 Max Hofheinz
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-# This module contains the calibration scripts. They must not require any
-# user interaction because they are used not only for the initial
-# calibration but also for recalibration. The user interface is provided
-# by GHz_DAC_calibrate in "scripts".
-
-import time
-import numpy as np
-import labrad
-from labrad.types import Value
-import keys
-
-#trigger to be set:
-#0x1: trigger S0
-#0x2: trigger S1
-#0x4: trigger S2
-#0x8: trigger S3
-#e.g. 0xA sets trigger S1 and S3
-trigger = 0xFL << 28
-
-FPGA_SERVER_NAME = 'ghz_fpgas'
-
-DACMAX= 1 << 13 - 1
-DACMIN= 1 << 13
-PERIOD = 2000
-SCOPECHANNEL_infiniium = 1
-TRIGGERCHANNEL_infiniium = 2
-
-SEQUENCE_LENGTH = 64
-
-
-def assertSpecAnalLock(server, device):
- p = server.packet()
- p.select_device(device)
- p.query_10_mhz_ref(key='ref')
- ans = p.send()
- if ans['ref'] != 'EXT':
- raise Exception('Spectrum analyzer %s external 10MHz reference not locked!' %device)
-
-
-def microwaveSourceServer(cxn, ID):
- anritsus = cxn.anritsu_server.list_devices()
- anritsus = [dev[1] for dev in anritsus]
- hittites = cxn.hittite_t2100_server.list_devices()
- hittites = [dev[1] for dev in hittites]
- if ID in anritsus:
- server = 'anritsu_server'
- elif ID in hittites:
- server = 'hittite_t2100_server'
- else:
- raise Exception('Microwave source %s not found' %ID)
- return (cxn.servers[server])
-
-
-def validSBstep(f):
- return round(0.5*np.clip(f,2.0/PERIOD,1.0)*PERIOD)*2.0/PERIOD
-
-
-def spectInit(spec):
- spec.gpib_write(':POW:RF:ATT 0dB;:AVER:STAT OFF;:FREQ:SPAN 100Hz;:BAND 300Hz;:INIT:CONT OFF;:SYST:PORT:IFVS:ENAB OFF;:SENS:SWE:POIN 101')
-
-def spectDeInit(spec):
- spec.gpib_write(':INIT:CONT ON')
-
-
-def spectFreq(spec,freq):
- spec.gpib_write(':FREQ:CENT %gGHz' % freq)
-
-
-def signalPower(spec):
- """returns the mean power in mW read by the spectrum analyzer"""
- dBs = spec.gpib_query('*TRG;*OPC?;:TRAC:MATH:MEAN? TRACE1')
- dBs=dBs.split(';')[1]
- return (10.0**(0.1*float(dBs)))
-
-
-def makeSample(a,b):
- """computes sram sample from dac A and B values"""
- if (np.max(a) > 0x1FFF) or (np.max(b) > 0x1FFF) or \
- (np.min(a) < -0x2000) or (np.min(b) < -0x2000):
- print 'DAC overflow'
- return long(a & 0x3FFFL) | (long(b & 0x3FFFL) << 14)
-
-
-def measurePower(spec,fpga,a,b):
- """returns signal power from the spectrum analyzer"""
- dac = [makeSample(a,b)] * SEQUENCE_LENGTH
- dac[0] |= trigger
- # fpga.dac_run_sram(dac,True)
- fpga.dac_write_sram(dac)
- return ((signalPower(spec)))
-
-
-def datasetNumber(dataset):
- return int(dataset[1][:5])
-
-
-def datasetDir(dataset):
- result = ''
- dataset = dataset[0]+[dataset[1]]
- for s in dataset[1:]:
- result += " >> " + s
- return result
-
-
-def minPos(l, c, r):
- """Calculates minimum of a parabola to three equally spaced points.
- The return value is in units of the spacing relative to the center point.
- It is bounded by -1 and 1.
- """
- d = l+r-2.0*c
- if d <= 0:
- return 0
- d = 0.5*(l-r)/d
- if d > 1:
- d = 1
- if d < -1:
- d = -1
- return d
-
-
-####################################################################
-# DAC zero calibration #
-####################################################################
-
-
-def zero(anr, spec, fpga, freq):
- """Calibrates the zeros for DAC A and B using the spectrum analyzer"""
-
- anr.frequency(Value(freq,'GHz'))
- spectFreq(spec,freq)
- a = 0
- b = 0
- precision = 0x800
- print ' calibrating at %g GHz...' % freq
- while precision > 0:
- fpga.dac_run_sram([0] * SEQUENCE_LENGTH, True)
- al = measurePower(spec, fpga, a-precision, b)
- ar = measurePower(spec, fpga, a+precision, b)
- ac = measurePower(spec, fpga, a, b)
- corra = long(round(precision*minPos(al, ac, ar)))
- a += corra
-
- bl = measurePower(spec, fpga, a, b-precision)
- br = measurePower(spec, fpga, a, b+precision)
- bc = measurePower(spec, fpga, a, b)
- corrb = long(round(precision*minPos(bl, bc, br)))
- b += corrb
- optprec = 2*np.max([abs(corra), abs(corrb)])
- precision /= 2
- if precision > optprec:
- precision = optprec
- print ' a = %4d b = %4d uncertainty : %4d, power %6.1f dBm' % \
- (a, b, precision, 10 * np.log(bc) / np.log(10.0))
- return ([a, b])
-
-
-def zeroFixedCarrier(cxn, boardname, use_switch=True):
- reg = cxn.registry
- reg.cd(['',keys.SESSIONNAME,boardname])
-
- fpga = cxn[FPGA_SERVER_NAME]
- fpga.select_device(boardname)
-
- if use_switch:
- switch = cxn.microwave_switch
- switch.switch(boardname)
-
- spec = cxn.spectrum_analyzer_server
- spectID = reg.get(keys.SPECTID)
- spec.select_device(spectID)
- spectInit(spec)
- assertSpecAnalLock(spec, spectID)
- uwaveSourceID = reg.get(keys.ANRITSUID)
- uwaveSource = microwaveSourceServer(cxn, uwaveSourceID)
-
- uwavePower = reg.get(keys.ANRITSUPOWER)
- frequency = (reg.get(keys.PULSECARRIERFREQ))['GHz']
- uwaveSource.select_device(uwaveSourceID)
- uwaveSource.amplitude(uwavePower)
- uwaveSource.output(True)
-
- print 'Zero calibration...'
-
- daczeros = zero(uwaveSource, spec, fpga, frequency)
-
- uwaveSource.output(False)
- spectDeInit(spec)
- if use_switch:
- switch.switch(0)
- return daczeros
-
-
-
-def zeroScanCarrier(cxn, scanparams, boardname, use_switch=True):
- """Measures the DAC zeros in function of the carrier frequency."""
- reg = cxn.registry
- reg.cd(['', keys.SESSIONNAME, boardname])
-
- fpga = cxn[FPGA_SERVER_NAME]
- fpga.select_device(boardname)
-
- if use_switch:
- switch = cxn.microwave_switch
- switch.switch(boardname)
-
- spec = cxn.spectrum_analyzer_server
- spectID = reg.get(keys.SPECTID)
- spec.select_device(spectID)
- spectInit(spec)
- assertSpecAnalLock(spec, spectID)
- uwaveSourceID = reg.get(keys.ANRITSUID)
- uwaveSource = microwaveSourceServer(cxn, uwaveSourceID)
- uwavePower = reg.get(keys.ANRITSUPOWER)
- uwaveSource.select_device(uwaveSourceID)
- uwaveSource.amplitude(uwavePower)
- uwaveSource.output(True)
-
- print 'Zero calibration from %g GHz to %g GHz in steps of %g GHz...' % \
- (scanparams['carrierMin'],scanparams['carrierMax'],scanparams['carrierStep'])
- ds = cxn.data_vault
- ds.cd(['', keys.SESSIONNAME, boardname], True)
- dataset = ds.new(keys.ZERONAME,
- [('Frequency', 'GHz')],
- [('DAC zero', 'A', 'clics'),
- ('DAC zero', 'B', 'clics')])
- ds.add_parameter(keys.ANRITSUPOWER, uwavePower)
-
- freq = scanparams['carrierMin']
- while freq < scanparams['carrierMax']+0.001*scanparams['carrierStep']:
- ds.add([freq]+(zero(uwaveSource, spec, fpga, freq)))
- freq += scanparams['carrierStep']
- uwaveSource.output(False)
- spectDeInit(spec)
- if use_switch:
- cxn.microwave_switch.switch(0)
- return (int(dataset[1][:5]))
-
-####################################################################
-# Pulse calibration #
-####################################################################
-
-
-def measureImpulseResponse(fpga, scope, baseline, pulse, dacoffsettime=6, pulselength=1):
- """Measure the response to a DAC pulse
- fpga: connected fpga server
- scope: connected scope server
- dac: 'a' or 'b'
- returns: list
- list[0] : start time (s)
- list[1] : time step (s)
- list[2:]: actual data (V)
- """
- #units clock cycles
- dacoffsettime = int(round(dacoffsettime))
- triggerdelay = 30
- looplength = 2000
- pulseindex = triggerdelay-dacoffsettime
- scope.start_time(Value(triggerdelay, 'ns'))
- #calculate the baseline voltage by capturing a trace without a pulse
-
- data = np.resize(baseline, looplength)
- data[pulseindex:pulseindex+pulselength] = pulse
- data[0] |= trigger
- fpga.dac_run_sram(data.astype('u4'),True)
- data = (scope.get_trace(1))
- data[0] -= Value(triggerdelay*1e-9, 'V') # TODO: not sure about units here--pjjo
- return (data)
-
-
-def measureImpulseResponse_infiniium(fpga, scope, baseline, pulse,
- dacoffsettime=6, pulselength=1, wait=75, looplength=6000):
- """Measure the response to a DAC pulse
- looplength: time between triggers, keep this above pulselength , 6000 for short dacs
- fpga: connected fpga server
- scope: connected scope server
- dac: 'a' or 'b'
- returns: list
- list[0] : start time (s)
- list[1] : time step (s)
- list[2:]: actual data (V)
- """
- #units clock cycles
- dacoffsettime = int(round(dacoffsettime))
- triggerdelay = 30 #keep at least at 30
- pulseindex = triggerdelay-dacoffsettime
-
- data = np.resize(baseline, looplength)
- data[pulseindex:pulseindex+pulselength] = pulse
- data[0] |= trigger
- fpga.dac_run_sram(data,True)
- if wait:
- time.sleep(wait) #keep this long enough!! 40 sec for 4096, 20 sec for 2048
-
- t, y = scope.get_trace(SCOPECHANNEL_infiniium) #start and stop in ns
-
- # Truncate data before t=0
- after_zero_idx = np.argwhere(t > 0).flatten()
- t = t[after_zero_idx]
- y = y[after_zero_idx]
-
- return t, y
-
-
-def calibrateACPulse(cxn, boardname, baselineA, baselineB, use_switch=True):
- """Measures the impulse response of the DACs after the IQ mixer"""
- pulseheight = 0x1800
-
- reg = cxn.registry
- reg.cd(['', keys.SESSIONNAME, boardname])
-
- if use_switch:
- switch = cxn.microwave_switch
-
- uwaveSourceID = reg.get(keys.ANRITSUID)
- uwaveSource = microwaveSourceServer(cxn,uwaveSourceID)
- uwaveSourcePower = reg.get(keys.ANRITSUPOWER)
- carrierFreq = reg.get(keys.PULSECARRIERFREQ)
- sens = reg.get(keys.SCOPESENSITIVITY)
- offs = reg.get(keys.SCOPEOFFSET, True, Value(0, 'mV'))
- if use_switch:
- switch.switch(boardname) #Hack to select the correct microwave switch
- switch.switch(0)
- uwaveSource.select_device(uwaveSourceID)
- uwaveSource.frequency(carrierFreq)
- uwaveSource.amplitude(uwaveSourcePower)
- uwaveSource.output(True)
-
- #Set up the scope
- scope = cxn.sampling_scope
- scopeID = reg.get(keys.SCOPEID)
- scope.select_device(scopeID)
- p = scope.packet().\
- reset().\
- channel(reg.get(keys.SSCOPECHANNEL, True, 2)).\
- trace(1).\
- record_length(5120L).\
- average(128).\
- sensitivity(sens).\
- offset(offs).\
- time_step(Value(2,'ns')).\
- trigger_level(Value(0.18,'V')).\
- trigger_positive()
- p.send()
-
- fpga = cxn[FPGA_SERVER_NAME]
- fpga.select_device(boardname)
- offsettime = reg.get(keys.TIMEOFFSET)
-
- baseline = makeSample(baselineA,baselineB)
-# print "Measuring offset voltage..."
-# offset = (measureImpulseResponse(fpga, scope, baseline, baseline))[2:]
-# offset = sum(offset) / len(offset)
-
- print "Measuring pulse response DAC A..."
- traceA = measureImpulseResponse(fpga, scope, baseline,
- makeSample(baselineA+pulseheight,baselineB),
- dacoffsettime=offsettime['ns'])
-
- print "Measuring pulse response DAC B..."
- traceB = measureImpulseResponse(fpga, scope, baseline,
- makeSample(baselineA,baselineB+pulseheight),
- dacoffsettime=offsettime['ns'])
-
- starttime = traceA[0]
- timestep = traceA[1]
- if (starttime != traceB[0]) or (timestep != traceB[1]) :
- print """Time scales are different for measurement of DAC A and B.
- Did you change settings on the scope during the measurement?"""
- exit
- #set output to zero
- fpga.dac_run_sram([baseline]*20)
- uwaveSource.output(False)
- ds = cxn.data_vault
- ds.cd(['',keys.SESSIONNAME,boardname],True)
- dataset = ds.new(keys.PULSENAME,[('Time','ns')],
- [('Voltage','A','V'),('Voltage','B','V')])
- setupType = reg.get(keys.IQWIRING)
- ds.add_parameter(keys.IQWIRING, setupType)
- ds.add_parameter(keys.PULSECARRIERFREQ, carrierFreq)
- ds.add_parameter(keys.ANRITSUPOWER, uwaveSourcePower)
- ds.add_parameter(keys.TIMEOFFSET, offsettime)
- # begin unit strip party
- starttime = starttime[starttime.unit] # stripping units
- timestep = timestep[timestep.unit] # stripping units
- traceA = traceA[traceA.unit] # stripping units
- traceB = traceB[traceB.unit] # stripping units
- data = np.transpose(\
- [1e9*(starttime+timestep*np.arange(np.alen(traceA)-2)),
- traceA[2:],traceB[2:]])
- ds.add(data)
-# traceA[2:]-offset,
-# traceB[2:]-offset]))
- if np.abs(np.argmax(np.abs(traceA-np.average(traceA))) - \
- np.argmax(np.abs(traceB-np.average(traceB)))) \
- * timestep > 0.5e-9:
- print "Pulses from DAC A and B do not seem to be on top of each other!"
- print "Sideband calibrations based on this pulse calibration will"
- print "most likely mess up you sequences!"
- print
- print "Check the following pulse calibration file in the data vault:"
- print datasetDir(dataset)
- print "If the pulses are offset by more than 0.5 ns,"
- print "bring up the board and try the pulse calibration again."
- print 5
- return (datasetNumber(dataset))
-
-
-def calibrateDCPulse(cxn,boardname,channel):
-
- reg = cxn.registry
- reg.cd(['',keys.SESSIONNAME,boardname])
-
- fpga = cxn[FPGA_SERVER_NAME]
- fpga.select_device(boardname)
-
- dac_baseline = -0x2000
- dac_pulse = 0x1FFF
- dac_neutral = 0x0000
- if channel:
- pulse = makeSample(dac_neutral, dac_pulse)
- baseline = makeSample(dac_neutral, dac_baseline)
- else:
- pulse = makeSample(dac_pulse, dac_neutral)
- baseline = makeSample(dac_baseline, dac_neutral)
- #Set up the scope
- scope = cxn.sampling_scope
- scopeID = reg.get(keys.SCOPEID)
- print "scopeID:", scopeID
- p = scope.packet().\
- select_device(scopeID).\
- reset().\
- channel(reg.get(keys.SSCOPECHANNEL, True, 2)).\
- trace(1).\
- record_length(5120).\
- average(128).\
- sensitivity(reg.get(keys.SSCOPESENSITIVITYDC, True, 200*labrad.units.mV)).\
- offset(Value(0,'mV')).\
- time_step(Value(5,'ns')).\
- trigger_level(Value(0.18,'V')).\
- trigger_positive()
- p.send()
-
- offsettime = reg.get(keys.TIMEOFFSET)
-
-
-
- print 'Measuring step response...'
- trace = measureImpulseResponse(fpga, scope, baseline, pulse,
- dacoffsettime=offsettime['ns'], pulselength=100)
- trace = trace[trace.unit] # strip units
- # set the output to zero so that the fridge does not warm up when the
- # cable is plugged back in
- fpga.dac_run_sram([makeSample(dac_neutral, dac_neutral)]*20, False)
- ds = cxn.data_vault
- ds.cd(['', keys.SESSIONNAME, boardname],True)
- dataset = ds.new(keys.CHANNELNAMES[channel], [('Time','ns')],
- [('Voltage','','V')])
- ds.add_parameter(keys.TIMEOFFSET, offsettime)
- ds.add(np.transpose([1e9*(trace[0]+trace[1]*np.arange(np.alen(trace)-2)),
- trace[2:]]))
- return (datasetNumber(dataset))
-
-
-def calibrateDCPulse_infiniium(cxn, boardname, channel, conf_10_MHz):
-
- reg = cxn.registry
- reg.cd(['', keys.SESSIONNAME,boardname])
-
- fpga = cxn[FPGA_SERVER_NAME]
- fpga.select_device(boardname)
-
- dac_baseline = 0x000
- dac_pulse = 0x1000
- dac_neutral = 0x0000
- if channel:
- pulse = makeSample(dac_neutral,dac_pulse)
- baseline = makeSample(dac_neutral, dac_baseline)
- else:
- pulse = makeSample(dac_pulse, dac_neutral)
- baseline = makeSample(dac_baseline, dac_neutral)
- scope = cxn.agilent_infiniium_oscilloscope()
- scope.select_device()
-
- scope.reset()
- print 'scope reset'
-
- fpga.dac_run_sram([makeSample(dac_neutral, dac_neutral)]*20, False) # zero dac output
- time.sleep(2)
-
- numberofaverages=4096
-
- p = scope.packet().\
- gpib_write('TIM:REFC '+str(conf_10_MHz)).\
- channelonoff(SCOPECHANNEL_infiniium, 'ON').\
- channelonoff(TRIGGERCHANNEL_infiniium, 'ON').\
- scale(SCOPECHANNEL_infiniium, 0.1).\
- scale(TRIGGERCHANNEL_infiniium, 0.5).\
- position(SCOPECHANNEL_infiniium, 0.02).\
- position(TRIGGERCHANNEL_infiniium,0.0).\
- horiz_scale(500.0e-9).\
- horiz_position(0.0).\
- trigger_sweep('TRIG').\
- trigger_mode('EDGE').\
- trigger_edge_slope('POS').\
- trigger_at(TRIGGERCHANNEL_infiniium, 1.0).\
- averagemode(1).\
- numavg(numberofaverages)
-
- p.send()
- print 'scope packet sent'
- time.sleep(1)
- ref_10_MHz = scope.gpib_query(':TIM:REFC?')
-
- if ref_10_MHz == '1':
- ref_str_rep = 'EXT'
- else:
- ref_str_rep = 'INT'
- print '10 MHz ref: ' + ref_str_rep
-
- offsettime = reg.get(keys.TIMEOFFSET)
-
- print 'Measuring step response...'
- t, y = measureImpulseResponse_infiniium(fpga, scope, baseline, pulse,
- dacoffsettime=offsettime['ns'], pulselength=3000, looplength=6000)
-
- # set the output to zero so that the fridge does not warm up when the
- # cable is plugged back in
- fpga.dac_run_sram([makeSample(dac_neutral, dac_neutral)]*20, False) # Zero output
- ds = cxn.data_vault
- ds.cd(['', keys.SESSIONNAME, boardname],True)
- dataset = ds.new(keys.CHANNELNAMES[channel], [('Time', 'ns')],
- [('Voltage', '', 'V')])
- ds.add_parameter(keys.TIMEOFFSET, offsettime)
- ds.add_parameter('dac baseline', dac_baseline)
- ds.add_parameter('dac pulse', dac_pulse)
- ds.add_parameter('10 MHz ref', ref_str_rep)
- ds.add_parameter('scope', 'Agilent13GHz')
- ds.add_parameter('stats', numberofaverages)
- ds.add(np.vstack((t['ns'], y['V'])).transpose())
- return datasetNumber(dataset)
-
-
-####################################################################
-# Sideband calibration #
-####################################################################
-
-
-def measureOppositeSideband(spec, fpga, corrector,
- carrierfreq, sidebandfreq, compensation):
- """Put out a signal at carrierfreq+sidebandfreq and return the power at
- carrierfreq-sidebandfreq"""
-
- arg = -2.0j*np.pi*sidebandfreq*np.arange(PERIOD)
- signal = corrector.DACify(carrierfreq,
- 0.5 * np.exp(arg) + 0.5 * compensation * np.exp(-arg),
- loop=True, iqcor=False, rescale=True)
- for i in range(4):
- signal[i] |= trigger
- fpga.dac_run_sram(signal, True)
- return ((signalPower(spec)) / corrector.last_rescale_factor)
-
-
-def sideband(anr, spect, fpga, corrector, carrierfreq, sidebandfreq):
- """When the IQ mixer is used for sideband mixing, imperfections in the
- IQ mixer and the DACs give rise to a signal not only at
- carrierfreq+sidebandfreq but also at carrierfreq-sidebandfreq.
- This routine determines amplitude and phase of the sideband signal
- for carrierfreq-sidebandfreq that cancels the undesired sideband at
- carrierfreq-sidebandfreq."""
- reserveBuffer = corrector.dynamicReserve
- corrector.dynamicReserve = 4.0
-
- if abs(sidebandfreq) < 3e-5:
- return (0.0j)
- anr.frequency(Value(carrierfreq,'GHz'))
- comp = 0.0j
- precision = 1.0
- spectFreq(spect,carrierfreq-sidebandfreq)
- while precision > 2.0**-14:
- fpga.dac_run_sram(np.array([0] * PERIOD, dtype='.
-
-
-import numpy as np
-
-# CHANGELOG
-#
-# 2012 April 12 - Jim Wenner
-#
-# Changed logic string in setSettling from np.any(self.decayRates!=rates)
-# to not (np.array_equal(self.decayRates,rates). With any(!=), if
-# self.decayRates is empty, the output will be an empty array and not
-# False, so the section to change self.decayRates is not entered.
-
-# CHANGELOG
-#
-# 2013 April/May - R. Barends
-#
-# Changes in Z board (Single) DAC calibration:
-#
-# Rewrote LoadCal to be more intelligible
-#
-# Introduced cutoff frequency. This is necessary because high frequency signals in the correction window have a bad S/N ratio.
-# In addition, the way the impulse response is calculated suppresses 1 GHz noise, but amplifies 500 MHz. Default value is to cut off at 450 MHz. Keep it above 350 MHz.
-# Also, these high frequencies tend to give rise to long oscillations in the deconvolved timetrace, messing up dualblock.
-#
-# Truncation of large correction factors in the fourier domain: the correction window can have very large amplitudes (and low S/N),
-# therefore the time domain signal can have large oscillations which will be truncated digitally, leading to deterioration of the waveform.
-# Now, a maximum value is enforced in the fourier domain. The value is truncated but the phase is kept.
-# This way we still have a partial correction, within the limits of the boards. Doing it this way also ensures that the waveforms are scalable.
-#
-# Cubic interpolation for the fourier transform. It visually reduced the ringing on the scope. Cubic interpolation algorithm is as fast as linear interpolation.
-#
-# The deconv now does NOT shift the timetrace. This arose from the rise being at t=10-20 ns, instead of t=0.
-# However, this leads to the timetrace running out of its intendend memory block. In addition, the phase varies more slowly, easing interpolation.
-#
-# Fixed logical bug in setSettling
-#
-# Enforces borderValues at first and last 4 points. The deconvolved signal can be nonzero at the start and end of a sequence.
-# This nonzero value persists, even when running the board with an empty envelope. Hence, the Z bias is in a small-valued but arbitrary state after each run,
-# possibly even oscillating, if the 4 last values are not identical. To remove this, the last 4 (FOUR) values are set to 0.0.
-# For dualblock, there is a function 'set_border_values' to set the bordervalues. Be sure to set it for both the first and last block, in fpgaseqtransmon.
-#
-#
-# Changes to IQ board DAC calibration:
-#
-# Enforces zeros at first and last 4 points. The deconvolved signal can be nonzero at the start and end of a sequence.
-# This nonzero value persists, even when running the board with an empty envelope. To remove this, the last 4 (FOUR) values must be set.
-#
-# Cubic interpolation for zero value
-#
-# Removed a tab
-
-def cosinefilter(n, width=0.4):
- """cosinefilter(n,width) cosine lowpass filter
- n samples from 0 to 1 GHz
- 1 from 0 GHz to width GHz
- rolls of from width GHz to 0.5 GHz like a quater cosine wave"""
- nr = n/2 + 1
- result = np.ones(nr,dtype=float)
- start = int(np.ceil(width*n))
- width = (0.5-width)*n
- if start < nr:
- result[start:] = 0.5+0.5*np.cos(
- np.linspace(np.pi * (start-0.5*n+width) / width,
- np.pi + np.pi / width*(nr-0.5*n),
- nr-start, endpoint=False))
- return result
-
-
-def gaussfilter(n, width=0.13):
- """lowpassfilter(n,width) gaussian lowpass filter.
- n samples from 0 to 1 GHz
- -3dB frequency at width GHz
- """
- nr = n/2 + 1
- x = 1.0 / width * np.sqrt(np.log(2.0)/2.0)
- gauss = np.exp(-np.linspace(0, x*nr/n, nr, endpoint=False)**2)
- x = np.exp(-(0.5*x)**2)
- gauss -= x
- gauss /= (1.0 - x)
- return gauss
-
-
-def flatfilter(n, width=0):
- nr = n/2 + 1
- return 1.0
- #return np.ones(nr)
-
-
-savedfftlens = np.zeros(8193,dtype=int)
-
-
-def fastfftlen(n):
- """
- Computes the smallest multiple of 2 3 and 5 larger or equal n.
- FFT is fastest for sizes that factorize in small numbers.
- Sizes up to 8192 are only calculated once and later looked up.
- """
- def _fastfftlen(n):
- logn = np.log(n)
- n5 = 5L ** np.arange(long(logn/np.log(5.) + 2. + 1.e-6))
- n3 = 3L ** np.arange(long(logn/np.log(3.) + 2. + 1.e-6))
- n35 = np.outer(n3, n5).flat
- n35 = np.compress(n35<2*n, n35)
- n235 = ((-np.log(n35)+logn)/np.log(2.) + 0.999999).astype(int)
- n235 *= (n235>0)
- n235 = 2**n235 * n35
- return np.min(n235)
-
- if n < np.alen(savedfftlens):
- nfft = savedfftlens[n]
- if nfft < n:
- nfft = _fastfftlen(n)
- savedfftlens[n] = nfft
- return nfft
- else:
- return _fastfftlen(n)
-
-def moving_average(x,m):
- """Moving average on x, with length m. Expects a numpy array for x. Elements are given by
- y[i] = Sum_{k=0..m-1} y[l] / m
- with l=i-fix(m/2)+k between 0 and length(x)-1. Try to keep m odd. RB."""
- n=np.alen(x)
- before=-np.fix(int(m)/2.0)
- y=[]
- for i in np.arange(len(x)):
- a=0.0
- for tel in np.arange(int(m)):
- idx=i+before+tel
- if idx<0:
- idx=0
- elif idx>=n:
- idx=n-1
- a += x[idx]/np.float(m)
- y.append(a)
- return np.array(y)
-
-
-def derivative(x,y):
- """Taking derivative, uses both adjacent points for estimate of derivative.
- Returns array with the same number of points (different than np.diff). RB."""
- n=np.alen(x)
- deriv=np.array(np.linspace(0.0,0.0,n),dtype=complex)
- for k in np.arange(n):
- if k==0:
- deriv[k]=1.0*(y[k+1]-y[k])/(x[k+1]-x[k])
- elif k==(n-1):
- deriv[k]=1.0*(y[k]-y[k-1])/(x[k]-x[k-1])
- else:
- deriv[k]=1.0*(y[k+1]-y[k-1])/(x[k+1]-x[k-1])
- return deriv
-
-
-def interpol_cubic(h,x2,fill_value=None):
- """Fast cubic interpolator (slightly faster than linear version of scipy interp1d;
- much faster than cubic version of scipy interp1d).
- Returns the values in in the same way interpol. Can deal with complex input.
- Uses linear interpolation at the edges, and returns the values at the edges outside of the range. RB."""
- xlen=np.alen(h)
- if type(h) is not np.ndarray:
- #we need a numpy array
- h=1.0*np.array(h)
- def func(xdet):
- if type(xdet) is not list and type(xdet) is not np.ndarray:
- xdet=np.array([xdet])
- yout=np.zeros(np.alen(xdet)).astype(h.dtype) #predefine
- x2=xdet #x2 = (xdet-xstart) #map xdet onto h index: x -> (x-xstart)/dx = 0... length
-
- #indices outside of the range
- xdet_idx = x2<0 #maps which index in x2 it is
- if xdet_idx.any():
- x2_idx = x2[ xdet_idx ] #maps x2 to x index
- h_idx = np.array(x2_idx).astype(int) #maps which h,x to take
- if fill_value is None:
- yout[xdet_idx]=h[0]
- else:
- yout[xdet_idx]=fill_value
- xdet_idx = x2>(xlen-1) #maps which index in x2 it is
- if xdet_idx.any():
- x2_idx = x2[ xdet_idx ] #maps x2 to x index
- h_idx = np.array(x2_idx).astype(int) #maps which h,x to take
- if fill_value is None:
- yout[xdet_idx]=h[xlen-1]
- else:
- yout[xdet_idx]=fill_value
-
- #indices on the rim: linear interpolation
- xdet_idx = np.logical_and(x2>=0,x2<1) #maps which index in x2 it is
- if xdet_idx.any():
- x2_idx = x2[ xdet_idx ] #maps x2 to x index
- h_idx = np.array(x2_idx).astype(int) #maps which h,x to take
- yout[xdet_idx]=(h[1]-h[0])*x2_idx + h[0]
- xdet_idx = np.logical_and(x2>=(xlen-2),x2<=(xlen-1)) #maps which index in x2 it is
- if xdet_idx.any():
- x2_idx = x2[ xdet_idx ] #maps x2 to x index
- h_idx = np.array(x2_idx).astype(int) #maps which h,x to take
- yout[xdet_idx]=(h[xlen-1]-h[xlen-2])*(x2_idx-h_idx[0]) + h[xlen-2]
-
- #indices inside the range: cubic interpolation
- xdet_idx = np.logical_and(x2>=1,x2<(xlen-2)) #maps which index in x2 it is
- if xdet_idx.any():
- x2_idx = x2[ xdet_idx ] #maps x2 to x index
- h_idx = np.array(x2_idx).astype(int) #maps which h,x to take
- hp2=h[h_idx+2]
- hp1=h[h_idx+1]
- hp0=h[h_idx]
- hm1=h[h_idx-1]
- d=hp0
- c=(hp1-hm1)/2.
- b=(-hp2+4*hp1-5*hp0+2*hm1)/2.
- a=(hp2-3*hp1+3*hp0-hm1)/2.
- xi=(x2_idx - h_idx)
- yout[xdet_idx]=((a * xi + b) * xi + c) * xi + d
-
- return np.array(yout)
- return func(x2)
-
-
-def interpol(signal, x, extrapolate=False):
- """
- Linear interpolation of array signal at floating point indices x
- (x can be an array or a scalar). If x is beyond range either the first or
- last element is returned. If extrapolate=True, the linear extrapolation of
- the first/last two points is returned instead.
- """
- n = np.alen(signal)
- if n == 1:
- return signal[0]
- i = np.floor(x).astype(int)
- i = np.clip(i, 0, n-2) #assumes x is between 0 and n-1
- p = x - i
- if not extrapolate:
- p = np.clip(p,0.0,1.0)
- return signal[i] * (1.0 - p) + signal[i+1] * p
-
-
-def findRelevant(starts, ends):
- n = np.size(starts)
- relevant = np.resize(True, n)
- for i in np.arange(n-1):
- relevant[i] = not np.any((starts[i+1:] <= starts[i]) &
- (ends[i+1:] >= ends[i]))
- return np.argwhere(relevant)[:,0]
-
-
-##################################################
-# #
-# Correction class for a DAC board with IQ mixer #
-# #
-##################################################
-
-
-class IQcorrection:
-
- def __init__(self, board, lowpass=cosinefilter, bandwidth=0.4,
- exceedCalLimits=0.001):
-
- """
- Returns a DACcorrection object for the given DAC board.
- """
-
- self.board = board
- #Set dynamic reserve
- self.dynamicReserve = 2.0
-
- #Use this to see how much the last DACify call rescaled the output
- self.last_rescale_factor = 1.0
- #Use this to see the smallest rescale factor DACify had to use
- self.min_rescale_factor = 1.0
-
- self.flipChannels = False
-
- # Set the Lowpass, i.e. the transfer function we want after correction
- # Unless otherwise specified, the filter will be flat and then roll off
- # between (1-bandwidth)*Nyquist and Nyquist
-
- if lowpass == False:
- lowpass = flatfilter
-
- self.lowpass = lowpass
- self.bandwidth = bandwidth
- self.exceedCalLimits = exceedCalLimits
-
- # empty pulse calibration
- self.correctionI = None
- self.correctionQ = None
- self.pulseCalFile = None
-
- # empty zero calibration
- self.zeroTableStart = np.zeros(0,dtype=float)
- self.zeroTableEnd = np.zeros(0,dtype=float)
- self.zeroTableStep = np.zeros(0,dtype=float)
- self.zeroCalFiles = np.zeros(0,dtype=int)
- self.zeroTableI = []
- self.zeroTableQ = []
-
- # empty sideband calibration
- self.sidebandCarrierStart = np.zeros(0,dtype=float)
- self.sidebandCarrierEnd = np.zeros(0,dtype=float)
- self.sidebandCarrierStep = np.zeros(0,dtype=float)
- self.sidebandStep = np.zeros(0,dtype=float)
- self.sidebandCount = np.zeros(0)
- self.sidebandCompensation = []
- self.sidebandCalFiles = np.zeros(0,dtype=int)
-
- self.selectCalAll()
-
- self.recalibrationRoutine = None
-
-
- def loadZeroCal(self, zeroData, calfile):
- l = np.shape(zeroData)[0]
- self.zeroTableI.append(zeroData[:,(1 + self.flipChannels)])
- self.zeroTableQ.append(zeroData[:,(1 + (not self.flipChannels))])
- self.zeroTableStart = np.append(self.zeroTableStart, zeroData[0,0])
- self.zeroTableEnd = np.append(self.zeroTableEnd, zeroData[-1,0])
- self.zeroCalFiles = np.append(self.zeroCalFiles, calfile)
- if l > 1:
- self.zeroTableStep = np.append(self.zeroTableStep,
- zeroData[1,0]-zeroData[0,0])
- print ' carrier frequencies: %g GHz to %g GHz in steps of %g MHz' % \
- (zeroData[0,0], zeroData[-1,0],
- self.zeroTableStep[-1]*1000.0)
-
- else:
- self.zeroTableStep = np.append(self.zeroTableStep, 1.0)
- print ' carrier frequency: %g GHz' % zeroData[0,0]
-
-
- def eliminateZeroCals(self):
- """
- Eliminate zero calibrations that have become obsolete.
- Returns the zero calibration files that are still used.
- You should not need to call this function. It is used internally
- during a recalibration.
- """
- keep = findRelevant(self.zeroTableStart,self.zeroTableEnd)
- self.zeroTableI = [self.zeroTableI[i] for i in keep]
- self.zeroTableQ = [self.zeroTableQ[i] for i in keep]
- self.zeroTableStart = self.zeroTableStart[keep]
- self.zeroTableEnd = self.zeroTableEnd[keep]
- self.zeroTableStep = self.zeroTableStep[keep]
- self.zeroCalFiles = self.zeroCalFiles[keep]
- return self.zeroCalFiles
-
-
- def loadSidebandCal(self, sidebandData, sidebandStep, calfile):
- """
- Load IQ sideband mixing calibration
- """
- self.sidebandStep = np.append(self.sidebandStep, sidebandStep)
-
- l,sidebandCount = np.shape(sidebandData)
- sidebandCount = (sidebandCount-1)/2
-
- self.sidebandCarrierStart = np.append(self.sidebandCarrierStart,
- sidebandData[0,0])
- self.sidebandCarrierEnd = np.append(self.sidebandCarrierEnd,
- sidebandData[-1,0])
- if l>1:
- self.sidebandCarrierStep = np.append(self.sidebandCarrierStep,
- sidebandData[1,0] - sidebandData[0,0])
- print ' carrier frequencies: %g GHz to %g GHz in steps of %g MHz' % \
- (sidebandData[0,0],
- sidebandData[-1,0],
- self.sidebandCarrierStep[-1]*1000.0)
- else:
- self.sidebandCarrierStep = np.append(self.sidebandCarrierStep,
- 1.0)
- print ' carrier frequency: %g GHz' % sidebandData[0,0]
-
- sidebandData = np.reshape(sidebandData[:,1:],(l,sidebandCount, 2))
- self.sidebandCompensation.append(
- sidebandData[:,:,0] + 1.0j * sidebandData[:,:,1])
- self.sidebandCalFiles = np.append(self.sidebandCalFiles, calfile)
- print ' sideband frequencies: %g MHz to %g Mhz in steps of %g MHz' % \
- (-500.0*(sidebandCount-1)*sidebandStep,
- 500.0*(sidebandCount-1)*sidebandStep,
- sidebandStep*1000)
-
-
- def eliminateSidebandCals(self):
- """
- Eliminate sideband calibrations that have become obsolete.
- Returns the sideband calibration files that are still used.
- You should not need to call this function. It is used internally
- during a recalibration.
- """
- keep = findRelevant(self.sidebandCarrierStart,self.sidebandCarrierEnd)
- self.sidebandCompensation = [self.sidebandCompensation[i] for i in keep]
- self.sidebandStep = self.sidebandStep[keep]
- self.sidebandCarrierStart = self.sidebandCarrierStart[keep]
- self.sidebandCarrierEnd = self.sidebandCarrierEnd[keep]
- self.sidebandCarrierStep = self.sidebandCarrierStep[keep]
- self.sidebandCalFiles = self.sidebandCalFiles[keep]
- return self.sidebandCalFiles
-
-
- def loadPulseCal(self, dataPoints, carrierfreq, calfile,
- flipChannels = False):
- """
- Demodulates the IQ mixer output with the carrier frequency.
- The result is inverted and multiplied with a lowpass filter, that rolls
- off between 0.5-cufoffwidth GHz and 0.5 GHz.
- It is stored in self.correctionI and self.correctionQ.
- """
- #read pulse calibration from data server
- self.flipChannels = flipChannels
- dataPoints = np.asarray(dataPoints)
- i = dataPoints[:,1 + self.flipChannels]
- q = dataPoints[:,1 + (not self.flipChannels)]
- # subtract DC offsets
- i -= np.average(i)
- q -= np.average(q)
- length = len(i)
- samplingfreq = int(np.round(1.0/(dataPoints[1,0]-dataPoints[0,0])))
- dataPoints = None
-
- #length for fft, long because we want good frequency resolution
- finalLength = 10240
- n = finalLength*samplingfreq
- print ' sampling frequency: %d GHz' % samplingfreq
-
- #convert carrier frequency to index
- carrierfreqIndex = carrierfreq*n/samplingfreq
-
- #if the carrier frequency doesn't fall on a frequency sampling point
- #we lose some precision
- if np.floor(carrierfreqIndex) < np.ceil(carrierfreqIndex):
- print """Warning: carrier frequency of calibration is not a multiple of %g MHz, accuracy may suffer.""" % 1000.0*samplingfreq/n
- carrierfreqIndex = int(np.round(carrierfreqIndex))
-
- #go to frequency space
- i = np.fft.rfft(i, n=n)
- q = np.fft.rfft(q, n=n)
-
- #demodulate
- low = i[carrierfreqIndex:carrierfreqIndex-finalLength/2-1:-1]
- high = i[carrierfreqIndex:carrierfreqIndex+finalLength/2+1:1]
- #calcualte the phase of the carrier
- phase = np.sqrt(np.sum(low*high))
- phase /= abs(phase)
- if (phase.conjugate()*low[0]).real < 0:
- phase *= -1
-
- self.correctionI = 1.0 / \
- (0.5 / abs(low[0]) * (np.conjugate(low/phase) + high/phase))
-
- low = q[carrierfreqIndex:carrierfreqIndex-finalLength/2-1:-1]
- high = q[carrierfreqIndex:carrierfreqIndex+finalLength/2+1:1]
- #calculate the phase of the carrier
- phase = np.sqrt(np.sum(low*high))
- phase /= abs(phase)
- if (phase.conjugate()*low[0]).real < 0:
- phase *= -1
- self.correctionQ = 1.0 / \
- (0.5 / abs(low[0]) * (np.conjugate(low/phase) + high/phase))
- #Make sure the correction does not get too large
- #If correction goes above 3 * dynamicReserve,
- #scale to 3 * dynamicReserve but preserve phase
- self.correctionI /= \
- np.clip(abs(self.correctionI) / 3. / self.dynamicReserve,
- 1.0, np.Inf)
- self.correctionQ /= \
- np.clip(abs(self.correctionQ) / 3. /self.dynamicReserve,
- 1.0, np.Inf)
- self.pulseCalFile = calfile
-
-
- def selectCalAll(self):
- """
- For each frequency use the lastest calibration available. This
- is the default behaviour.
- """
- self.zeroCalIndex = None
- self.sidebandCalIndex = None
- print 'For each correction the best calfile will be chosen.'
-
-
- def selectCalLatest(self):
- """
- Only use the latest calibration and extrapolate it if the
- carrier frequency lies outside the calibrated range
- """
-
- self.zeroCalIndex = -1
- self.sidebandCalIndex = -1
- print 'Zero calibration: selecting calset %d' % \
- self.zeroCalFiles[-1]
- print 'Sideband calibration: selecting calset %d' % \
- self.sidebandCalFiles[-1]
-
-
- def findCalset(self, rangeStart, rangeEnd, calStarts, calEnds, calType):
-
- badness = np.max([np.resize(self.exceedCalLimits,
- np.shape(calStarts)),
- calStarts-rangeStart,
- rangeEnd-calEnds], axis=0)
- i = np.size(badness) - np.argmin(badness[::-1]) - 1
- if badness[i] > self.exceedCalLimits:
- print '\n closest calset: %g, only covers %g GHz to %g GHz' \
- % (i,calStarts[i], calEnds[i])
- return i
-
-
- def selectCalByRange(self, start,end):
- """
- Use only the latest calibration covering the given range. If
- there is no such calibration use the one that is closest to
- covering it.
- """
- print 'Zero calibration:',
- self.zeroCalIndex = self.findCalset(start, end, self.zeroTableStart,
- self.zeroTableEnd, 'zero')
- print ' selecting calset %d' % \
- self.zeroCalFiles[self.zeroCalIndex]
- print 'Sideband calibration:',
- self.sidebandCalIndex = self.findCalset(start, end,
- self.sidebandCarrierStart,
- self.sidebandCarrierEnd,
- 'sideband')
- print ' selecting calset %d' % \
- self.sidebandCalFiles[self.sidebandCalIndex]
-
- def DACzeros(self, carrierFreq):
- """
- Returns the DAC values for which, at the given carrier
- frequency, the IQmixer output power is smallest.
- Uses cubic interpolation
- """
- if self.zeroTableI == []:
- return [0.0,0.0]
- i = self.zeroCalIndex
- if i is None:
- i = self.findCalset(carrierFreq, carrierFreq, self.zeroTableStart,
- self.zeroTableEnd, 'zero')
- carrierFreqFreq=carrierFreq
- carrierFreq = (carrierFreq - self.zeroTableStart[i]) / self.zeroTableStep[i] #now it becomes and index
- #zeroI=interpol_cubic(self.zeroTableI[i], carrierFreq)
- #zeroQ=interpol_cubic(self.zeroTableQ[i], carrierFreq)
- #print 'board:',self.board,' freq:',carrierFreqFreq,' zeroI,Q:',zeroI,zeroQ
- return [interpol_cubic(self.zeroTableI[i], carrierFreq), interpol_cubic(self.zeroTableQ[i], carrierFreq)]
- #return [interpol(self.zeroTableI[i], carrierFreq), interpol(self.zeroTableQ[i], carrierFreq)] #old
-
- def _IQcompensation(self, carrierFreq, n):
- """
- Returns the sideband correction at the given carrierFreq and for
- sideband frequencies
- (0, 1, 2, ..., n/2, n/2+1-n, ..., -1, 0) * (1.0 / n) GHz
- """
- if self.sidebandCompensation == []:
- return np.zeros(n+1, dtype = complex)
- i = self.sidebandCalIndex
- if i is None:
- i = self.findCalset(carrierFreq, carrierFreq,
- self.sidebandCarrierStart,
- self.sidebandCarrierEnd, 'sideband')
- carrierFreq = (carrierFreq - self.sidebandCarrierStart[i]) / \
- self.sidebandCarrierStep[i]
- w = np.shape(self.sidebandCompensation[i])[1]
- maxfreq = 0.5 * self.sidebandStep[i] * (w-1)
- p = self.sidebandStep[i]/(1-2*maxfreq)
- freqs = np.zeros(n+1,dtype=float)
- freqs[1:n/2+1] = np.arange(1,n/2+1)
- freqs[n/2+1:n] = np.arange(n/2+1-n,0)
- freqs /= n
- compensation = np.zeros(w+2,complex)
- compensation[1:w+1] = interpol(self.sidebandCompensation[i],carrierFreq)
- compensation[0] = (1 - p) * compensation[1] + p * compensation[w]
- compensation[w+1] = (1 - p) * compensation[w] + p * compensation[1]
- return interpol(compensation,
- (freqs + maxfreq + self.sidebandStep[i]) / self.sidebandStep[i],
- extrapolate=True)
-
-
- def DACify(self, carrierFreq, i, q=None, loop=False, rescale=False,
- zerocor=True, deconv=True, iqcor=True, zipSRAM=True,
- zeroEnds=False):
- """
- Computes a SRAM sequence from I and Q values in the range from
- -1 to 1. If Q is omitted, the imaginary part of I sets the Q
- value
-
- Perfroms the following corrections at the given carrier frequency
- (in GHz):
- - DAC zeros
- - deconvolution with filter chain response
- (For length-1 i and q, this correction cannot be performed)
- - IQ mixer
-
- DACify only sets the lowest 28 bits of the SRAM samples. Add
- trigger signals to the highest 4 bits via bitwise or when
- needed.
-
- If you use deconvolution and unless you have a periodic signal
- (i.e. the signal given to DACify is looped without any dead
- time), you should have at least 5ns before and 20ns after your
- pulse where the signal is 0 (or very small). Otherwise your
- signal will be deformed because the correction for the DAC
- pulse response will either be clipped or wrapped around and
- appear at the beginning of your signal!
-
- Keyword arguments:
-
- loop=True: Does the the FFT on exactly the length of i and q.
- You need this if you have a periodic signal that is
- non-zero at the borders of the signal (like a continous
- sinewave). Otherwise DACify could do the fft on a larger
- array (padded with 0) in order to have a faster fft (fft
- is fastest for numbers that factorize into small numbers)
-
- rescale=True: If the corrected signal exceeds the DAC range,
- it is rescaled to fit. Useful to drive as hard as possible
- without signal distortions (e.g. for spectroscopy).
- Otherwise the signal is clipped. After a DACify call
- DACcorrection.last_rescale_factor contains the rescale
- factor actually used. DACcorrection.min_rescale_factor
- contains the smallest rescale factor used so far.
-
- zerocor=False: Do not perform zero correction.
-
- deconv=False: Do not perform deconvolution. Sideband frequency
- dependence of the IQ compensation will be ignored.
-
- iqcor=False: Do not perform IQ mixer correction.
-
- zipSRAM=False: returns (I,Q) tuples instead of packed SRAM data,
- tuples are not clipped to fit the DAC range.
-
- Example:
- cor = DACcorrection('DR Lab FPGA 0')
- t = arange(-50.0,50.0)
- # 5 ns FWHM Gaussian at 6 GHz carrier frequency,
- # sideband mixed to 6.1 GHz
- signal=2.0**(-t**2/2.5**2) * exp(-2.0j*pi*0.1*t)
- signal = cor.DACify(6.0, signal)
- #add trigger
- signal[0] |= 0xF<<28
- fpga.loop_sram(signal)
- """
- i = np.asarray(i)
- if q == None:
- i = i.astype(complex)
- else:
- i = i + 1.0j * q
- n = np.alen(i)
- if loop:
- nfft = n
- else:
- nfft = fastfftlen(n)
- if n > 1:
- # treat offset properly even when n != nfft
- background = 0.5*(i[0]+i[-1])
- i = np.fft.fft(i-background,n=nfft)
- i[0] += background * nfft
- return self.DACifyFT(carrierFreq, i, n=n, loop=loop, rescale=rescale,
- zerocor=zerocor, deconv=deconv, iqcor=iqcor, zipSRAM=zipSRAM,
- zeroEnds=zeroEnds)
-
-
- def DACifyFT(self, carrierFreq, signal, t0=0, n=8192, loop=False,
- rescale=False, zerocor=True, deconv=True, iqcor=True,
- zipSRAM=True, zeroEnds=False):
- """
- Works like DACify but takes the Fourier transform of the
- signal as input instead of the signal itself. Because of that
- DACifyFT is faster and more acurate than DACify and you do not
- have to lowpass filter the signal before sampling it.
- n gives the number of points (or the length in ns),
- t0 the start time. Signal can either be a function which will
- be evaluated between -0.5 and 0.5 GHz, or an array of length
- nfft with complex samples at frequencies in GHz of
- np.linspace(0.5, 1.5, nfft, endpoint=False) % 1 - 0.5
- If you want DACifyFT to be fast nfft should factorize in 2 3 and 5.
- If n < nfft, the result is truncated to n samples.
- For the rest of the arguments see DACify.
- """
- if n == 0:
- return np.zeros(0)
- if callable(signal):
- if loop:
- nfft = n
- else:
- nfft = fastfftlen(n)
- elif signal is None:
- if zerocor:
- i,q = self.DACzeros(carrierFreq)
- signal = np.uint32((int(np.round(i)) & 0x3FFF) \
- << (14 * self.flipChannels) | \
- (int(np.round(q)) & 0x3FFF) \
- << (14 * (not self.flipChannels)))
- else:
- signal = np.uint32(0)
- return np.resize(signal, n)
- else:
- signal = np.asarray(signal)
- nfft = np.alen(signal)
- if n > nfft:
- n = nfft
- nrfft = nfft/2+1
- f = np.linspace(0.5, 1.5, nfft, endpoint=False) % 1 - 0.5
- if callable(signal):
- signal = np.asarray(signal(f)).astype(complex)
- if t0 != 0:
- signal *= np.exp(2.0j*np.pi*t0*f)
-
- if n > 1:
- #apply convolution and iq correction
- #FT the input
- #add the first point at the end so that the elements of signal and
- #signal[::-1] are the Fourier components at opposite frequencies
- signal = np.hstack((signal, signal[0]))
-
- #correct for the non-orthoganality of the IQ channels
- if iqcor:
- signal += signal[::-1].conjugate() * \
- self._IQcompensation(carrierFreq, nfft)
-
-
- #separate I (FT of a real signal) and Q (FT of an imaginary signal)
- i = 0.5 * (signal[0:nrfft] + \
- signal[nfft:nfft-nrfft:-1].conjugate())
- q = -0.5j * (signal[0:nrfft] - \
- signal[nfft:nfft-nrfft:-1].conjugate())
-
- #resample the FT of the response function at intervals 1 ns / nfft
- if deconv and (self.correctionI != None):
- l = np.alen(self.correctionI)
- freqs = np.arange(0,nrfft) * 2.0 * (l - 1.0) / nfft
- #correctionI = interpol(self.correctionI, freqs,extrapolate=True)
- #correctionQ = interpol(self.correctionQ, freqs,extrapolate=True)
- correctionI = interpol_cubic(self.correctionI, freqs, fill_value=0.0)
- correctionQ = interpol_cubic(self.correctionQ, freqs, fill_value=0.0)
- lp = self.lowpass(nfft, self.bandwidth)
- i *= correctionI * lp
- q *= correctionQ * lp
- #do the actual deconvolution and transform back to time space
- i = np.fft.irfft(i, n=nfft)[:n]
- q = np.fft.irfft(q, n=nfft)[:n]
- else:
- #only apply iq correction for sideband frequency 0
- if iqcor:
- signal += signal.conjugate() * \
- self._IQcompensation(carrierFreq,1)[0]
- i = signal.real
- q = signal.imag
-
- # rescale or clip data to fit the DAC range
- fullscale = 0x1FFF / self.dynamicReserve
-
- if zerocor:
- zeroI, zeroQ = self.DACzeros(carrierFreq)
- else:
- zeroI = zeroQ = 0.0
-
- if rescale:
- rescale = np.min([1.0,
- ( 0x1FFF - zeroI) / fullscale / np.max(i),
- (-0x2000 - zeroI) / fullscale / np.min(i),
- ( 0x1FFF - zeroQ) / fullscale / np.max(q),
- (-0x2000 - zeroQ) / fullscale / np.min(q)])
- if rescale < 1.0:
- print 'Corrected signal scaled by %g to fit DAC range.' % \
- rescale
- # keep track of rescaling in the object data
- self.last_rescale_factor = rescale
- if not isinstance(self.min_rescale_factor, float) \
- or rescale < self.min_rescale_factor:
- self.min_rescale_factor = rescale
- fullscale *= rescale
-
- # Due to deconvolution, the signal to put in the dacs can be nonzero at
- # the end of a sequence even with a short pulse. This nonzero value
- # exists even when running the board with an empty envelope. To remove
- # it, the first and last 4 (FOUR) values must be set to zero.
- if zeroEnds:
- i[:4] = 0.0
- i[-4:] = 0.0
- q[:4] = 0.0
- q[-4:] = 0.0
- i = np.round(i * fullscale + zeroI).astype(np.int32)
- q = np.round(q * fullscale + zeroQ).astype(np.int32)
-
-
-
- if not rescale:
- clippedI = np.clip(i,-0x2000,0x1FFF)
- clippedQ = np.clip(q,-0x2000,0x1FFF)
- if np.any((clippedI != i) | (clippedQ != q)):
- print 'Corrected IQ signal beyond DAC range, clipping'
- i = clippedI
- q = clippedQ
-
- if not zipSRAM:
- return (i, q)
-
- return ((i & 0x3FFF) << (14 * self.flipChannels) | \
- (q & 0x3FFF) << (14 * (not self.flipChannels))).\
- astype(np.uint32)
-
-
- def recalibrate(self, carrierMin, carrierMax=None, zeroCarrierStep=0.02,sidebandCarrierStep=0.05, sidebandMax=0.35, sidebandStep=0.05):
- if carrierMax is None:
- carrierMax = carrierMin
- if self.recalibrationRoutine is None:
- print 'No calibration routine hooked in.'
- return self
- return self.recalibrationRoutine(self.board, carrierMin, carrierMax,
- zeroCarrierStep, sidebandCarrierStep,
- sidebandMax, sidebandStep, self)
-
-
-
-#############################################
-# #
-# Correction class for a single DAC channel #
-# #
-#############################################
-
-
-
-class DACcorrection:
-
-
- def __init__(self, board, channel, lowpass=gaussfilter, bandwidth=0.15):
-
- """
- Returns a DACcorrection object for the given DAC board.
- keywords:
-
- lowpass: Sets the low pass filter function,
- i.e. the transfer function we want after
- correction. It expects func of form func(n,
- bandwidth). n is the number of samples between 0
- frequency and the Nyquist frequency (half the sample
- freq). bandwidth is all the other parameters the
- function needs, they are passed to func just as
- specified by the bandwidth keyword to
- DACcorrection. The return value is the transmission
- (between 0 and 1) at f=0, 1/N, ... (n-1)/N where N is
- the Nyquist frequency. Default: gaussfilter
-
- bandwidth: bandwidth are arguments passed to the lowpass
- filter function (see above)
-
- RB: This filter setting is controlled in the __init__.py, not here
-
- """
-
- self.board = board
- self.channel = channel
- #Set dynamic reserve
- self.dynamicReserve = 2.0
-
- #Use this to see how much the last DACify call rescaled the output
- self.last_rescale_factor = 1.0
- #Use this to see the smallest rescale factor DACify had to use
- self.min_rescale_factor = 1.0
-
- # Set the Lowpass, i.e. the transfer function we want after correction
- if lowpass == False:
- lowpass = flatfilter
- self.lowpass = lowpass
- self.bandwidth = bandwidth
- print lowpass.__name__ , bandwidth
- self.correction = []
-
- self.zero = 0.0
-
- self.clicsPerVolt = None
-
- self.decayRates = np.array([])
- self.decayAmplitudes = np.array([])
- self.reflectionRates = np.array([])
- self.reflectionAmplitudes = np.array([])
- self.precalc = np.array([])
-
-
-
- def loadCal(self, dataPoints, zero=0.0 , clicsPerVolt=None,
- lowpass=flatfilter, bandwidth=0.15, replace=False,maxfreqZ=0.45):
- """
- Adds a response function to the list of internal
- calibrations. dataPoints contains a step response. It is a n x
- 2 array. dataPoints[:,0] contains the time in dacIntervals,
- dataPoints[:,1] the amplitude (scale does not matter).
-
- If you add a SECONDARY CALIBRATION (i.e. a calibration that
- has been obtained with a input signal already numerically
- corrected) then you have to PROVIDE THE OPTIONAL 'lowpass'
- and 'bandwidth' ARGUMENTS to tell the deconvolution
- about the numerical lowpass filter you used to generate the
- input signal for the calibration. If you omit these
- parameters, DACify will also correct for the numerical lowpass
- filter: If you use the same numerical lowpass filter to
- generate the calibration and when you call DACify, they will
- cancel and you get no lowpass filter; If you use a narrower
- filter to generate the calibration than when you call DACify,
- you will end up with a band pass, certainly not what you want.
-
- The optional 'zero' argument gives the DAC value, giving 0 output,
- defaults to 0.
-
- maxfreqZ=0.45 is optimal (10% below Nyquist frequency)
- """
-
- #read pulse calibration from data server
- samplingfreq = int(np.round(1.0/(dataPoints[1,0]-dataPoints[0,0])))
- samplingtime = dataPoints[:,0]
- stepResponse = dataPoints[:,1]
-
- #standard way of generating the impulse response function:
- #apply moving average filter
- #stepResponse = moving_average(stepResponse,np.round(samplingfreq/2.5))
- #The moving average filter by Max is a bit too much, it leads to visible ringing for short (~20 ns) Z pulse
- #get impulse response from step response
- #Normally: h(t) = d/dt u(t), and:
- #impulseResponse = derivative(samplingtime,stepResponse)
-
- #however, we have spurious 1 GHz, 2 GHz etc signals. We can suppress that by subtracting one point from the other which is 1 ns away.
- #This also averages over a ns.
- #If we don't do this, we end up with the amplitude after a Z pulse being dissimilar to the idle amplitude, because of aliasing.
- #One issue is that this amplifies noise at 500 MHz, therefore we HAVE to cut it off later
- distance=samplingfreq
- impulseResponse = stepResponse[distance:]-stepResponse[:-distance]
- samplingtime = samplingtime[0:np.alen(impulseResponse)-1] #+ distance/2.0/samplingfreq
-
- #get time shift from the impulse response
- idx = impulseResponse.argmax()
- tshift = samplingtime[idx]
-
- self.dataPoints = impulseResponse #THIS CONTAINS THE TIME DOMAIN SIGNAL
-
- #compute correction to apply in frequency domain
- finalLength = 102400 #length for fft, long because we want good frequency resolution
- n = finalLength*samplingfreq #this is done, so we can later take 0:finalLength/2+1, i.e. 0 to 500 MHz. The progam expects this frequency range, so DON'T change it.
-
- #go to frequency space, and calculate the frequency domain correction function ~1/H
- impulseResponse_FD = np.fft.rfft(impulseResponse,n=n) #THIS CONTAINS THE FREQ DOMAIN SIGNAL
- freqs=samplingfreq/2.0*np.arange(np.alen(impulseResponse_FD))/np.alen(impulseResponse_FD)
-
- #Normally the deconv corrects for the measured impulse response not appearing at t=0.
- #This is bad, because A) the phase will depend strongly on frequency, which is harder to interpolate and
- #B) the time domain signal will run out of its memory block.
- #Here we apply a timeshift tshift, so the deconv won't shift the pulse in time.
- impulseResponse_FD *= np.exp(2.0j*np.pi*freqs*tshift)
-
- #the correction window ~1/H(f)
- correction = lowpass(finalLength,bandwidth) * abs(impulseResponse_FD[0]) / impulseResponse_FD[0:finalLength/2+1] #0:finalLength/2+1 = 0 to 500 MHz. The progam expects this frequency range in other functions, so DON'T change it.
-
- #apply a cut off frequency, necessary to kick out 500 MHz signal which messes up dualblock scans with nonzero Z.
- #Also, the 1 GHz suppression applied above amplifies noise at 500 MHz.
- if maxfreqZ:
- freqs=0.5*np.arange(np.alen(correction))/np.alen(correction)
- correction = correction * 1.0 * (abs(freqs)<=maxfreqZ)
-
- self.correction += [correction]
- self.zero = zero
- self.clicsPerVolt = clicsPerVolt
- self.precalc = np.array([])
-
-
- def setSettling(self, rates, amplitudes):
- """
- If a calibration can be characterized by time constants, i.e.
- the step response function is
- 0 for t < 0
- 1 + sum(amplitudes[i]*exp(-decayrates[i]*t)) for t >= 0,
- then you don't need to load the response function explicitly
- but can just give the timeconstants and amplitudes.
- All previously used time constants will be replaced.
- """
- rates = np.asarray(rates)
- amplitudes = np.asarray(amplitudes)
- if np.shape(rates) != np.shape(amplitudes):
- raise Error('arguments to setSettling must have same shape.')
- s = np.size(rates)
- rates = np.reshape(np.asarray(rates),s)
- amplitudes = np.reshape(np.asarray(amplitudes),s)
- if (not np.array_equal(self.decayRates,rates)) or (not np.array_equal(self.decayAmplitudes,amplitudes)):
- print 'emptying precalc (settling)'
- self.decayRates = rates
- self.decayAmplitudes = amplitudes
- self.precalc = np.array([])
-
- def setReflection(self, rates, amplitudes):
- """ Correct for reflections in the line.
- Impulse response of a line reflection is H = (1-amplitude) / (1-amplitude * exp( -2i*pi*f/rate) )
- All previously used time constants for the reflections will be replaced.
- """
- rates = np.asarray(rates)
- amplitudes = np.asarray(amplitudes)
- if np.shape(rates) != np.shape(amplitudes):
- raise Error('arguments to setReflection must have same shape.')
- s = np.size(rates)
- rates = np.reshape(np.asarray(rates),s)
- amplitudes = np.reshape(np.asarray(amplitudes),s)
- if (not np.array_equal(self.reflectionRates,rates)) or (not np.array_equal(self.reflectionAmplitudes,amplitudes)):
- print 'emptying precalc (reflection)'
- self.reflectionRates = rates
- self.reflectionAmplitudes = amplitudes
- self.precalc = np.array([])
-
-
- def setFilter(self, lowpass=None, bandwidth=0.15):
- """
- Set the lowpass filter used for deconvolution.
-
- lowpass: Sets the low pass filter function, i.e. the transfer
- function we want after correction. It expects func of form
- func(n, bandwidth). n is the number of samples between 0
- frequency and the Nyquist frequency (half the sample
- freq). bandwidth is all the other parameters the function
- needs, they are passed to func just as specified by the
- bandwidth keyword to DACcorrection. The return value is
- the transmission (between 0 and 1) at f=0, 1/N,
- ... (n-1)/N where N is the Nyquist frequency. Default:
- gaussfilter
-
- bandwidth: bandwidth are arguments passed to the lowpass
- filter function (see above)
- """
- if lowpass is None:
- lowpass=self.lowpass
-
- if (self.lowpass != lowpass) or (self.bandwidth != bandwidth):
- self.lowpass = lowpass
- self.bandwidth = bandwidth
- self.precalc = np.array([])
-
-
- def DACify(self, signal, loop=False, rescale=False, fitRange=True,
- zerocor=True, deconv=True, volts=True, dither=False,
- averageEnds=False):
- """
- Computes a SRAM sequence for one DAC channel. If volts is
- True, the input is expected in volts. Otherwise inputs of -1
- and 1 correspond to the DAC range divided by
- self.dynamicReserve (default 2). In the latter case, clipping
- may occur beyond -1 and 1.
- DACify corrects for
- - zero offsets (if zerocor is True)
- - gain (if volts is True)
- - pulse shape (if deconv is True)
- DACify returns a long array in the range -0x2000 to 0x1FFF
-
- If you use deconvolution and unless you have a periodic signal
- (i.e. the signal given to DACify is looped without any dead
- time), you should have at least 5ns before and 200ns (or
- however long the longest pulse calibration trace is) after
- your pulse where the signal is constant with the same value at
- the beginning and the end of the sequence. Otherwise your
- signal will be deformed because the correction for the DAC
- pulse response will either be clipped or wrapped around and
- appear at the beginning of your signal!
-
- Keyword arguments:
-
- loop=True: Does the the FFT on exactly the length of the input
- signal. You need this if you have a periodic signal that
- is non-zero at the borders of the signal (like a continous
- sinewave). Otherwise DACify pads the input with the
- average of the first and the last datapoint to optain a
- signal length for which fft is fast (fft is fastest for
- numbers that factorize into small numbers and extremly
- slow for large prime numbers)
-
- rescale=True: If the corrected signal exceeds the DAC range,
- it is rescale to fit. Usefull to drive as hard as possible
- without signal distorsions (e.g. for spectroscopy).
- Otherwise the signal is clipped. After a DACify call
- DACcorrection.last_rescale_factor contains the rescale factor
- actually used. DACcorrection.min_rescale_factor contains the
- smallest rescale factor used so far.
-
- fitRange=False: Do not clip data to fit into 14 bits. Only
- effective without rescaling.
-
- zerocor=False: Do not perform zero correction.
-
- deconv=False: Do not perform deconvolution.
-
- volts=False: Do not correct the gain. A input signal of
- amplitude 1 will then result in an output signal with
- amplitude DACrange/dynamicReserve
- """
-
- signal = np.asarray(signal)
-
- if np.alen(signal) == 0:
- return np.zeros(0)
-
- n = np.alen(signal)
-
- if loop:
- nfft = n
- else:
- nfft = fastfftlen(n)
-
- nrfft = nfft/2+1
- background = 0.5*(signal[0] + signal[-1])
- signal_FD = np.fft.rfft(signal-background, n=nfft) #FT the input
- signal = self.DACifyFT(signal_FD, t0=0, n=n, nfft=nfft, offset=background,
- loop=loop,
- rescale=rescale, fitRange=fitRange, deconv=deconv,
- zerocor=zerocor, volts=volts, dither=dither,
- averageEnds=averageEnds)
- return signal
-
-
- def DACifyFT(self, signal, t0=0, n=8192, offset=0, nfft=None, loop=False,
- rescale=False, fitRange=True, deconv=True, zerocor=True,
- volts=True, maxvalueZ=5.0, dither=False, averageEnds=False):
- """
- Works like DACify but takes the Fourier transform of the
- signal as input instead of the signal. n gives the number of
- points (or the length in ns), t0 the start time. Signal can
- either be an array of length n/2 + 1 giving the frequency
- components from 0 to 500 MHz. or a function which will be
- evaluated between 0 and 0.5 (GHz). For the rest of the
- arguments see DACify
- """
-
- # TODO: Remove this hack that strips units
- decayRates = np.array([x['GHz'] for x in self.decayRates])
- decayAmplitudes = self.decayAmplitudes
-
- reflectionRates = np.array([x['GHz'] for x in self.reflectionRates])
- reflectionAmplitudes = self.reflectionAmplitudes
-
- #read DAC zeros
- if zerocor:
- zero = self.zero
- else:
- zero = 0
- if volts and self.clicsPerVolt:
- fullscale = 0x1FFF / self.clicsPerVolt
- else:
- fullscale = 0x1FFF / self.dynamicReserve
-
-
- #evaluate the Fourier transform 'signal'
- if callable(signal):
- if loop:
- nfft = n
- elif nfft is None:
- nfft = fastfftlen(n)
- nrfft = nfft/2+1
- signal = np.asarray(signal(np.linspace(0.0, float(nrfft)/nfft,
- nrfft, endpoint=False))).astype(complex)
- elif signal is None:
- signal = np.int32(np.round(fullscale*offset+zero))
- if fitRange:
- signal = np.clip(signal, -0x2000,0x1FFF)
- signal = np.uint32(signal & 0x3FFF)
- return np.resize(signal, n)
- else:
- signal = np.asarray(signal)
- nrfft = len(signal)
- if nfft is None or nfft/2 + 1 != nrfft:
- nfft = 2*(nrfft-1)
-
-
- if t0 != 0:
- signal *= np.exp(np.linspace(0.0,
- 2.0j * np.pi * t0 * nrfft / nfft, nrfft, endpoint=False))
- signal[0] += nfft*offset
- #do the actual deconvolution and transform back to time space
- if deconv:
- # check if the precalculated correction matches the
- # length of the data, if not we have to recalculate
- if np.alen(self.precalc) != nrfft:
- # lowpass filter
- precalc = self.lowpass(nfft, self.bandwidth).astype(complex)
-
- freqs = np.linspace(0, nrfft * 1.0 / nfft,
- nrfft, endpoint=False)
- i_two_pi_freqs = 2j*np.pi*freqs
-
- # pulse correction
- for correction in self.correction:
- l = np.alen(correction)
- precalc *= interpol_cubic(correction, freqs*2.0*(l-1)) #cubic, as fast as linear interpol
-
- # Decay times:
- # add to qubit registry the following keys:
- # settlingAmplitudes=[-0.05] #relative amplitude
- # settlingRates = [0.01 GHz] #rate is in GHz, (1/ns)
- if np.alen(decayRates):
- precalc /= (1.0 + np.sum(decayAmplitudes[:, None] * i_two_pi_freqs[None, :] / (i_two_pi_freqs[None, :] + decayRates[:, None]), axis=0))
-
- # Reflections:
- # add to qubit registry the following keys:
- # reflectionAmplitudes=[0.05] #relative amplitude
- # reflectionRates = [0.01 GHz] #rate is in GHz, (1/ns)
- #
- # Reflections are dealt with by modelling a wire with round-trip time 1/rate,
- # and reflection coefficient amplitude.
- # It's the simplest model which can describe the effect of reflections in wiring
- # in for example the wiring between the DAC output and fridge ports. Think about echo,
- # reflections give rise to an endless sum of copies of the original signal with decreasing amplitude:
- # f(t) -> (1-amplitude) Sum_k=0^\infty (amplitude^k f(t-k 1/rate) ).
- #
- # Suppose X is an ideal pulse, H the impulse response of a piece of cable (with reflection, settling etc).
- # To get X at the end of the cable you need to send Y = X/H.
- # So if you have different impulse responses H1, H2, H3: Y = X / (H1 * H2 * H3)
- if np.alen(reflectionRates):
- for rate,amplitude in zip(reflectionRates,reflectionAmplitudes):
- if abs(rate) > 0.0:
- precalc /= (1.0 - amplitude) / (1.0-amplitude*np.exp(-i_two_pi_freqs/rate))
-
-
- # The correction window can have very large amplitudes,
- # therefore the time domain signal can have large oscillations which will be truncated digitally,
- # leading to deterioration of the waveform. The large amplitudes in the correction window have low S/N ratios.
- # Here, we apply a maximum value, i.e. truncate the value, but keep the phase.
- # This way we still have a partial correction, within the limits of the boards.
- # Doing it this way also helps a lot with the waveforms being scalable.
- if maxvalueZ:
- precalc = precalc * (1.0 * (abs(precalc)<=maxvalueZ)) + np.exp(1j*np.angle(precalc))*maxvalueZ * 1.0 * (abs(precalc) > maxvalueZ)
-
- self.precalc = precalc
- signal *= self.precalc
- else:
- signal *= self.lowpass(nfft, self.bandwidth)
-
- # transform to real space
- signal = np.fft.irfft(signal, n=nfft)
- signal = signal[0:n]
-
- # Due to deconvolution, the signal to put in the dacs can be nonzero at
- # the end of a sequence with even a short pulse. This nonzero value
- # exists even when running the board with an empty envelope. To remove
- # this, the first and last 4 values must be set.
- if averageEnds:
- signal[0:4] = np.mean(signal[0:4])
- signal[-4:] = np.mean(signal[-4:])
-
- if rescale:
- rescale = np.min([1.0,
- ( 0x1FFF - zero) / fullscale / np.max(signal),
- (-0x2000 - zero) / fullscale / np.min(signal)])
- if rescale < 1.0:
- print 'Corrected signal scaled by %g to fit DAC range.' % \
- rescale
- # keep track of rescaling in the object data
- self.last_rescale_factor = rescale
- if not isinstance(self.min_rescale_factor, float) \
- or rescale < self.min_rescale_factor:
- self.min_rescale_factor = rescale
- fullscale *= rescale
-
- if dither:
- ditheringspan = 2. #a dithering span of 3 goes from -1.5.. 1.5, i.e. 0..3 = 0,1,2,3 = 4 numbers = 2 bits exactly
- else:
- ditheringspan = 0.
- dithering = ditheringspan * (np.random.rand( len(signal ) )-0.5)
- dithering[0:4] = 0.0
- dithering[-4:] = 0.0
-
- signal = np.round(1.0*signal * fullscale + zero + dithering).astype(np.int32)
-
- if not rescale:
- if (np.max(signal) > 0x1FFF) or (np.min(signal) < -0x2000):
- print 'Corrected Z signal beyond DAC range, clipping'
- print 'max: ', np.max(signal) ,' min: ', np.min(signal)
- signal = np.clip(signal,-0x2000,0x1FFF)
- if not fitRange:
- return signal #this returns the signal between -8192 .. + 8191
-
- return (signal & 0x3FFF).astype(np.uint32) #this returns the signal between 0 .. 16383. -1 = 16382. It will lead to errors visible in fpgatest
diff --git a/ghzdac/keys.py b/ghzdac/keys.py
deleted file mode 100644
index dae15e95..00000000
--- a/ghzdac/keys.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 2007, 2008 Max Hofheinz
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-IQWIRING = 'IQ mixer wiring'
-ANRITSUPOWER = 'Anritsu power level'
-PULSECARRIERFREQ = 'Carrier frequency for pulse calibration'
-ANRITSUID = 'Anritsu ID'
-SPECTID = 'Spectrum analyzer ID'
-SCOPEID = 'Sampling Scope ID'
-SCOPEOFFSET = 'Sampling Scope DC offset'
-SCOPESENSITIVITY = 'Sampling Scope sensitivity'
-SSCOPECHANNEL = 'Sampling Scope channel'
-SSCOPESENSITIVITYDC = 'Sampling Scope sensitivity DC'
-SWITCHPOSITION = 'Microwave switch position'
-SWITCHNAME = 'Microwave switch name'
-SWITCHUSE = 'Microwave switch use'
-TIMEOFFSET = 'DAC delay for pulse calibration'
-SETUPTYPES = ['no IQ mixer',
- 'DAC A -> mixer I, DAC B -> mixer Q',
- 'DAC A -> mixer Q, DAC B -> mixer I']
-SESSIONNAME = 'GHzDAC Calibration'
-ZERONAME = 'zero'
-PULSENAME = 'pulse'
-IQNAME = 'IQ'
-CHANNELNAMES = ['DAC A','DAC B']
-VERTICALSCALE = 'vertical scale'
-HORIZONTALSCALE = 'horizontal scale'
-HORIZONTALPOSITION = 'horizontal position'
-AVERAGES = 'numavg'
-
-POSITION = 'position'
-TRIGGERLEVEL = 'trigger level'
-PULSELENGTH = 'pulse length'
-DACDCPULSE = 'dac dc pulse'
-DACACPULSE = 'dac ac pulse'
-HARMONIC = 'Carrier frequency harmonic'
-SERVERSETTINGVALUES = [
- 'deconvIQ',
- 'deconvZ',
- 'bandwidthIQ',
- 'bandwidthZ',
- 'maxfreqZ',
- 'maxvalueZ',
- 'dither'
-]
diff --git a/ghzdac_cal/GHz_DAC_calibrate.py b/ghzdac_cal/GHz_DAC_calibrate.py
deleted file mode 100644
index 8ead9b61..00000000
--- a/ghzdac_cal/GHz_DAC_calibrate.py
+++ /dev/null
@@ -1,275 +0,0 @@
-"""This module runs the board calibrations.
-
-Quick start::
-
-.. code:: python
-
- import ghzdac_cal.GHz_DAC_calibrate as gc
- gc.calibrate_iq(cxn, ['Vince DAC 11'])
- # or if you want to do all the boards in one go:
- gc.calibrate_iq(cxn, gc.find_microwave_dacs(cxn))
-
-Generally, this module calls calibration code in the ``ghzdac`` package.
-Therefore, this module requires that the ``servers`` repository is in the Python
-path (the ``servers/`` directory should be put on the Python path, not its
-parent directory. When this is set up properly, ``import ghzdac`` will succeed.
-
-The most user-facing functions in this module are at the bottom; recommended
-reading order is bottom-to-top (i.e. start with ``calibrate_iq``, then
-``find_microwave_dacs``, and so on.
-
-"""
-# Copyright (C) 2007 Max Hofheinz
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-# This is the user interface to the calibration scripts for a initial
-# calibration
-
-# HOW TO USE
-#
-# 1 calibrate_pulse. This calibrates the microwave envelope shape.
-# 2 calibrate_iq. This calibrates the I and Q dc levels needed to zero the rf
-# output, and also calibrates the I and Q amplitudes needed to null the
-# unwanted sideband.
-
-from __future__ import with_statement, absolute_import
-
-from numpy import clip
-
-import labrad
-import labrad.async
-
-# GHz_DAC_calibrate is only a front-end, the actual calibration
-# routines are in ghzdac.calibrate
-
-import ghzdac
-import ghzdac.calibrate as calibrate
-import ghzdac.keys as keys
-
-FPGA_SERVER_NAME = 'ghz_fpgas'
-
-def calibrate_dc_pulse(fpga_name, channel, dc_scope):
- """ Calls ghzdac.calibrate.calibrateDCPulse, synchronously.
-
- :param str fpga_name: e.g. "Vince DAC 11"
- :param int channel: 0 for A, 1 for B
- :param str dc_scope: 'sampling_scope' or 'infiniium'
- :return int: dataset number
- """
- with labrad.connect() as cxn:
- if dc_scope == 'sampling_scope':
- return calibrate.calibrateDCPulse(cxn, fpga_name, channel)
- elif dc_scope == 'infiniium':
- return calibrate.calibrateDCPulse_infiniium(cxn, fpga_name, channel, 'EXT')
- else:
- raise Exception("invalid scope: {}".format(dc_scope))
-
-
-def zero_fixed_carrier(fpga_name, use_switch=True):
- """ Calls ghzdac.calibrate.zeroFixedCarrier, synchronously.
- :param str fpga_name: e.g. "Vince DAC 11"
- :param bool use_switch: Whether to use microwave switch
- :return list[int]: zeros for A and B
- """
- with labrad.connect() as cxn:
- return calibrate.zeroFixedCarrier(cxn, fpga_name, use_switch=use_switch)
-
-
-def calibrate_ac_pulse(fpga_name, zero_a, zero_b, use_switch=True):
- """ Calls ghzdac.calibrate.calibrateACPulse, synchronously.
-
- :param str fpga_name:
- :param int zero_a: Zero for channel A, in DAC units (i.e. 0 < x < 0x1FFF)
- :param int zero_b: channel B
- :param bool use_switch: Whether to use microwave switch
- :return int: dataset number
- """
- with labrad.connect() as cxn:
- return calibrate.calibrateACPulse(cxn, fpga_name, zero_a, zero_b,
- use_switch=use_switch)
-
-
-def calibrate_pulse(cxn, fpga_name, dc_scope = 'infiniium'):
- """
- :param labrad connection object cxn:
- :param str fpga_name: corresponds with registry, e.g. "Vince DAC 11"
- :param str dc_scope: 'sampling_scope' or 'infiniium'
- :return:
- """
-
- if not isinstance(fpga_name, str):
- node_name = cxn[FPGA_SERVER_NAME].list_dacs()[0].partition(' DAC')[0] # pull out node name by looking at dacs
- fpga_name = node_name + ' DAC ' + str(fpga_name)
-
- reg = cxn.registry
- reg.cd(['', keys.SESSIONNAME, fpga_name], True)
- wiring = reg.get(keys.IQWIRING)
- print "Board: %s, wiring: %s" % (fpga_name, wiring)
- board_type = None
- if wiring in keys.SETUPTYPES[1:3]:
- board_type = 'ac'
- elif wiring == keys.SETUPTYPES[0]:
- board_type = 'dc'
-
- if board_type == 'ac':
- use_switch = reg.get(keys.SWITCHUSE, 'b', True, True)
- zero_a, zero_b = zero_fixed_carrier(fpga_name, use_switch=use_switch)
- dataset = calibrate_ac_pulse(fpga_name, zero_a, zero_b,
- use_switch=use_switch)
- reg.set(keys.PULSENAME, [dataset])
- elif board_type == 'dc':
- channel = int(raw_input('Select channel: 0 (DAC A) or 1 (DAC B): '))
- dataset = calibrate_dc_pulse(fpga_name, channel, dc_scope=dc_scope)
- reg.set(keys.CHANNELNAMES[channel], [dataset])
-
-
-def zero_scan_carrier(fpga_name, scan_params, use_switch=True):
- """ Calls ghzdac.calibrate.zeroScanCarrier, synchronously.
-
- :param str fpga_name: corresponds with registry, e.g. "Vince DAC 11"
- :param dict scan_params: parameters dictionary
- :return int: dataset number
- """
- with labrad.connect() as cxn:
- return calibrate.zeroScanCarrier(cxn, scan_params, fpga_name, use_switch=use_switch)
-
-
-def iq_corrector(fpga_name):
- """ Calls ghzdac.IQcorrectorAsync, synchronously.
-
- :param str fpga_name: e.g. "Vince DAC 11"
- :return ghzdac.correction.IQcorrection: IQ correction object
- """
- with labrad.connect() as cxn:
- return ghzdac.IQcorrector(fpga_name, cxn, pulsecor=False)
-
-
-def sideband_scan_carrier(fpga_name, scan_params, corrector, use_switch=True):
- """ Calls ghzdac.calibrate.sidebandScanCarrier, synchronously.
-
- :param fpga_name: e.g. "Vince DAC 11"
- :param dict scan_params: parameters dict
- :param ghzdac.correction.IQcorrection corrector: corrector object, from e.g. iq_corrector
- :return:
- """
- with labrad.connect() as cxn:
- corrector.dynamicReserve = 4.0 # TODO: I don't know why we do this.
- return calibrate.sidebandScanCarrier(cxn, scan_params, fpga_name, corrector, use_switch=use_switch)
-
-
-def modify_scan_params(carrier_start, carrier_stop, carrier_step, sideband_carrier_step, sideband_step, sideband_count):
- """
- Generates a dictionary used to determine frequencies for calibration
-
- :param labrad.Value carrier_start: e.g. 4 GHz
- :param labrad.Value carrier_stop: e.g. 7 GHz
- :param labrad.Value carrier_step: e.g. 0.05 GHz
- :param labrad.Value sideband_carrier_step: e.g. 0.05 GHz
- :param labrad.Value sideband_step: e.g. 0.05 GHz
- :param labrad.Value sideband_count: e.g. 14
- :return dict scan_params: dict with frequency parameters for calibration
- """
-
- scan_params = {}
-
- maxsidebandfreq = clip(0.5 * (sideband_count - 1.0) * sideband_step['GHz'], 1e-3, 0.5)
-
- scan_params['carrierMin'] = clip(carrier_start['GHz'], 0, 20)
- scan_params['carrierMax'] = clip(carrier_stop['GHz'], 0, 20)
- scan_params['carrierStep'] = carrier_step['GHz']
- scan_params['sidebandCarrierStep'] = sideband_carrier_step['GHz']
- scan_params['sidebandFreqStep'] = calibrate.validSBstep(sideband_step['GHz'])
- scan_params['sidebandFreqCount'] = int(maxsidebandfreq / scan_params['sidebandFreqStep'] + 0.5) * 2
-
- return scan_params
-
-
-def find_microwave_dacs(cxn):
- """
- :param cxn: labrad connection object
- :return list microwave_dacs: list of microwave dacs (with IQ mixers)
- """
-
- microwave_dacs = []
-
- fpgas = cxn[FPGA_SERVER_NAME].list_devices()
- reg = cxn.registry
-
- for fpga in fpgas:
- if 'DAC' in fpga[1]:
- reg.cd(['', keys.SESSIONNAME, fpga[1]], True)
- wiring = reg.get(keys.IQWIRING)
- if wiring in keys.SETUPTYPES[1:3]:
- microwave_dacs.append(fpga[1])
-
- return microwave_dacs
-
-# TODO: make a pause before starting a uwave switch=0 board
-
-
-def calibrate_iq(cxn,
- dacs_to_calibrate,
- zero=True,
- sideband=True,
- carrier_start= 4*labrad.units.GHz,
- carrier_stop=7*labrad.units.GHz,
- carrier_step=0.025*labrad.units.GHz,
- sideband_carrier_step=0.05*labrad.units.GHz,
- sideband_step=0.05*labrad.units.GHz,
- sideband_count=14,
- use_switch=True):
- """Runs IQ mixer calibration for one or more DACs
-
- :param cxn: labrad connection object
- :param list[string] or list[int] or string dacs_to_calibrate: DAC or list of
- DACs to calibrate
- :param bool zero: whether to run the zero calibration
- :param bool sideband: whether to run the sideband calibration
- :param labrad.Value carrier_start: e.g. 4 GHz
- :param labrad.Value carrier_stop: e.g. 7 GHz
- :param labrad.Value carrier_step: e.g. 0.05 GHz
- :param labrad.Value sideband_carrier_step: e.g. 0.05 GHz
- :param labrad.Value sideband_step: e.g. 0.05 GHz
- :param labrad.Value sideband_count: e.g. 14
- """
-
- if dacs_to_calibrate == 'all':
- dacs_to_calibrate = find_microwave_dacs(cxn)
- elif not isinstance(dacs_to_calibrate, list):
- dacs_to_calibrate = [dacs_to_calibrate]
- num_strings = len([x for x in dacs_to_calibrate if isinstance(x, str)])
- if 0 < num_strings < len(dacs_to_calibrate):
- raise ValueError("Please pass in either all strings or no strings to dacs_to_calibrate.")
- if len([x for x in dacs_to_calibrate if isinstance(x, int)]) > 0:
- node_name = cxn[FPGA_SERVER_NAME].list_dacs()[0].partition(' DAC')[0]
- for idx in range(len(dacs_to_calibrate)):
- dac_number = dacs_to_calibrate[idx]
- dacs_to_calibrate[idx] = node_name + ' DAC ' + str(dac_number)
-
- scan_params = modify_scan_params(carrier_start, carrier_stop, carrier_step,
- sideband_carrier_step, sideband_step, sideband_count)
-
- # TODO: do we want to change how we get the registry keys? (i.e. away from ghzdac.keys)
- reg = cxn.registry
- for dac in dacs_to_calibrate:
- if zero:
- iq_dataset = zero_scan_carrier(dac, scan_params, use_switch=use_switch)
- reg.cd(['', keys.SESSIONNAME, dac], True)
- reg.set(keys.ZERONAME, [iq_dataset])
- if sideband:
- corrector = iq_corrector(dac)
- sideband_dataset = sideband_scan_carrier(dac, scan_params, corrector, use_switch=use_switch)
- reg.cd(['', keys.SESSIONNAME, dac], True)
- reg.set(keys.IQNAME, [sideband_dataset])
diff --git a/ghzdac_cal/__init__.py b/ghzdac_cal/__init__.py
deleted file mode 100644
index e69de29b..00000000