diff --git a/bin/test_plot.py b/bin/test_plot.py
index 230d6eb3854b283baa413bd2ef04d3f5a7dac2b5..851d55079f0ac2d3f57b9b5416559de7f65bcdca 100644
--- a/bin/test_plot.py
+++ b/bin/test_plot.py
@@ -16,19 +16,16 @@ m m2 0.5 0.5 0 n4 n5
 s s3 10 1 n5 n6
 
 yaxis abs:deg
+
+pd pd_cav n3
+
+cav c1 m1 n3 m2 n4
 """
 
 kat = finesse.kat()
 
 kat.parseCommands(code)
 
-kat.add(cavity('cav1', 'm1', 'n3', 'm2', 'n4'))
-
-kat.add(photodiode('pd_ref','n2'))
-kat.add(photodiode('pd_trs','n5'))
-kat.add(photodiode('pd_cav','n4', num_demods=1, demods=[1]))
-
-
 kat.add(xaxis("lin", [0, 360], kat.m2.phi, 100))
 
 kat.m1.Rcx = -1000.0
@@ -41,7 +38,7 @@ kat.maxtem = 0
 out = kat.run(printout=0,printerr=0)
 
 pl.figure()
-pl.plot(out.x, out["pd_cav"])
+pl.plot(out.x, out.y)
 pl.xlabel(out.xlabel)
 pl.ylabel("Intensity [W]")
 pl.legend(out.ylabels)
diff --git a/pykat/components.py b/pykat/components.py
index b54b28e7875101a999d805bc431b1584b0735e72..bc203fb9eb541735eb823ceb3af7f7950e22bc1c 100644
--- a/pykat/components.py
+++ b/pykat/components.py
@@ -365,7 +365,7 @@ class space(Component):
         self.__L = Param("L", self, SIfloat(L))
         self.__n = Param("n", self, SIfloat(n))
 
-	self.__g = AttrParam("g", self, g)
+        self.__g = AttrParam("g", self, g)
         self.__gx = AttrParam("gx", self, gx)
         self.__gy = AttrParam("gy", self, gy)
         
@@ -558,11 +558,12 @@ class grating(Component):
         return self._QItem
 
 class isolator(Component):
-    def __init__(self, name, node1, node2, S = 0):
+    def __init__(self, name, node1, node2, node3="dump", S = 0):
         Component.__init__(self, name)
         
         self._requested_node_names.append(node1)
         self._requested_node_names.append(node2)
+        self._requested_node_names.append(node3)
         
         self.__S = Param("S",self,SIfloat(S))
         
@@ -582,11 +583,13 @@ class isolator(Component):
         
         if len(values) == 4:
             return isolator(values[0], values[2], values[3], values[1])
+        elif len(values) == 5:
+            return isolator(values[0], values[2], values[3], values[4], values[1])
         else:
             raise pkex.BasePyKatException("Isolator Finesse code format incorrect '{0}'".format(text))
         
     def getFinesseText(self):
-        rtn = ['isol {0} {1} {2} {3}'.format(self.name, self.S.value, self.nodes[0].name, self.nodes[1].name)]
+        rtn = ['isol {0} {1} {2} {3} {4}'.format(self.name, self.S.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name)]
         
         for p in self._params:
             rtn.extend(p.getFinesseText())
diff --git a/pykat/detectors.py b/pykat/detectors.py
index 3d785067a7f8db7ab7755d328af5256ded39516e..670348742ff8c88340a5f05a109a1720ce48ad60 100644
--- a/pykat/detectors.py
+++ b/pykat/detectors.py
@@ -28,17 +28,19 @@ class Detector(object) :
         self._mask = {}
         self.__scale = None
 
-        if node[-1]=='*':
-            self._alternate_beam = True
-            node=node[:-1]
+        if node != None:
+            if node[-1]=='*':
+                self._alternate_beam = True
+                node=node[:-1]
             
-        self.__requested_node = node
+            self.__requested_node = node
     
     def _register_param(self, param):
         self._params.append(param)
         
     def _on_kat_add(self, kat):
-        self.__node = kat.nodes.createNode(self.__requested_node)
+        if self.__requested_node != None:
+            self.__node = kat.nodes.createNode(self.__requested_node)
     
     @staticmethod
     def parseFinesseText(text):    
@@ -321,7 +323,7 @@ class pd(Detector):
             
             if senstype == 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))
 
             if self.scale != None:
@@ -330,135 +332,135 @@ class pd(Detector):
             if self.pdtype != None:
                 rtn.append("pdtype {0} {1}".format(self.name, self.pdtype))
                 
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
+            for p in self._params:
+                rtn.extend(p.getFinesseText())
             
         return rtn
-            
-class photodiode(Detector):
+  
+def qnoised(pd):
+    
+    def __init__(self, name, num_demods, node_name, alternate_beam=False, pdtype=None, **kwargs):
+        pd.__init__(self, name, num_demods, node_name, alternate_beam=False, **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")
+    
+    @pd.senstype.setter
+    def senstype(self,value): raise pkex.BasePyKatException("qnoised detector has no sensitvity type")
+    
+    
+    @staticmethod
+    def parseFinesseText(text): 
+        values = text.split()
 
-    class __F(list):
-        def __init__(self, values=None):
-            if values==None:
-                values = []
-            list.__init__(self,[SIfloat(value) for value in values])
+        if len(values) <= 3:
+            raise pkex.BasePyKatException("Photodiode code format incorrect '{0}' (2)".format(text))
             
-    class __Phi(list):
-        def __convertValue(self, value):
-            if value=="max":
-                return value                
-            else:
-                return SIfloat(value)
-                
-        def __init__(self, values=None):
-            if values==None:
-                values = []
-            list.__init__(self,[self.__convertValue(value) for value in values])
-
-        def __getitem__(self, key): # probably not needed
-            if list.__getitem__(self,key)=="max":
-                return list.__getitem__(self,key)
-            else:
-                return float(list.__getitem__(self,key))
+        demods = values[2]
         
-    @property
-    def f(self): return self.__f
-
-    @property
-    def phi(self): return self.__phi
-
-    @property
-    def pdtype(self): return self.__pdtype
-    @pdtype.setter
-    def pdtype(self, value): self.__pdtype = value
-
-    def __init__(self, name, node, senstype="", num_demods=0, demods=[], pdtype=None):
-        Detector.__init__(self, name, node)
+        if len(values) <= 4 and demods > 0:
+            raise pkex.BasePyKatException("Photodiode code format incorrect '{0}' (2)".format(text))
+        elif len(values) > 4 and demods == 0:
+            raise pkex.BasePyKatException("Photodiode 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("Photodiode code format incorrect '{0}' (4)".format(text))
+        
+        f = values[3:len(values)-1:2]    
+        phs = values[4:len(values)-1:2]
+        
+        dict = {}
         
-        if num_demods>2:
-            raise NotImplementedError("pd with more than two demodulations not implemented yet")   
+        for i in range(len(f)):
+            dict['f{0}'.format(i+1)] = f[i]
+        for i in range(len(phs)):
+            dict['phi{0}'.format(i+1)] = phs[i]
             
-        self.num_demods = num_demods
-        self.senstype = senstype
-        self.__pdtype = pdtype
+        node = values[-1]
+        alt_beam = node[-1] == '*'
+        
+        if alt_beam:
+            node = node[0:-1]
+        
+        return qnoised(values[1], demods, node, 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):
+                fphi_str += " " + str(self.__getattribute__("f"+str(n)))
+                phi_val = self.__getattribute__("phi"+str(n))
+                
+                if phi_val != None:
+                    fphi_str += " " + str(phi_val)
+            
+            senstype = self.senstype
+            
+            if senstype == None:
+                senstype = ""
+                
+            rtn.append("qnoised {0} {1} {2} {3}{4}".format(self.name, self.num_demods, fphi_str, self.node.name, alt_str))
 
+            if self.scale != None:
+                rtn.append("scale {1} {0}".format(self.name, self.scale))
+                
+            for p in self._params:
+                rtn.extend(p.getFinesseText())
+            
+        return rtn
+    
+def xd(Detector):
 
-        # every second element into f (starting at 1)
-        self.__f = self.__F(demods[::2])
+    def __init__(self, name, node_name, component, motion):
+        Detector.__init__(name, None)
         
-        # Every second element into phi (starting at 2)
-        self.__phi = self.__Phi(demods[1::2])
+        self.__motion = motion
+        self.__component = component
         
+    @property
+    def motion(self): return self.__motion
+    
+    @property
+    def component(self): return self.__component
+    
     @staticmethod
     def parseFinesseText(text): 
         values = text.split()
 
-        if values[0][0:2] != "pd":
-            raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
-        if len(values[0])==2:
-            __num_demods=0
-            __senstype=""
-        elif len(values[0])==3 or len(values[0])==4:
-            if values[0][2]=="S":
-                __senstype="S"
-            elif values[0][2]=="N":
-                __senstype="N"
-            else:
-                try:
-                    __num_demods=int(values[0][2])
-                    __senstype=""
-                except ValueError:
-                    raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
-            if len(values[0])==4:
-                try:
-                    __num_demods=int(values[0][3])
-                except ValueError:
-                    raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))                
-        else:
-            raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
-
-        if __num_demods<0 or __num_demods>5:
-            raise exceptions.FinesseParse("'{0}' number of demodulations must be >0 and <5".format(text))
-
-        values.pop(0) # remove initial value
-
-        if len(values) == 2 * __num_demods + 1 or len(values) == 2 * __num_demods + 2:
-            return photodiode(values[0], values[-1], __senstype, __num_demods, values[1:len(values)-1])
-        else:
-            raise exceptions.FinesseParse("Photodiode code format incorrect '{0}'".format(text))
-
-        #return photodiode("name", "node", demods)   
-        #raise NotImplementedError("This function is not implemented")   
-        
+        if len(values) != 4:
+            raise pkex.BasePyKatException("Motion detector command format incorrect '{0}' (2)".format(text))
+            
+        return xd(values[1], values[2], values[3])
+    
     def getFinesseText(self) :
+        rtn = []
+        
         if self.enabled:
-            rtn = []
-            __f_phi=range(len(self.f)+len(self.phi))
-            __f_phi[::2]=self.f
-            __f_phi[1::2]=self.phi
-            __f_phi_str = " ".join(map(str, __f_phi))
-            
-            if self._alternate_beam:
-                rtn.append("pd{0}{1} {2} {3} {4}".format(self.senstype, self.num_demods, self.name, __f_phi_str,  self.node.name))
-            else:
-                rtn.append("pd{0}{1} {2} {3} {4}*".format(self.senstype, self.num_demods, self.name, __f_phi_str,  self.node.name))
+            rtn.append("xd {0} {1} {2}".format(self.name, self.component, self.motion))
 
-            if self.scale != None and self.scale !='':
+            if self.scale != None:
                 rtn.append("scale {1} {0}".format(self.name, self.scale))
-
-            if self.pdtype != None and self.pdtype != '':
-                rtn.append("pdtype {0} {1}".format(self.name, self.pdtype))
-
-            if self.noplot:
-                rtn.append("noplot {0}".format(self.name))
+                
+            for p in self._params:
+                rtn.extend(p.getFinesseText())
             
-            return rtn
-        else:
-            return None
+        return rtn
     
-    def getQGraphicsItem(self):
-        if self._svgItem == None:
-            self._svgItem = ComponentQGraphicsItem(":/resources/photodiode_red.svg",self,[(-5,11,self.node)])
-        
-        return self._svgItem    
-        
+    
\ No newline at end of file
diff --git a/pykat/finesse.py b/pykat/finesse.py
index f920cda09055aec82abc959d87089b05d47a08f2..bd5079859213d0eb4bf62aab03979f0804387240 100644
--- a/pykat/finesse.py
+++ b/pykat/finesse.py
@@ -33,6 +33,7 @@ import pickle
 import pykat
 import warnings
 import re
+import collections
 
 from collections import namedtuple
 
@@ -53,7 +54,6 @@ NO_GUI = False
 NO_BLOCK = "NO_BLOCK"
 pykat_web = "www.gwoptics.org/pykat"
 
-
 class katRun(object):
     def __init__(self):
         self.runDateTime = datetime.datetime.now()
@@ -258,6 +258,7 @@ class kat(object):
         self.pykatgui = None
         self.__signals = Signals()
         self.constants = {}
+        self.vacuum = []
         
         # initialise default block
         self.__currentTag= NO_BLOCK
@@ -857,6 +858,25 @@ class kat(object):
                 out.append(txt + "\n")
                             
 
+        if self.vacuum != None:
+            
+            if isinstance(self.vacuum, collections.Container):
+                objs = []
+                
+                if len(self.vacuum) > 0:
+                    for a in self.vacuum:
+                        if hasattr(a, 'name'):
+                            objs.append(a.name)
+                        else:
+                            objs.append(str(a))
+
+                    out.append("vacuum {0}\n".format(" ".join(objs)))
+                                        
+            elif isinstance(self.vacuum, str):
+                out.append("vacuum {0}\n".format(self.vacuum))
+            else:
+                pkex.BasePyKatException("Couldn't understand vacuum input list")
+
         if self.scale != None and self.scale !='': out.append("scale {0}\n".format(self.scale))
         if self.phase != None: out.append("phase {0}\n".format(self.phase))
         if self.maxtem != None: