Skip to content
Snippets Groups Projects
Select Git revision
  • 32e83600d001450c5d370e99e5c64e10ceee4ab4
  • master default protected
2 results

Mock_Generate-0001_t_10M_wandt.py

Blame
  • Forked from Xisco Jimenez Forteza / RDStackingProject
    Source project has a limited visibility.
    detectors.py 33.06 KiB
    # -*- coding: utf-8 -*-
    """
    Created on Fri Feb 01 0split()9:09:10 2013
    
    @author: Daniel
    """
    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function
    from __future__ import unicode_literals
    
    import pykat.external.six as six
    if six.PY2:
        import exceptions
    
    import abc
    from pykat.node_network import *
    from pykat.param import Param, AttrParam
    from pykat.SIfloat import SIfloat
    
    import pykat.external.six as six
    import pykat.exceptions as pkex
    
    import collections
    import warnings
    import copy
    
    from pykat import USE_GUI, NoGUIException
    
    if USE_GUI:
        import pykat.gui.resources
        from pykat.gui.graphics import *
    
    id___ = 0
    
    class BaseDetector(object) :
        """
        This is a base class for all detectors. Classes Detector1 and Detector2 should be used directly.
        This base class can handled detectors connected to multiple nodes.
        """
        
        __metaclass__ = abc.ABCMeta
        
        def __new__(cls, *args, **kwargs):
            # This creates an instance specific class for the component
            # this enables us to add properties to instances rather than
            # all classes
            global id___
            id___ += 1
            cnew_name = str("%s.%s_%i" % (cls.__module__, cls.__name__, id___))
            
            cnew = type(cnew_name, (cls,), {})
            
            return object.__new__(cnew)
            
        def __init__(self, name, nodes=None, max_nodes=1):
            
            self.__name = name
            self._svgItem = None
            self._kat = None
            self.noplot = False
            self.enabled = True
            self.tag = None
            self._params = []
            self._mask = {}
            self.__scale = []
            self.__removed = False
            self.noplot = False
            
            self._alternate_beam = []
            self._nodes = []
            self._requested_nodes = []
            
            if nodes != None:
                if isinstance(nodes, (list, tuple)):
                    
                    if len(nodes) > max_nodes:
                        raise pkex.BasePyKatException("Tried to set too many nodes, %s, maximum number is %i." %(str(nodes),max_nodes))
                        
                    for n in nodes:
                        if n[-1]=='*':
                            self._alternate_beam.append(True)
                            n = n[:-1]
                        else:
                            self._alternate_beam.append(False)
                            
                        self._requested_nodes.append(n)
                elif isinstance(nodes, six.string_types):
                    # if we don't have a collection
                    if nodes[-1]=='*':
                        self._alternate_beam.append(True)
                        nodes = nodes[:-1]
                    else:
                        self._alternate_beam.append(False)
                        
                    self._requested_nodes.append(nodes)
                else:
                    raise pkex.BasePyKatException("Nodes should be a list or tuple of node names or a singular node name as a string.")
        
        def __deepcopy__(self, memo):
            """
            When deep copying a kat object we need to take into account
            the instance specific properties.
            """
            
            # Here we create a copy of this object based of the base class
            # of this one, otherwise we're making a copy of a copy of a copy...
            result = self.__class__.__new__(self.__class__.__base__)
            result.__dict__ = copy.deepcopy(self.__dict__, memo)
            
            return result
                    
        def _register_param(self, param):
            self._params.append(param)
            
        def _on_kat_add(self, kat):
            self._kat = kat
            
            for rn in self._requested_nodes:
                if rn != None:
                    self._nodes.append(kat.nodes.createNode(rn))
        
        def remove(self):
            if self.__removed:
                raise pkex.BasePyKatException("{0} has already been marked as removed".format(self.name))
            else:
                self._kat.remove(self)
        
            self.__removed = True
            
        @staticmethod
        def parseFinesseText(text):    
            raise NotImplementedError("This function is not implemented")
            
        def getFinesseText(self):
            """ Base class for individual finesse optical components """    
            raise NotImplementedError("This function is not implemented")
            
        def getQGraphicsItem(self):    
            if not USE_GUI:
                raise NoGUIException
                
            return None
    
        @property
        def removed(self): return self.__removed
        
        @property 
        def scale(self): return self.__scale
        @scale.setter
        def scale(self, value):
            if value not in self.__scale:
                self.__scale = value
        
        @property
        def name(self): return self.__name        
    
        def __str__(self): return self.name
    
        def mask(self, n, m, factor):
            _id = str(n)+"_"+str(m)
            
            # if the mask is 1 then remove this so it doesn't get 
            # printed as by default the value is 1.0
            if _id in self._mask and factor == 1.0:
                del self._mask[_id]
                    
            self._mask[_id] = factor
    
        def _set_node(value, index):
            if self._kat is None:
                raise pkex.BasePyKatException("This detector has not been added to a kat object yet")
            else:
                if value[-1] == '*':
                    self._alternate_beam[index] = True
                    value = value[:-1]
                else:
                    self._alternate_beam[index] = False
                    
                if value in self._kat.nodes:
                    self._nodes[index] = self._kat.nodes[value]
                else:
                    raise pkex.BasePyKatException("There is no node called " + value + " in the kat object this detector is attached to.")
        
        def _getScaleCmds(self, rtn):
            if self.scale != None:
                if isinstance(self.scale, six.string_types):
                    rtn.append("scale {1} {0}".format(self.name, self.scale))
                elif isinstance(self.scale, (list, tuple)):
                    for s in self.scale:
                        rtn.append("scale {1} {0}".format(self.name, s))
                else:
                    raise pkex.BasePyKatException("Scale command should either be a list of scales or a single string.")
    
    class Detector0(BaseDetector):
        """
        A detector that attaches to no nodes.
        """
        pass
                            
    class Detector1(BaseDetector):
        """
        A detector that attaches to one node.
        """
        @property 
        def node(self): return self._nodes[0]
        @node.setter
        def node(self, value):
            self._set_node(value, 0)      
            
                    
    class Detector2(BaseDetector):
        """
        A detector that attaches to two node.
        """
        
        @property 
        def node1(self): return self._nodes[0]
        @node1.setter
        def node(self, value):
            self._set_node(value, 0)
            
        @property 
        def node2(self): return self._nodes[1]
        @node2.setter
        def node(self, value):
            self._set_node(value, 1)
                    
                    
    class beam(Detector1):
        
        def __init__(self, name, node_name, frequency=None, alternate_beam=False):
            BaseDetector.__init__(self, name, node_name)
            
            self.alternate_beam = alternate_beam
            self.__f = Param("f", self, frequency)        
        
        @property
        def f(self): return self.__f
        
        @f.setter
        def f(self, value):
            self.__f.value = value
            
        @staticmethod
        def parseFinesseText(text):
            
            values = text.split()
    
            node=values[-1]
            alt_beam = node[-1] == '*'
            
            if len(values) == 3:
                return beam(values[1], node, alternate_beam=alt_beam)
            elif len(values) == 4:
                return beam(values[1], node, alternate_beam=alt_beam, frequency=SIfloat(values[2]))
            else:
                raise pkex.BasePyKatException('Beam detector code "{0}" is not a valid FINESSE command'.format(text))
        
        def getFinesseText(self) :
            rtn = []
            
            if self.alternate_beam:
                alt = '*'
            else:
                alt = ''
            
            if self.f.value is None:
                rtn.append("beam {name} {node}{alt}".format(name=self.name, node=self.node.name, alt=alt))
            else:
                rtn.append("beam {name} {f} {node}{alt}".format(name=self.name, f=str(self.f.value), node=self.node.name, alt=alt))
                
            for p in self._params:
                rtn.extend(p.getFinesseText())
            
            return rtn
            
    class xd(Detector0):
        
        def __init__(self, name, component, motion):
            BaseDetector.__init__(self, name, None)
            
            self.component = component
            self.motion = motion
    
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
            
            if len(values) == 4:
                return xd(values[1], values[2], values[3])
            else:
                raise pkex.BasePyKatException('Motion detector code "{0}" is not a valid FINESSE command'.format(text))
                
        def getFinesseText(self) :
            rtn = []
            
            rtn.append("xd {name} {component} {motion}".format(name=self.name,
                                                               component=self.component,
                                                               motion=self.motion))
            
            return rtn
                  
            
    class ad(Detector1):
        
        def __init__(self, name, frequency, node_name, mode=None, alternate_beam=False):
            BaseDetector.__init__(self, name, node_name)
            self.mode = mode
            self.alternate_beam = alternate_beam
            self.__f = Param("f", self, frequency)
        
        @property
        def mode(self): return self.__mode
        @mode.setter
        def mode(self, value):
            if value != None and len(value) != 2:
                raise pkex.BasePyKatException('Mode must be a container of length 2, first element the x mode and second the y mode')
        
            self.__mode = value
            
        @property
        def f(self): return self.__f
        @f.setter
        def f(self, value): 
            self.__f.value = value
            
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
    
            node=values[-1]
            alt_beam = node[-1] == '*'
            if len(values) == 6:
                return ad(values[1], values[4], values[5], mode = [int(values[2]), int(values[3])], alternate_beam=alt_beam)
            elif len(values) == 4:
                return ad(values[1], values[2], values[3], alternate_beam=alt_beam)
            else:
                raise pkex.BasePyKatException('Amplitude detector code "{0}" is not a valid FINESSE command'.format(text))
                
        def getFinesseText(self) :
            rtn = []
            
            if self.alternate_beam:
                alt = '*'
            else:
                alt = ''
            
            if self.mode is None:
                rtn.append("ad {name} {f} {node}{alt}".format(name=self.name, f=str(self.f.value), node=self.node.name, alt=alt))
            else:
                rtn.append("ad {name} {n} {m} {f} {node}{alt}".format(name=self.name, n=str(self.mode[0]), m=str(self.mode[1]), f=str(self.f.value), node=self.node.name, alt=alt))
                
            for p in self._params:
                rtn.extend(p.getFinesseText())
            
            return rtn
    
    class gouy(Detector1):
        
        def __init__(self, name, direction, spaces):
            BaseDetector.__init__(self, name)
            self.spaces = copy.copy(spaces)
            self.direction = direction
            self.alternate_beam = False
            
        @property
        def direction(self): return self.__dir
        @direction.setter
        def direction(self, value):
            if value is None or (value != 'x' and value != 'y'):
                raise pkex.BasePyKatException('Direction must be either x or y')
        
            self.__dir = value
    
        @property
        def spaces(self): return self.__spaces
        @spaces.setter
        def spaces(self, value):
    
            if value is None or len(value) < 1:
                raise pkex.BasePyKatException('Must be a list of space names')
        
            self.__spaces = value
            
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
    
            if len(values) > 3:
                return gouy(str(values[1]), str(values[2]), values[3:])
            else:
                raise pkex.BasePyKatException('Gouy detector code "{0}" is not a valid FINESSE command'.format(text))
                
        def getFinesseText(self) :
            rtn = []
    
            rtn.append("gouy {name} {dir} {spaces}".format(name=self.name, dir=str(self.direction), spaces=" ".join(self.spaces)))
            
            for p in self._params:
                rtn.extend(p.getFinesseText())
            
            return rtn
    
    
    
    class bp(Detector1):
        acceptedParameters = ['w', 'w0', 'z', 'zr', 'g', 'r', 'q']
        
        def __init__(self, name, direction, parameter, node, alternate_beam=False):
            BaseDetector.__init__(self, name, node)
            self.parameter = parameter
            self.direction = direction
            self.alternate_beam = alternate_beam
            
        @property
        def direction(self): return self.__dir
        @direction.setter
        def direction(self, value):
            if value is None or (value != 'x' and value != 'y'):
                raise pkex.BasePyKatException('Direction must be either x or y')
        
            self.__dir = value
    
        @property
        def parameter(self): return self.__param
        @parameter.setter
        def parameter(self, value):
            
            if value is None or (value not in self.acceptedParameters) :
                raise pkex.BasePyKatException('Parameter must be one of: %s'%(", ".join(self.acceptedParameters)))
        
            self.__param = value
            
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
            
            node=values[-1]
            alt_beam = node[-1] == '*'
            
            if len(values) > 3:
                return bp(str(values[1]), str(values[2]), str(values[3]), str(values[4]), alternate_beam=alt_beam)
            else:
                raise pkex.BasePyKatException('Gouy detector code "{0}" is not a valid FINESSE command'.format(text))
                
        def getFinesseText(self) :
            rtn = []
    
            if self.alternate_beam:
                alt = "*"
            else:
                alt = ""
                
            rtn.append("bp {name} {dir} {param} {node}{alt}".format(name=self.name, dir=str(self.direction), param=self.parameter, node=self.node.name, alt=alt))
            
            for p in self._params:
                rtn.extend(p.getFinesseText())
            
            return rtn
    
    id___2 = 0
    
    class pd(Detector1):
    
        def __new__(cls, *args, **kwargs):
            # This creates an instance specific class for the component
            # this enables us to add properties to instances rather than
            # all classes
            global id___2
            id___2 += 1
            cnew_name = str("%s.%s_%i" % (cls.__module__, cls.__name__, id___2))
        
            cnew = type(cnew_name, (cls,), {})
        
            return object.__new__(cnew)
    
        def __deepcopy__(self, memo):
            """
            When deep copying a kat object we need to take into account
            the instance specific properties.
            """
            
            # Here we create a copy of this object based of the base class
            # of this one, otherwise we're making a copy of a copy of a copy...
            result = self.__class__.__new__(self.__class__.__base__)
            result.__dict__ = copy.deepcopy(self.__dict__, memo)
            
            result.__set_demod_attrs()
            
            return result
    
        def __init__(self, name=None, num_demods=1, node_name=None, senstype=None, alternate_beam=False, pdtype=None, **kwargs):
            BaseDetector.__init__(self, name, node_name)
            
            self.__num_demods = num_demods
            self.__senstype = senstype
            self.alternate_beam = alternate_beam
            self.__pdtype = pdtype
    
            # create the parameters for all 5 demodulations regardless
            # of how many the user specifies. Later we add properties to
            # those which correspond to the number of demodulations
            
            self.__f1 = Param("f1", self, None)
            self.__f2 = Param("f2", self, None)
            self.__f3 = Param("f3", self, None)
            self.__f4 = Param("f4", self, None)
            self.__f5 = Param("f5", self, None)
            
            self.__phase1 = Param("phase1", self, None)
            self.__phase2 = Param("phase2", self, None)
            self.__phase3 = Param("phase3", self, None)
            self.__phase4 = Param("phase4", self, None)
            self.__phase5 = Param("phase5", self, None)
            
            fs = [self.__f1, self.__f2, self.__f3, self.__f4, self.__f5]
            ps = [self.__phase1, self.__phase2, self.__phase3, self.__phase4, self.__phase5]
    
            for i in range(num_demods):
                f = 'f{0}'.format(i+1)
                #print("i {0} fs {1} f {2} keys {3}".format(i,len(fs),f, kwargs.keys()))
                
                if f in kwargs:
                    fs[i].value = kwargs[f]
                else:
                    raise pkex.BasePyKatException("Missing demodulation frequency {0} (f{0})".format(i+1))    
            
                p = 'phase{0}'.format(i+1)
                
                if p in kwargs:
                    if kwargs[p] is None and i<num_demods-1:
                        raise pkex.BasePyKatException("Missing demodulation phase {0} (phase{0})".format(i+1))
                        
                    ps[i].value = kwargs[p]
                elif i<num_demods-1:
                    raise pkex.BasePyKatException("Missing demodulation phase {0} (phase{0})".format(i+1))
            
       
            self.__set_demod_attrs()
                    
        @property
        def senstype(self): return self.__senstype
        @senstype.setter
        def senstype(self,value):
            if value == "": value = None
            
            if value != "S" and value != "N" and value != None: 
                raise pkex.BasePyKatException("Photodiode sensitivity type can either be 'N', 'S' or None.")
                
            self.__senstype = value
            
        @property
        def num_demods(self): return self.__num_demods
        @num_demods.setter
        def num_demods(self, value): 
            if value < 0 or value > 5:
                raise pkex.BasePyKatException("Number of demodulations must be between 0 and 5")
            
            self.__num_demods = value
            self.__set_demod_attrs()
    
        @property
        def pdtype(self): return self.__pdtype
        @pdtype.setter
        def pdtype(self, value): self.__pdtype = value
        
        def __get_fphi(self, name):
            return getattr(self, '_pd__' + name)
        
        def __set_f(self, num, value):
            value = SIfloat(value)
            
            p = getattr(self, '_pd__f' + num)
            p.value = value
        
        def __set_phi(self, num, value):
            value = SIfloat(value)
            
            if value is None and num != self.num_demods:
                # check if we are setting no phase that this is only on the last
                # demodulation phase.
                raise pkex.BasePyKatException("Only last demodulation phase can be set to None")
            elif isinstance(value, six.string_types) and not isinstance(value,float) and value.lower() != "max":
                raise pkex.BasePyKatException("Demodulation phase can only be set to a 'max' or a number (or None if the last demodulation phase)")
              
            p = getattr(self, '_pd__phase' + num)  
            p.value = value
            
        def __set_demod_attrs(self):
            """
            For the set number of demodulations the correct number of 
            Parameters are created.
            """
            
            # if there are demodulations present then we want to add
            # the various parameters so they are available for users
            # to play with.
            if self.__num_demods > 0:
                for i in range(1,6):
                    name = str(i)
                    
                    if i <= self.num_demods:
                        if not hasattr(self, "f"+name):
                            fget = lambda self, i=i:        self.__get_fphi('f'+str(i))
                            fset = lambda self, value, i=i: self.__set_f(str(i), value)
                            
                            setattr(self.__class__, "f"+name, property(fget=fget, fset=fset))
                        
                        if not hasattr(self, "phase"+name):
                            setattr(self.__class__, "phase"+name, property(fget=lambda self, i=i: self.__get_fphi('phase'+str(i)), fset=lambda self, value, i=i: self.__set_phi(str(i), value)))
                    else:
                        if hasattr(self, "f"+name):
                            delattr(self.__class__, "f"+name)
                            
                        if hasattr(self, "phase"+name):
                            delattr(self.__class__, "phase"+name)
            else:
                return
        
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
            demods = 0
            senstype = None
    
            if len(values[0]) == 4:
                senstype = values[0][2]
                demods = int(values[0][3])
            elif len(values[0]) == 3:
                demods = int(values[0][2])
            elif len(values[0]) != 2:
                raise pkex.BasePyKatException("Photodiode code format incorrect '{0}' (1)".format(text))
            
            if len(values) <= 3 and demods > 0:
                raise pkex.BasePyKatException("Photodiode code format incorrect '{0}' (2)".format(text))
            elif len(values) > 3 and demods == 0:
                raise pkex.BasePyKatException("Photodiode code format incorrect '{0}' (3)".format(text))
                
            num_f_phs = len(values) - 3
            expected_f_phs = demods * 2
            
            if not (num_f_phs == expected_f_phs or num_f_phs == expected_f_phs-1):
                raise pkex.BasePyKatException("Photodiode code format incorrect '{0}' (4)".format(text))
            
            f = values[2:len(values)-1:2]    
            phs = values[3:len(values)-1:2]
            
            dict = {}
            
            for i in range(len(f)):
                dict['f{0}'.format(i+1)] = SIfloat(f[i])
            for i in range(len(phs)):
                if phs[i] == "max":
                    dict['phase{0}'.format(i+1)] = "max"
                else:
                    dict['phase{0}'.format(i+1)] = SIfloat(phs[i])
                
            node = values[-1]
            alt_beam = node[-1] == '*'
            
            if alt_beam:
                node = node[0:-1]
            
            return pd(values[1], demods, node, senstype=senstype, alternate_beam=alt_beam, **dict)
    
            
        def getFinesseText(self) :
            rtn = []
            
            if self.enabled:
                alt_str = ""
                fphi_str = ""
                
                if self.alternate_beam:
                    alt_str = "*"
                    
                for n in range(1, 1+self.num_demods):
                    _f = self.__getattribute__("f"+str(n))
                    
                    if _f == "$fs":
                        fphi_str += " $fs"
                    else:
                        fphi_str += " {0:.16g}".format(float(_f))
                        
                    phi_val = self.__getattribute__("phase"+str(n))
                    
                    if phi_val != None:
                        if type(phi_val) == float:
                            fphi_str += " {0:.16g}".format(float(phi_val))
                        else:
                            fphi_str += " {0}".format(phi_val)
                
                senstype = self.senstype
                
                if senstype is None:
                    senstype = ""
                
                rtn.append("pd{0}{1} {2}{3} {4}{5}".format(senstype, self.num_demods, self.name, fphi_str, self.node.name, alt_str))
    
                self._getScaleCmds(rtn)
                
                if self.pdtype != None:
                    rtn.append("pdtype {0} {1}".format(self.name, self.pdtype))
                    
                for p in self._params:
                    rtn.extend(p.getFinesseText())
                
            return rtn
      
    class qnoised(pd):
        
        def __init__(self, name, num_demods, node_name, alternate_beam=False, pdtype=None, **kwargs):
            super(qnoised, self).__init__(name, num_demods, node_name, alternate_beam=alternate_beam, pdtype=pdtype, **kwargs)
        
            self.__homangle = AttrParam("homangle", self, None)
        
        @property
        def homangle(self): return self.__homangle
        @homangle.setter
        def homangle(self, value): self.__homangle.value = value
        
        @pd.pdtype.setter
        def pdtype(self, value):
            raise pkex.BasePyKatException("Setting pdtype is not possible with qnoised detectors")
        
        def parseAttributes(self, values):
            
            for key in values.keys():
                if key in ["homangle"]:
                    self.__homangle.value = values[key]
                else:
                    raise pkex.BasePyKatException("No attribute {0} for qnoised".format(key))
        
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
    
            if len(values) <= 3:
                raise pkex.BasePyKatException("qnoised code format incorrect '{0}' (2)".format(text))
                
            demods = int(values[2])
            
            if len(values) <= 4 and demods > 0:
                raise pkex.BasePyKatException("qnoised code format incorrect '{0}' (2)".format(text))
            elif len(values) > 4 and demods == 0:
                raise pkex.BasePyKatException("qnoised code format incorrect '{0}' (3)".format(text))
                
            num_f_phs = len(values) - 4
            expected_f_phs = demods * 2
            
            if not (num_f_phs == expected_f_phs or num_f_phs == (expected_f_phs-1)):
                raise pkex.BasePyKatException("qnoised code format incorrect '{0}' (4)".format(text))
            
            f = values[3:len(values)-1:2]    
            phs = values[4:len(values)-1:2]
            
            dict = {}
            
            for i in range(len(f)):
                dict['f{0}'.format(i+1)] = f[i]
            for i in range(len(phs)):
                dict['phase{0}'.format(i+1)] = phs[i]
                
            node = values[-1]
            alt_beam = node[-1] == '*'
            
            if alt_beam:
                node = node[0:-1]
            
            if values[0].endswith('S'):
                sens='S'
            elif values[0].endswith('N'):
                sens='N'
            else:
                sens=None
                
            return qnoised(values[1], demods, node, senstype=sens, alternate_beam=alt_beam, **dict)
        
        def getFinesseText(self) :
            rtn = []
            
            if self.enabled:
                alt_str = ""
                fphi_str = ""
                
                if self.alternate_beam:
                    alt_str = "*"
                
                for n in range(1, 1+self.num_demods):
                    _f = self.__getattribute__("f"+str(n))
                    
                    if _f == "$fs":
                        fphi_str += " $fs"
                    else:
                        fphi_str += " {0:.16g}".format(float(_f))
                        
                    phi_val = self.__getattribute__("phase"+str(n))
                    
                    if phi_val != None:
                        if type(phi_val) == float:
                            fphi_str += " {0:.16g}".format(float(phi_val))
                        else:
                            fphi_str += " " + str(phi_val)
                
                senstype = self.senstype
                
                if senstype is None:
                    senstype = ""
                    
                rtn.append("qnoised{5} {0} {1} {2} {3}{4}".format(self.name, self.num_demods, fphi_str, self.node.name, alt_str, senstype))
    
                self._getScaleCmds(rtn)
                    
                for p in self._params:
                    rtn.extend(p.getFinesseText())
                
            return rtn
    
    class qshot(pd):
        
        def __init__(self, name, num_demods, node_name, alternate_beam=False, **kwargs):
            super(qshot, self).__init__(name, num_demods, node_name, alternate_beam=alternate_beam, pdtype=None, **kwargs)     
    
        @pd.pdtype.setter
        def pdtype(self, value):
            raise pkex.BasePyKatException("Setting pdtype is not possible with qshot detectors")
        
        @pd.senstype.setter
        def senstype(self,value):
            raise pkex.BasePyKatException("qshot detector has no sensitvity type")
        
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
    
            if len(values) <= 3:
                raise pkex.BasePyKatException("qshot code format incorrect '{0}' (2)".format(text))
                
            demods = int(values[2])
            
            if len(values) <= 4 and demods > 0:
                raise pkex.BasePyKatException("qshot code format incorrect '{0}' (2)".format(text))
            elif len(values) > 4 and demods == 0:
                raise pkex.BasePyKatException("qshot code format incorrect '{0}' (3)".format(text))
                
            num_f_phs = len(values) - 4
            expected_f_phs = demods * 2
            
            if not (num_f_phs == expected_f_phs or num_f_phs == (expected_f_phs-1)):
                raise pkex.BasePyKatException("qshot code format incorrect '{0}' (4)".format(text))
            
            f = values[3:len(values)-1:2]    
            phs = values[4:len(values)-1:2]
            
            dict = {}
            
            for i in range(len(f)):
                dict['f{0}'.format(i+1)] = f[i]
            for i in range(len(phs)):
                dict['phase{0}'.format(i+1)] = phs[i]
                
            node = values[-1]
            alt_beam = node[-1] == '*'
            
            if alt_beam:
                node = node[0:-1]
                
            if values[0].endswith('S'):
                sens='S'
            elif values[0].endswith('N'):
                sens='N'
            else:
                sens=None
            
            return qshot(values[1], demods, node, senstype=sens, alternate_beam=alt_beam, **dict)
        
        def getFinesseText(self) :
            rtn = []
            
            if self.enabled:
                alt_str = ""
                fphi_str = ""
                
                if self.alternate_beam:
                    alt_str = "*"
                    
                for n in range(1, 1+self.num_demods):
                    _f = self.__getattribute__("f"+str(n))
                    
                    if _f == "$fs":
                        fphi_str += " $fs"
                    else:
                        fphi_str += " {0:.16g}".format(float(_f))
                        
                    phi_val = self.__getattribute__("phase"+str(n))
                    
                    if phi_val != None:
                        if type(phi_val) == float:
                            fphi_str += " {0:.16g}".format(float(phi_val))
                        else:
                            fphi_str += " " + str(phi_val)
                
                senstype = self.senstype
                
                if senstype is None:
                    senstype = ""
                    
                rtn.append("qshot{5} {0} {1} {2} {3}{4}".format(self.name, self.num_demods, fphi_str, self.node.name, alt_str,senstype))
    
                self._getScaleCmds(rtn)
                    
                for p in self._params:
                    rtn.extend(p.getFinesseText())
                
            return rtn
        
        
    class hd(Detector2):
        
        def __init__(self, name, phase, node1_name, node2_name):
            BaseDetector.__init__(self, name, (node1_name, node2_name), max_nodes=2)
        
            self.__phase = Param("phase", self, phase)
        
        @property
        def phase(self): return self.__phase
        @phase.setter
        def phase(self, value): self.__phase.value = value
        
        def parseAttributes(self, values):
            raise pkex.BasePyKatException("hd detector %s has no attributes to set" % self.name)
        
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
            
            return hd(values[1], float(values[2]), str(values[3]), str(values[4]))
        
        def getFinesseText(self):
            rtn = []
            
            if self.enabled:   
                n1 = self.node1.name
                n2 = self.node2.name
                
                if self._alternate_beam[0]: n1 += "*"
                if self._alternate_beam[1]: n2 += "*"
                
                rtn.append("hd {0} {1} {2} {3}".format(self.name, self.phase, n1, n2))
    
                self._getScaleCmds(rtn)
                    
                for p in self._params:
                    rtn.extend(p.getFinesseText())
                
            return rtn
            
    class qhd(Detector2):
        
        def __init__(self, name, phase, node1_name, node2_name, sensitivity=""):
            BaseDetector.__init__(self, name, (node1_name, node2_name), max_nodes=2)
        
            self.__phase = Param("phase", self, phase)
            self.sensitivity = sensitivity
            
        @property
        def phase(self): return self.__phase
        @phase.setter
        def phase(self, value): self.__phase.value = value
        
        @property
        def sensitivity(self): 
            return self.__sensitivity
        @sensitivity.setter
        def sensitivity(self, value):
            if value == 'S' or value == 'N':
                self.__sensitivity = value
            elif value is None or value == '':
                self.__sensitivity = ""
            else:
                raise pkex.BasePyKatException("qhd (%s) sensitivity option '%s' is not available, use either 'S' or 'N'." % (self.name, value))
            
        
        def parseAttributes(self, values):
            raise pkex.BasePyKatException("hd detector %s has no attributes to set" % self.name)
        
        @staticmethod
        def parseFinesseText(text): 
            values = text.split()
            
            sens = values[0][-1]
            
            if sens != 'S' and sens != 'N':
                sens = None
                
            return qhd(values[1], float(values[2]), str(values[3]), str(values[4]), sensitivity = sens)
        
        def getFinesseText(self):
            rtn = []
            
            if self.enabled:   
                n1 = self.node1.name
                n2 = self.node2.name
                
                if self._alternate_beam[0]: n1 += "*"
                if self._alternate_beam[1]: n2 += "*"
                
                rtn.append("qhd{4} {0} {1} {2} {3}".format(self.name, self.phase, n1, n2, self.sensitivity))
    
                self._getScaleCmds(rtn)
                    
                for p in self._params:
                    rtn.extend(p.getFinesseText())
                
            return rtn