diff --git a/.gitignore b/.gitignore
index 49f02d865c63ee31a39470459192592247ba3a01..b2e235683b41025454d2a89f184267fd22e86d92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,3 +123,4 @@ dmypy.json
 .pyre/
 
 .DS_Store
+/pulse_lib/examples/q1
diff --git a/pulse_lib/base_pulse.py b/pulse_lib/base_pulse.py
index 2f80ca23d730511f3ce15fac813054357502040e..450e887dcd9dfe93e9dd6f271886f436a058ed21 100644
--- a/pulse_lib/base_pulse.py
+++ b/pulse_lib/base_pulse.py
@@ -286,6 +286,18 @@ class pulselib:
                                    self.IQ_channels, self.qubit_channels,
                                    self.digitizers, self.digitizer_channels)
 
+    def _create_QbloxPulsar_uploader(self):
+        try:
+            from pulse_lib.qblox.pulsar_uploader import PulsarUploader
+        except ImportError:
+            logging.error('Import of QbloxPulsar uploader failed', exc_info=True)
+            raise
+
+        self.uploader = PulsarUploader(self.awg_devices, self.awg_channels,
+                                       self.marker_channels,
+                                       self.IQ_channels, self.qubit_channels,
+                                       self.digitizers, self.digitizer_channels)
+
     def finish_init(self):
         if self._backend in ["Keysight", "M3202A"]:
             self._create_M3202A_uploader()
@@ -296,6 +308,9 @@ class pulselib:
         elif self._backend == "Keysight_QS":
             self._create_KeysightQS_uploader()
 
+        elif self._backend == "Qblox":
+            self._create_QbloxPulsar_uploader()
+
         elif self._backend in ["Demo", "None", None]:
             logging.info('No backend defined')
             TODO('define demo backend')
diff --git a/pulse_lib/configuration/physical_channels.py b/pulse_lib/configuration/physical_channels.py
index 16942ed8b1ead024414676579fe14e7996b51789..93ba60c1f3fc8dd421e7226a8adcc63b8301c9bc 100644
--- a/pulse_lib/configuration/physical_channels.py
+++ b/pulse_lib/configuration/physical_channels.py
@@ -21,12 +21,17 @@ class marker_channel:
     '''
     Keysight: 0 = trigger out channel, 1...4 = analogue channel
     Tektronix: tuple = (channel,marker number), int = analogue channel
+    Qblox: 0...3 = marker out.
     '''
     setup_ns: float
     hold_ns: float
     amplitude: float = 1000
     invert: bool = False
     delay: float = 0 # ns
+    sequencer_name : str = None
+    '''
+    Qblox only: name of qubit, awg or digitizer channel to use for sequencing
+    '''
 
 # NOTES on digitizer configuration options for M3102A FPGA
 #  * Input: I/Q demodulated (external demodulation) with pairing in FPGA
diff --git a/pulse_lib/examples/configuration/init_keysight.py b/pulse_lib/examples/configuration/init_keysight.py
new file mode 100644
index 0000000000000000000000000000000000000000..4cf0b0887669c7b8ef486758671245eb9eca0b38
--- /dev/null
+++ b/pulse_lib/examples/configuration/init_keysight.py
@@ -0,0 +1,22 @@
+import qcodes as qc
+
+from pulse_lib.tests.mock_m3202a import MockM3202A_fpga
+
+if not qc.Station.default:
+    station = qc.Station()
+else:
+    station = qc.Station.default
+
+_use_dummy=True
+
+def add_awg(name, slot_nr):
+    try:
+        awg = station[name]
+    except:
+        awg = MockM3202A_fpga(name, 0, slot_nr)
+        station.add_component(awg)
+    return awg
+
+awg1 = add_awg('AWG1', 2)
+awg2 = add_awg('AWG2', 3)
+
diff --git a/pulse_lib/examples/configuration/init_keysight_qs.py b/pulse_lib/examples/configuration/init_keysight_qs.py
new file mode 100644
index 0000000000000000000000000000000000000000..f3a396670643590726188888908c627ccbe8f8c4
--- /dev/null
+++ b/pulse_lib/examples/configuration/init_keysight_qs.py
@@ -0,0 +1,30 @@
+import qcodes as qc
+
+from pulse_lib.tests.mock_m3202a_qs import MockM3202A_QS
+from pulse_lib.tests.mock_m3102a_qs import MockM3102A_QS
+
+if not qc.Station.default:
+    station = qc.Station()
+else:
+    station = qc.Station.default
+
+_use_dummy=True
+
+def add_awg(name, slot_nr):
+    try:
+        awg = station[name]
+    except:
+        awg = MockM3202A_QS(name, 0, slot_nr)
+        station.add_component(awg)
+    return awg
+
+def add_dig(name, slot_nr):
+    try:
+        dig = station[name]
+    except:
+        dig = MockM3102A_QS(name, 0, slot_nr)
+        station.add_component(dig)
+    return dig
+
+awg1 = add_awg('AWG1', 2)
+dig1 = add_dig('Dig1', 5)
diff --git a/pulse_lib/examples/configuration/init_pulsars.py b/pulse_lib/examples/configuration/init_pulsars.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ceece659bdb91c523c362c2d9b26341f5d30c09
--- /dev/null
+++ b/pulse_lib/examples/configuration/init_pulsars.py
@@ -0,0 +1,54 @@
+import qcodes as qc
+
+from pulsar_qcm.pulsar_qcm import pulsar_qcm, pulsar_qcm_dummy
+from pulsar_qrm.pulsar_qrm import pulsar_qrm, pulsar_qrm_dummy
+
+try:
+    from q1simulator import Q1Simulator
+    _q1simulator_found = True
+except:
+    print('package q1simulator not found')
+    _q1simulator_found = False
+
+if not qc.Station.default:
+    station = qc.Station()
+else:
+    station = qc.Station.default
+
+_use_dummy = True
+_use_simulator = True
+
+def add_pulsar_module(module_type, name, ip_addr):
+    try:
+        pulsar = station[name]
+    except:
+        if _use_simulator:
+            if not _q1simulator_found:
+                raise Exception('q1simulator not found')
+            pulsar = Q1Simulator(name)
+        elif module_type == 'qrm':
+            if _use_dummy:
+                print(f'Starting QRM {name} dummy')
+                pulsar = pulsar_qrm_dummy(name)
+            else:
+                print(f'Connecting QRM {name} on {ip_addr}...')
+                pulsar = pulsar_qrm(name, ip_addr)
+        else:
+            if _use_dummy:
+                print(f'Starting QCM {name} dummy')
+                pulsar = pulsar_qcm_dummy(name)
+            else:
+                print(f'Connecting QCM {name} on {ip_addr}...')
+                pulsar = pulsar_qcm(name, ip_addr)
+
+        pulsar.reset()
+        station.add_component(pulsar)
+
+    return pulsar
+
+qcm0 = add_pulsar_module('qcm', 'qcm0', '192.168.0.2')
+qrm1 = add_pulsar_module('qrm', 'qrm1', '192.168.0.3')
+
+qcm0.reference_source('internal')
+qrm1.reference_source('external')
+
diff --git a/pulse_lib/examples/configuration/init_pulsars_3.py b/pulse_lib/examples/configuration/init_pulsars_3.py
new file mode 100644
index 0000000000000000000000000000000000000000..adb1b31fc2c001e8e8697ad46bb13e858d57df3f
--- /dev/null
+++ b/pulse_lib/examples/configuration/init_pulsars_3.py
@@ -0,0 +1,56 @@
+import qcodes as qc
+
+from pulsar_qcm.pulsar_qcm import pulsar_qcm, pulsar_qcm_dummy
+from pulsar_qrm.pulsar_qrm import pulsar_qrm, pulsar_qrm_dummy
+
+try:
+    from q1simulator import Q1Simulator
+    _q1simulator_found = True
+except:
+    print('package q1simulator not found')
+    _q1simulator_found = False
+
+if not qc.Station.default:
+    station = qc.Station()
+else:
+    station = qc.Station.default
+
+_use_dummy = True
+_use_simulator = True
+
+def add_pulsar_module(module_type, name, ip_addr):
+    try:
+        pulsar = station[name]
+    except:
+        if _use_simulator:
+            if not _q1simulator_found:
+                raise Exception('q1simulator not found')
+            pulsar = Q1Simulator(name)
+        elif module_type == 'qrm':
+            if _use_dummy:
+                print(f'Starting QRM {name} dummy')
+                pulsar = pulsar_qrm_dummy(name)
+            else:
+                print(f'Connecting QRM {name} on {ip_addr}...')
+                pulsar = pulsar_qrm(name, ip_addr)
+        else:
+            if _use_dummy:
+                print(f'Starting QCM {name} dummy')
+                pulsar = pulsar_qcm_dummy(name)
+            else:
+                print(f'Connecting QCM {name} on {ip_addr}...')
+                pulsar = pulsar_qcm(name, ip_addr)
+
+        pulsar.reset()
+        station.add_component(pulsar)
+
+    return pulsar
+
+qcm0 = add_pulsar_module('qcm', 'qcm0', '192.168.0.2')
+qcm2 = add_pulsar_module('qcm', 'qcm2', '192.168.0.4')
+qrm1 = add_pulsar_module('qrm', 'qrm1', '192.168.0.3')
+
+qcm0.reference_source('internal')
+qcm2.reference_source('external')
+qrm1.reference_source('external')
+
diff --git a/pulse_lib/examples/configuration/medium_iq.py b/pulse_lib/examples/configuration/medium_iq.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ef2fd8eb2679a04848af4b84dd146ced5bc0038
--- /dev/null
+++ b/pulse_lib/examples/configuration/medium_iq.py
@@ -0,0 +1,104 @@
+from pulse_lib.base_pulse import pulselib
+from pulse_lib.virtual_channel_constructors import virtual_gates_constructor, IQ_channel_constructor
+import numpy as np
+
+_backend = 'Qblox'
+#_backend = 'Keysight'
+#_backend = 'Keysight_QS'
+
+_ch_offset = 0
+
+def init_hardware():
+    global _ch_offset
+
+    if _backend == 'Qblox':
+        _ch_offset = 0
+        from .init_pulsars_3 import qcm0, qcm2, qrm1
+        return [qcm0, qcm2], [qrm1]
+    if _backend == 'Keysight':
+        _ch_offset = 1
+        from .init_keysight import awg1, awg2
+        return [awg1,awg2], []
+    if _backend == 'Keysight_QS':
+        _ch_offset = 1
+        from .init_keysight_qs import awg1, dig1
+        TODO()
+        return [awg1], [dig1]
+
+
+def init_pulselib(awgs, digitizers, virtual_gates=False, bias_T_rc_time=None):
+
+    pulse = pulselib(_backend)
+
+    for awg in awgs:
+        pulse.add_awg(awg)
+
+    for dig in digitizers:
+        pulse.add_digitizer(dig)
+
+    awg1 = awgs[0].name
+    awg2 = awgs[1].name
+    # define channels
+    pulse.define_channel('P1', awg1, 0 + _ch_offset)
+    pulse.define_channel('P2', awg1, 1 + _ch_offset)
+    pulse.define_channel('I1', awg1, 2 + _ch_offset)
+    pulse.define_channel('Q1', awg1, 3 + _ch_offset)
+
+    pulse.define_channel('P3', awg2, 0 + _ch_offset)
+    pulse.define_channel('P4', awg2, 1 + _ch_offset)
+    pulse.define_channel('I2', awg2, 2 + _ch_offset)
+    pulse.define_channel('Q2', awg2, 3 + _ch_offset)
+
+    pulse.define_marker('M1', awg1, 0, setup_ns=40, hold_ns=20)
+    pulse.define_marker('M2', awg2, 2, setup_ns=40, hold_ns=20)
+
+    dig_name = digitizers[0].name if len(digitizers) > 0 else 'Dig1'
+    pulse.define_digitizer_channel('SD1', dig_name, 1)
+
+    # add limits on voltages for DC channel compensation (if no limit is specified, no compensation is performed).
+    pulse.add_channel_compensation_limit('P1', (-100, 100))
+    pulse.add_channel_compensation_limit('P2', (-50, 50))
+    pulse.add_channel_compensation_limit('P3', (-80, 80))
+
+    pulse.awg_channels['P1'].attenuation = 0.5
+
+    if bias_T_rc_time:
+        pulse.add_channel_bias_T_compensation('P1', bias_T_rc_time)
+        pulse.add_channel_bias_T_compensation('P2', bias_T_rc_time)
+
+    if virtual_gates:
+        # set a virtual gate matrix
+        virtual_gate_set_1 = virtual_gates_constructor(pulse)
+        virtual_gate_set_1.add_real_gates('P1','P2', 'P3', 'P4')
+        virtual_gate_set_1.add_virtual_gates('vP1','vP2', 'vP3', 'vP4')
+        inv_matrix = 1.2*np.eye(4) - 0.05
+        virtual_gate_set_1.add_virtual_gate_matrix(np.linalg.inv(inv_matrix))
+
+    # define IQ output pair
+    IQ_pair_1 = IQ_channel_constructor(pulse)
+    IQ_pair_1.add_IQ_chan("I1", "I")
+    IQ_pair_1.add_IQ_chan("Q1", "Q")
+    IQ_pair_1.add_marker("M1")
+    # frequency of the MW source
+    IQ_pair_1.set_LO(2.400e9)
+
+    # add 2 qubits: q2
+    IQ_pair_1.add_virtual_IQ_channel("q1", 2.435e9)
+    IQ_pair_1.add_virtual_IQ_channel("q2", 2.450e9)
+
+    # define IQ output pair
+    IQ_pair_2 = IQ_channel_constructor(pulse)
+    IQ_pair_2.add_IQ_chan("I2", "I")
+    IQ_pair_2.add_IQ_chan("Q2", "Q")
+    IQ_pair_2.add_marker("M2")
+    # frequency of the MW source
+    IQ_pair_2.set_LO(2.800e9)
+
+    # add qubits:
+    IQ_pair_2.add_virtual_IQ_channel("q3", 2.900e9)
+    IQ_pair_2.add_virtual_IQ_channel("q4", 2.700e9)
+
+    pulse.finish_init()
+
+    return pulse
+
diff --git a/pulse_lib/examples/configuration/small.py b/pulse_lib/examples/configuration/small.py
index 802165c46296fc3f5cf401a5f76a77b65cad27d9..7a10b000db75a6449663ee9385d5c97a03fa07dc 100644
--- a/pulse_lib/examples/configuration/small.py
+++ b/pulse_lib/examples/configuration/small.py
@@ -1,32 +1,47 @@
-from pulse_lib.tests.mock_m3202a import MockM3202A_fpga
 from pulse_lib.base_pulse import pulselib
 from pulse_lib.virtual_channel_constructors import virtual_gates_constructor
 import numpy as np
-import qcodes
 
+_backend = 'Qblox'
+#_backend = 'Keysight'
+#_backend = 'Keysight_QS'
 
-def init_hardware():
-    # try to close all instruments of previous run
-    try:
-        qcodes.Instrument.close_all()
-    except: pass
-
-    awg1 = MockM3202A_fpga("AWG1", 0, 2)
-    return [awg1]
-
+_ch_offset = 0
 
-def init_pulselib(awgs, virtual_gates=False, bias_T_rc_time=None):
-
-    pulse = pulselib()
+def init_hardware():
+    global _ch_offset
+
+    if _backend == 'Qblox':
+        _ch_offset = 0
+        from .init_pulsars import qcm0, qrm1
+        return [qcm0], [qrm1]
+    if _backend == 'Keysight':
+        _ch_offset = 1
+        from .init_keysight import awg1
+        return [awg1], []
+    if _backend == 'Keysight_QS':
+        _ch_offset = 1
+        from .init_keysight_qs import awg1
+        return [awg1], []
+
+pulse = None
+def init_pulselib(awgs, digitizers, virtual_gates=False, bias_T_rc_time=None):
+    global pulse
+
+    pulse = pulselib(_backend)
 
     for awg in awgs:
-        pulse.add_awgs(awg.name, awg)
+        pulse.add_awg(awg)
+
+    for dig in digitizers:
+        pulse.add_digitizer(dig)
 
+    awg1 = awgs[0].name
     # define channels
-    pulse.define_channel('P1','AWG1', 1)
-    pulse.define_channel('P2','AWG1', 2)
+    pulse.define_channel('P1', awg1, 0 + _ch_offset)
+    pulse.define_channel('P2', awg1, 1 + _ch_offset)
 
-    # add limits on voltages for DC channel compenstation (if no limit is specified, no compensation is performed).
+    # add limits on voltages for DC channel compensation (if no limit is specified, no compensation is performed).
     pulse.add_channel_compensation_limit('P1', (-200, 500))
     pulse.add_channel_compensation_limit('P2', (-200, 500))
 
diff --git a/pulse_lib/examples/configuration/small_iq.py b/pulse_lib/examples/configuration/small_iq.py
index ebe8bc52c9d053b69352c83afed20cb2b5b4bd06..25f101d089a4d0f3d3206d1c5cbb73654510ec01 100644
--- a/pulse_lib/examples/configuration/small_iq.py
+++ b/pulse_lib/examples/configuration/small_iq.py
@@ -1,36 +1,52 @@
-from pulse_lib.tests.mock_m3202a import MockM3202A_fpga
 from pulse_lib.base_pulse import pulselib
 from pulse_lib.virtual_channel_constructors import virtual_gates_constructor, IQ_channel_constructor
 import numpy as np
-import qcodes
 
+#_backend = 'Qblox'
+_backend = 'Keysight'
+#_backend = 'Keysight_QS'
+
+_ch_offset = 0
 
 def init_hardware():
-    # try to close all instruments of previous run
-    try:
-        qcodes.Instrument.close_all()
-    except: pass
+    global _ch_offset
 
-    awg1 = MockM3202A_fpga("AWG1", 0, 2)
-    return [awg1]
+    if _backend == 'Qblox':
+        _ch_offset = 0
+        from .init_pulsars import qcm0, qrm1
+        return [qcm0], [qrm1]
+    if _backend == 'Keysight':
+        _ch_offset = 1
+        from .init_keysight import awg1
+        return [awg1], []
+    if _backend == 'Keysight_QS':
+        _ch_offset = 1
+        from .init_keysight_qs import awg1, dig1
+        return [awg1], [dig1]
 
 
-def init_pulselib(awgs, virtual_gates=False, bias_T_rc_time=None):
+def init_pulselib(awgs, digitizers, virtual_gates=False, bias_T_rc_time=None,
+                  lo_frequency=None):
 
-    pulse = pulselib()
+    pulse = pulselib(_backend)
 
     for awg in awgs:
-        pulse.add_awgs(awg.name, awg)
+        pulse.add_awg(awg)
+
+    for dig in digitizers:
+        pulse.add_digitizer(dig)
 
+    awg1 = awgs[0].name
     # define channels
-    pulse.define_channel('P1','AWG1', 1)
-    pulse.define_channel('P2','AWG1', 2)
-    pulse.define_channel('I1','AWG1', 3)
-    pulse.define_channel('Q1','AWG1', 4)
+    pulse.define_channel('P1', awg1, 0 + _ch_offset)
+    pulse.define_channel('P2', awg1, 1 + _ch_offset)
+    pulse.define_channel('I1', awg1, 2 + _ch_offset)
+    pulse.define_channel('Q1', awg1, 3 + _ch_offset)
 
-    pulse.define_digitizer_channel('SD1', 'Digitizer', 1)
+    dig_name = digitizers[0].name if len(digitizers) > 0 else 'Dig1'
+    pulse.define_digitizer_channel('SD1', dig_name, 1)
 
-    # add limits on voltages for DC channel compenstation (if no limit is specified, no compensation is performed).
+    # add limits on voltages for DC channel compensation (if no limit is specified, no compensation is performed).
     pulse.add_channel_compensation_limit('P1', (-200, 500))
     pulse.add_channel_compensation_limit('P2', (-200, 500))
 
@@ -51,10 +67,11 @@ def init_pulselib(awgs, virtual_gates=False, bias_T_rc_time=None):
     IQ_pair_1.add_IQ_chan("I1", "I")
     IQ_pair_1.add_IQ_chan("Q1", "Q")
     # frequency of the MW source
-    IQ_pair_1.set_LO(2.40e9)
+    lo_freq = lo_frequency if lo_frequency is not None else 2.400e9
+    IQ_pair_1.set_LO(lo_freq)
 
     # add 1 qubit: q1
-    IQ_pair_1.add_virtual_IQ_channel("q1")
+    IQ_pair_1.add_virtual_IQ_channel("q1", 2.415e9)
 
     pulse.finish_init()
 
diff --git a/pulse_lib/examples/example_2Dscan.py b/pulse_lib/examples/example_2Dscan.py
index e22509c21940d8f78c2ec285d4134e56080c10f0..7758598c5f54d75256de182e18687d84c3d7412f 100644
--- a/pulse_lib/examples/example_2Dscan.py
+++ b/pulse_lib/examples/example_2Dscan.py
@@ -46,17 +46,17 @@ def create_2D_scan(pulse_lib, gate1, sweep1_mv, gate2, sweep2_mv, n_steps, t_mea
 
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # create channels P1, P2
-p = init_pulselib(awgs, virtual_gates=True)
+p = init_pulselib(awgs, digs, virtual_gates=True)
 
 
 my_seq = create_2D_scan(
         p,
-        'P1', 100,
-        'P2', 100,
-        100, 50.0,
+        'vP1', 100,
+        'vP2', 100,
+        200, 2.0,
         bias_T_corr=True
         )
 
diff --git a/pulse_lib/examples/example_baseband_pulses.py b/pulse_lib/examples/example_baseband_pulses.py
index 6fc2bfee2df375299aad739fb9211a5da377572e..00218787a76c84d49f257844fd67dbc559dee61c 100644
--- a/pulse_lib/examples/example_baseband_pulses.py
+++ b/pulse_lib/examples/example_baseband_pulses.py
@@ -14,10 +14,10 @@ def tukey_pulse(duration, sample_rate, amplitude, alpha):
 
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # create channels P1, P2
-p = init_pulselib(awgs)
+p = init_pulselib(awgs, digs)
 
 seg1 = p.mk_segment()
 seg2 = p.mk_segment()
@@ -28,9 +28,9 @@ seg1.P1.add_ramp_ss(200, 300, 340, 120)
 seg1.P1.reset_time()
 # overlapping pulses are summed
 seg1.P1.add_block(0, 200, 120)
-seg1.P1.add_ramp_ss(180, 190, 0, 50)
-seg1.P1.add_ramp_ss(190, 200, 50, 0)
-seg1.P1.add_sin(50, 150, 50, 100e6)
+seg1.P1.add_ramp_ss(180, 192, 0, 50)
+seg1.P1.add_ramp_ss(192, 200, 50, 0)
+seg1.P1.add_sin(52, 152, 50, 100e6)
 # wait 100 ns after last pulse of segment
 seg1.P1.wait(100)
 
@@ -39,7 +39,7 @@ seg2.P1 += -200
 #
 seg2.P2.add_block(100, 180, 200)
 seg2.P2.add_ramp_ss(180, 200, 200, 100)
-seg2.P2.add_ramp_ss(200, 250, 100, 0)
+seg2.P2.add_ramp_ss(200, 240, 100, 0)
 seg2.P1.add_ramp_ss(100, 200, 0, -200)
 # reset time base for SEGMENT: aligns all segments
 seg2.reset_time()
diff --git a/pulse_lib/examples/example_bias_T_compensation.py b/pulse_lib/examples/example_bias_T_compensation.py
index d4dddd181db13fa737de85f9379d80e44d8d8d40..c625e12c39739231b73ea288b1115f0e1df5f106 100644
--- a/pulse_lib/examples/example_bias_T_compensation.py
+++ b/pulse_lib/examples/example_bias_T_compensation.py
@@ -7,14 +7,14 @@ from utils.plot import plot_awgs
 
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # RC time = 1 ms
 bias_T_rc_time = 0.001
 compensate_bias_T = True
 
 # create channels P1, P2
-p = init_pulselib(awgs, virtual_gates=True,
+p = init_pulselib(awgs, digs, virtual_gates=True,
                   bias_T_rc_time=bias_T_rc_time if compensate_bias_T else None)
 
 
diff --git a/pulse_lib/examples/example_custom_pulses.py b/pulse_lib/examples/example_custom_pulses.py
index d10b6d2ec9145d1c0f689ca6225c31883456793b..aa7f614d8ca79a06f887991de964b57148d940b6 100644
--- a/pulse_lib/examples/example_custom_pulses.py
+++ b/pulse_lib/examples/example_custom_pulses.py
@@ -51,34 +51,34 @@ def iswap_pulse(duration, sample_rate, amplitude, frequency, mw_amp):
 
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # create channels P1, P2
-p = init_pulselib(awgs, virtual_gates=True)
+p = init_pulselib(awgs, digs, virtual_gates=True)
 
 seg  = p.mk_segment()
 
-seg.P1.wait(10)
+seg.P1.wait(20)
 seg.P1.reset_time()
 seg.P1.add_custom_pulse(0, 60, 350.0, tukey_pulse, alpha=0.5)
-seg.P1.add_sin(20, 40, 50.0, 2e8)
-seg.P1.wait(10)
+#seg.P1.add_sin(20, 40, 50.0, 2e8)
+seg.P1.wait(20)
 seg.reset_time()
 
 # looping on arguments
 alpha_loop = lp.linspace(0.3, 0.5, n_steps = 2, name = 'alpha', axis = 0)
 amplitude_loop = lp.linspace(300, 500, n_steps = 3, name = 'amplitude', unit = 'mV', axis = 1)
 
-seg.P2.wait(10)
+seg.P2.wait(20)
 seg.P2.reset_time()
 seg.P2.add_custom_pulse(0, 60, amplitude_loop, tukey_pulse, alpha=alpha_loop)
-seg.P2.add_sin(20, 40, 50.0, 2e8)
-seg.P2.wait(10)
+#seg.P2.add_sin(20, 40, 50.0, 2e8)
+seg.P2.wait(20)
 seg.reset_time()
 
 # virtual gate: compensation is visible on P1
-seg.vP2.add_custom_pulse(10, 70, 150.0, iswap_pulse, frequency=2e8, mw_amp=2.5)
-seg.vP2.wait(10)
+seg.vP2.add_custom_pulse(20, 80, 150.0, iswap_pulse, frequency=2e8, mw_amp=2.5)
+seg.vP2.wait(20)
 
 # create sequence
 seq = p.mk_sequence([seg])
diff --git a/pulse_lib/examples/example_dig_acquire.py b/pulse_lib/examples/example_dig_acquire.py
index bdab0b3b61970714f0316becd09286d7ecb20294..4db2926c450f066229845f12d8669686fd420b5a 100644
--- a/pulse_lib/examples/example_dig_acquire.py
+++ b/pulse_lib/examples/example_dig_acquire.py
@@ -1,72 +1,18 @@
-import numpy as np
-import time
 import logging
 from pprint import pprint
 import matplotlib.pyplot as pt
 
-import qcodes
 import qcodes.logger as logger
 from qcodes.logger import start_all_logging
 
-from pulse_lib.base_pulse import pulselib
-from pulse_lib.tests.mock_m3202a import MockM3202A_fpga
-from pulse_lib.tests.mock_m3202a_qs import MockM3202A_QS
-from pulse_lib.tests.mock_m3102a_qs import MockM3102A_QS
 from pulse_lib.tests.hw_schedule_mock import HardwareScheduleMock
 
-import pulse_lib.segments.utility.looping as lp
-from pulse_lib.virtual_channel_constructors import IQ_channel_constructor, virtual_gates_constructor
-
-import scipy.signal.windows as windows
+from configuration.medium_iq import init_hardware, init_pulselib
 
 start_all_logging()
 logger.get_file_handler().setLevel(logging.DEBUG)
 
 
-try:
-    qcodes.Instrument.close_all()
-except: pass
-
-
-def set_channel_props(pulse, channel_name, compensation_limits=(0,0), attenuation=1.0, delay=0, bias_T_rc_time=None):
-    pulse.add_channel_compensation_limit(channel_name, compensation_limits)
-    pulse.add_channel_attenuation(channel_name, attenuation)
-    pulse.add_channel_delay(channel_name, delay)
-    pulse.add_channel_bias_T_compensation(channel_name, bias_T_rc_time)
-
-
-def init_pulselib(awgs, digitizer):
-    p = pulselib(backend='Keysight_QS')
-    for awg in awgs:
-        p.add_awg(awg)
-    p.add_digitizer(digitizer)
-
-    awg1, awg2 = awgs
-
-    p.define_channel('P1', awg1.name, 1)
-    p.define_channel('P2', awg1.name, 2)
-    p.define_channel('P3', awg1.name, 3)
-    p.define_channel('P4', awg1.name, 4)
-
-    set_channel_props(p, 'P1', compensation_limits=(-500,500), attenuation=2.0, delay=0, bias_T_rc_time=0.001)
-    set_channel_props(p, 'P2', compensation_limits=(-250,250), attenuation=1.0, delay=0, bias_T_rc_time=0.001)
-    set_channel_props(p, 'P3', compensation_limits=(-280,280), attenuation=1.0, delay=0, bias_T_rc_time=0.001)
-    set_channel_props(p, 'P4', bias_T_rc_time=0.001)
-
-    p.define_digitizer_channel('SE1', digitizer.name, 1)
-
-    p.finish_init()
-
-    # add virtual channels.
-
-    virtual_gate_set_1 = virtual_gates_constructor(p)
-    virtual_gate_set_1.add_real_gates('P1','P2','P3','P4')
-    virtual_gate_set_1.add_virtual_gates('vP1','vP2','vP3','vP4')
-    virtual_gate_set_1.add_virtual_gate_matrix(0.9*np.eye(4) + 0.1)
-
-    return p
-
-
 def create_seq(pulse_lib):
 
     seg1 = pulse_lib.mk_segment(name='init')
@@ -88,7 +34,7 @@ def create_seq(pulse_lib):
     s.vP4.add_ramp_ss(1e4, 3e4, 0, 50)
     s.vP4.add_block(3e4, 7e4, 50)
     s.vP4.add_ramp_ss(7e4, 9e4, 50, 0)
-    s.SE1.acquire(4e4, t_measure=2e4)
+    s.SD1.acquire(4e4, t_measure=2e4)
 
     # generate the sequence and upload it.
     my_seq = pulse_lib.mk_sequence([seg1, seg2, seg3])
@@ -123,11 +69,11 @@ def play_next():
     my_seq.play([index], release=True)
 
 
-awg1 = MockM3202A_fpga("A1", 0, 2)
-awg2 = MockM3202A_QS("A2", 0, 4)
-dig = MockM3102A_QS("Dig", 0, 9)
+# create "AWG1","AWG2"
+awgs, digs = init_hardware()
 
-pulse = init_pulselib([awg1, awg2], dig)
+# create channels
+pulse = init_pulselib(awgs, digs, virtual_gates=True)
 
 my_seq = create_seq(pulse)
 
@@ -137,7 +83,7 @@ job = my_seq.upload([0])
 
 my_seq.play([0], release=False)
 
-plot(my_seq, job, (awg1,dig) )
+plot(my_seq, job, awgs)
 pprint(job.upload_info)
 
 my_seq.play([0], release=True)
diff --git a/pulse_lib/examples/example_param_sweep.py b/pulse_lib/examples/example_param_sweep.py
index 0b6f5a85c67659cb5db0f789945fa4f8b1922bf3..6af659582d3b9bfc5d9e833fff85b5bb59f6aebe 100644
--- a/pulse_lib/examples/example_param_sweep.py
+++ b/pulse_lib/examples/example_param_sweep.py
@@ -8,10 +8,10 @@ from utils.plot import plot_awgs
 
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # create channels P1, P2
-p = init_pulselib(awgs)
+p = init_pulselib(awgs, digs)
 
 v_param = lp.linspace(0, 200, 11, axis=0, unit = "mV", name = "vPulse")
 t_wait = lp.linspace(20, 100, 5, axis=1, unit = "mV", name = "t_wait")
diff --git a/pulse_lib/examples/example_rabi.py b/pulse_lib/examples/example_rabi.py
index 14a4fe6ea05068ab08712e933a8eae2aab67e68c..b99deb6d87ed732f14941b18b65d8d9bb5d413b0 100644
--- a/pulse_lib/examples/example_rabi.py
+++ b/pulse_lib/examples/example_rabi.py
@@ -8,10 +8,10 @@ from configuration.small_iq import init_hardware, init_pulselib
 from utils.plot import plot_awgs
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # create channels P1, P2
-p = init_pulselib(awgs, virtual_gates=True)
+p = init_pulselib(awgs, digs, virtual_gates=True)
 
 gates = ['vP1','vP2']
 v_init = [70, 20]
@@ -19,7 +19,7 @@ v_manip = [0,0]
 v_read = [30, 25]
 t_measure = 100 # short time for visibility of other pulses
 
-t_X90 = 50
+t_X90 = 60
 amplitude = 50
 
 t_pulse = lp.linspace(100, 1000, 10, axis=0)
@@ -29,7 +29,7 @@ amplitude = 50
 # init pulse
 init = p.mk_segment()
 init.add_block(0, 100, gates, v_init, reset_time=True)
-init.add_ramp(0, 50, gates, v_init, v_manip)
+init.add_ramp(0, 60, gates, v_init, v_manip)
 
 manip = p.mk_segment()
 manip.q1.add_MW_pulse(0, t_pulse, amplitude, f_drive)
diff --git a/pulse_lib/examples/example_ramsey.py b/pulse_lib/examples/example_ramsey.py
index f98b37f8b1e940b45e28738ba173d43405d22bd3..cad7c3fd316c712797c034360a13281b80714b14 100644
--- a/pulse_lib/examples/example_ramsey.py
+++ b/pulse_lib/examples/example_ramsey.py
@@ -1,4 +1,3 @@
-import numpy as np
 import matplotlib.pyplot as pt
 
 from pulse_lib.tests.hw_schedule_mock import HardwareScheduleMock
@@ -9,11 +8,12 @@ from configuration.small_iq import init_hardware, init_pulselib
 from utils.plot import plot_awgs
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 
 # create channels P1, P2
-p = init_pulselib(awgs, virtual_gates=True)
+p = init_pulselib(awgs, digs, virtual_gates=True)
+
 
 gates = ['vP1','vP2']
 v_init = [70, 20]
@@ -22,10 +22,12 @@ v_read = [30, 25]
 t_measure = 100 # short time for visibility of other pulses
 
 f_drive = 2.420e9
-t_X90 = 50
+t_X90 = 60
 amplitude = 50
 
-t_wait = lp.linspace(0, 200, 21, axis=0)
+p.qubit_channels['q1'].reference_frequency = 2.420e9
+
+t_wait = lp.linspace(0, 200, 11, axis=0)
 
 # init pulse
 init = p.mk_segment()
diff --git a/pulse_lib/examples/example_smooth_step.py b/pulse_lib/examples/example_smooth_step.py
index ff71dc800e6709a09249b303c3eda65f07014897..f183b1239012c1f1e24ca0a59294a6e1f8394d2d 100644
--- a/pulse_lib/examples/example_smooth_step.py
+++ b/pulse_lib/examples/example_smooth_step.py
@@ -49,10 +49,10 @@ def gaussian_step(duration, sample_rate, amplitude, stddev):
 
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # create channels P1, P2
-p = init_pulselib(awgs, virtual_gates=True)
+p = init_pulselib(awgs, digs, virtual_gates=True)
 
 seg  = p.mk_segment()
 
diff --git a/pulse_lib/examples/example_virtual_IQ_markers_plotting.py b/pulse_lib/examples/example_virtual_IQ_markers_plotting.py
index 5811edcb8dd516e519896cba8930f8abad6b100a..1f8543716a76abfd617cee8e22351b8087916065 100644
--- a/pulse_lib/examples/example_virtual_IQ_markers_plotting.py
+++ b/pulse_lib/examples/example_virtual_IQ_markers_plotting.py
@@ -1,95 +1,19 @@
-import numpy as np
-import time
 import logging
 from pprint import pprint
 import matplotlib.pyplot as pt
+import scipy.signal.windows as windows
 
-import qcodes
 import qcodes.logger as logger
 from qcodes.logger import start_all_logging
 
-from pulse_lib.base_pulse import pulselib
-from pulse_lib.tests.mock_m3202a import MockM3202A, MockM3202A_fpga
 from pulse_lib.tests.hw_schedule_mock import HardwareScheduleMock
 
-import pulse_lib.segments.utility.looping as lp
-from pulse_lib.virtual_channel_constructors import IQ_channel_constructor, virtual_gates_constructor
+from configuration.medium_iq import init_hardware, init_pulselib
 
-import scipy.signal.windows as windows
 
 start_all_logging()
 logger.get_file_handler().setLevel(logging.DEBUG)
 
-
-try:
-    qcodes.Instrument.close_all()
-except: pass
-
-
-def set_channel_props(pulse, channel_name, compensation_limits=(0,0), attenuation=1.0, delay=0):
-    pulse.add_channel_compensation_limit(channel_name, compensation_limits)
-    pulse.awg_channels[channel_name].attenuation = attenuation
-    pulse.add_channel_delay(channel_name, delay)
-
-
-
-def init_pulselib(awg1, awg2, awg3):
-    p = pulselib()
-    p.add_awg(awg1)
-    p.add_awg(awg2)
-    p.add_awg(awg3)
-    p.define_channel('P1', awg1.name, 1)
-    p.define_channel('P2', awg1.name, 2)
-    p.define_channel('P3', awg1.name, 3)
-    p.define_channel('P4', awg1.name, 4)
-
-    p.define_marker('M1', awg3.name, 3, setup_ns=40, hold_ns=20)
-    p.define_marker('M2', awg3.name, 0, setup_ns=40, hold_ns=20)
-
-    set_channel_props(p, 'P1', compensation_limits=(-100,100), attenuation=2.0, delay=0)
-    set_channel_props(p, 'P2', compensation_limits=(-50,50), attenuation=1.0, delay=0)
-    set_channel_props(p, 'P3', compensation_limits=(-80,80), attenuation=1.0, delay=0)
-
-    p.define_channel('I1', awg2.name, 1)
-    p.define_channel('Q1', awg2.name, 2)
-    p.define_channel('I2', awg2.name, 3)
-    p.define_channel('Q2', awg2.name, 4)
-
-    set_channel_props(p, 'I1', compensation_limits=(-80,80), attenuation=1.0, delay=0)
-
-    p.finish_init()
-
-    # add virtual channels.
-
-    virtual_gate_set_1 = virtual_gates_constructor(p)
-    virtual_gate_set_1.add_real_gates('P1','P2','P3','P4')
-    virtual_gate_set_1.add_virtual_gates('vP1','vP2','vP3','vP4')
-    virtual_gate_set_1.add_virtual_gate_matrix(0.9*np.eye(4) + 0.1)
-
-    #make virtual channels for IQ usage (also here, make one one of these object per MW source)
-    IQ_chan_set_1 = IQ_channel_constructor(p)
-    # set right association of the real channels with I/Q output.
-    IQ_chan_set_1.add_IQ_chan("I1", "I")
-    IQ_chan_set_1.add_IQ_chan("Q1", "Q")
-    IQ_chan_set_1.add_marker("M1")
-    # frequency of the MW source
-    IQ_chan_set_1.set_LO(2e9)
-    IQ_chan_set_1.add_virtual_IQ_channel("MW_qubit_1")
-    IQ_chan_set_1.add_virtual_IQ_channel("MW_qubit_2")
-
-    IQ_chan_set_2 = IQ_channel_constructor(p)
-    # set right association of the real channels with I/Q output.
-    IQ_chan_set_2.add_IQ_chan("I2", "I")
-    IQ_chan_set_2.add_IQ_chan("Q2", "Q")
-    IQ_chan_set_2.add_marker("M2")
-    # frequency of the MW source
-    IQ_chan_set_2.set_LO(2e9)
-    IQ_chan_set_2.add_virtual_IQ_channel("MW_qubit_3")
-    IQ_chan_set_2.add_virtual_IQ_channel("MW_qubit_4")
-
-    return p
-
-
 def tukey(duration, sample_rate):
     n_points = int(duration * sample_rate)
     return windows.tukey(n_points, alpha=0.5)
@@ -110,22 +34,22 @@ def create_seq(pulse_lib):
     s.vP4.add_ramp_ss(0, 100, 50, 100)
     s.vP4.add_ramp_ss(100, 200, 100, 50)
 
-    s.MW_qubit_2.add_MW_pulse(50, 150, 50, 200e6, AM=tukey)
+    s.q2.add_MW_pulse(40, 140, 50, 2450e6, AM=tukey)
 
-    s.MW_qubit_1.add_MW_pulse(0, 300, 20, 35e6)
+    s.q1.add_MW_pulse(0, 300, 20, 2435e6)
 
-    s.MW_qubit_3.add_MW_pulse(250, 300, 60, 50e6)
+    s.q3.add_MW_pulse(240, 300, 60, 2900e6)
 
     seg2b = pulse_lib.mk_segment('manip2')
     s = seg2b
     s.vP4.add_ramp_ss(0, 100, 50, 100)
     s.vP4.add_ramp_ss(100, 200, 100, 50)
 
-    s.MW_qubit_2.add_MW_pulse(50, 150, 50, 200e6, AM=tukey)
+    s.q2.add_MW_pulse(40, 140, 50, 2450e6, AM=tukey)
 
-    s.MW_qubit_1.add_MW_pulse(0, 300, 60, 35e6)
+    s.q1.add_MW_pulse(0, 300, 40, 2435e6)
 
-    s.MW_qubit_3.add_MW_pulse(250, 300, 20, 20e6)
+    s.q3.add_MW_pulse(240, 300, 20, 2900e6)
 
     seg3 = pulse_lib.mk_segment('measure', 1e8)
     s = seg3
@@ -174,11 +98,11 @@ def play_next():
     my_seq.play([index], release=True)
 
 
-awg1 = MockM3202A("A1", 0, 2)
-awg2 = MockM3202A("A2", 0, 3)
-awg3 = MockM3202A_fpga("A3", 0, 4)
+# create "AWG1","AWG2"
+awgs, digs = init_hardware()
 
-pulse = init_pulselib(awg1, awg2, awg3)
+# create channels
+pulse = init_pulselib(awgs, digs, virtual_gates=True)
 
 my_seq = create_seq(pulse)
 
@@ -188,7 +112,7 @@ job = my_seq.upload()
 
 my_seq.play(release=False)
 
-plot(my_seq, job, (awg1, awg2, awg3) )
+plot(my_seq, job, awgs)
 pprint(job.upload_info)
 
 my_seq.play(release=True)
diff --git a/pulse_lib/examples/example_virtual_IQ_multiple_segments.py b/pulse_lib/examples/example_virtual_IQ_multiple_segments.py
index 4744edcb2975d2bba706f3732f4072560effbe43..2f52c407f7175e4089ea82884d97d2f73bc9edb7 100644
--- a/pulse_lib/examples/example_virtual_IQ_multiple_segments.py
+++ b/pulse_lib/examples/example_virtual_IQ_multiple_segments.py
@@ -1,99 +1,19 @@
 import numpy as np
-import time
 import logging
 from pprint import pprint
 import matplotlib.pyplot as pt
 
-import qcodes
 import qcodes.logger as logger
 from qcodes.logger import start_all_logging
 
-from pulse_lib.base_pulse import pulselib
-from pulse_lib.tests.mock_m3202a import MockM3202A, MockM3202A_fpga
 from pulse_lib.tests.hw_schedule_mock import HardwareScheduleMock
 
-import pulse_lib.segments.utility.looping as lp
-from pulse_lib.virtual_channel_constructors import IQ_channel_constructor, virtual_gates_constructor
-
-import scipy.signal.windows as windows
+from configuration.medium_iq import init_hardware, init_pulselib
 
 start_all_logging()
 logger.get_file_handler().setLevel(logging.DEBUG)
 
 
-try:
-    qcodes.Instrument.close_all()
-except: pass
-
-
-def set_channel_props(pulse, channel_name, compensation_limits=(0,0), attenuation=1.0, delay=0):
-    pulse.add_channel_compenstation_limit(channel_name, compensation_limits)
-    pulse.awg_channels[channel_name].attenuation = attenuation
-    pulse.add_channel_delay(channel_name, delay)
-
-
-
-def init_pulselib(awg1, awg2, awg3):
-    p = pulselib()
-    p.add_awg(awg1)
-    p.add_awg(awg2)
-    p.add_awg(awg3)
-    p.define_channel('P1', awg1.name, 1)
-    p.define_channel('P2', awg1.name, 2)
-    p.define_channel('P3', awg1.name, 3)
-    p.define_channel('P4', awg1.name, 4)
-
-    p.define_marker('M1', awg3.name, 3, setup_ns=40, hold_ns=20)
-    p.define_marker('M2', awg3.name, 0, setup_ns=40, hold_ns=20)
-
-    set_channel_props(p, 'P1', compensation_limits=(-100,100), attenuation=2.0, delay=0)
-    set_channel_props(p, 'P2', compensation_limits=(-50,50), attenuation=1.0, delay=0)
-    set_channel_props(p, 'P3', compensation_limits=(-80,80), attenuation=1.0, delay=0)
-
-    p.define_channel('I1', awg2.name, 1)
-    p.define_channel('Q1', awg2.name, 2)
-    p.define_channel('I2', awg2.name, 3)
-    p.define_channel('Q2', awg2.name, 4)
-
-    set_channel_props(p, 'I1', compensation_limits=(-80,80), attenuation=1.0, delay=0)
-
-    p.finish_init()
-
-    # add virtual channels.
-
-    virtual_gate_set_1 = virtual_gates_constructor(p)
-    virtual_gate_set_1.add_real_gates('P1','P2','P3','P4')
-    virtual_gate_set_1.add_virtual_gates('vP1','vP2','vP3','vP4')
-    virtual_gate_set_1.add_virtual_gate_matrix(0.9*np.eye(4) + 0.1)
-
-    #make virtual channels for IQ usage (also here, make one one of these object per MW source)
-    IQ_chan_set_1 = IQ_channel_constructor(p)
-    # set right association of the real channels with I/Q output.
-    IQ_chan_set_1.add_IQ_chan("I1", "I")
-    IQ_chan_set_1.add_IQ_chan("Q1", "Q")
-    IQ_chan_set_1.add_marker("M1")
-    # frequency of the MW source
-    IQ_chan_set_1.set_LO(2e9)
-    IQ_chan_set_1.add_virtual_IQ_channel("MW_qubit_1")
-    IQ_chan_set_1.add_virtual_IQ_channel("MW_qubit_2")
-
-    IQ_chan_set_2 = IQ_channel_constructor(p)
-    # set right association of the real channels with I/Q output.
-    IQ_chan_set_2.add_IQ_chan("I2", "I")
-    IQ_chan_set_2.add_IQ_chan("Q2", "Q")
-    IQ_chan_set_2.add_marker("M2")
-    # frequency of the MW source
-    IQ_chan_set_2.set_LO(2e9)
-    IQ_chan_set_2.add_virtual_IQ_channel("MW_qubit_3")
-    IQ_chan_set_2.add_virtual_IQ_channel("MW_qubit_4")
-
-    return p
-
-
-def tukey(duration, sample_rate):
-    n_points = int(duration * sample_rate)
-    return windows.tukey(n_points, alpha=0.5)
-
 def create_seq(pulse_lib):
 
     seg1 = pulse_lib.mk_segment(name='init', sample_rate=1e8)
@@ -110,23 +30,15 @@ def create_seq(pulse_lib):
     s.vP4.add_ramp_ss(0, 100, 50, 100)
     s.vP4.add_ramp_ss(100, 200, 100, 50)
 
-#    s.MW_qubit_2.add_MW_pulse(50, 150, 50, 200e6, AM=tukey)
-
-    s.MW_qubit_1.add_MW_pulse(0, 300, 20, 35e6)
-    s.MW_qubit_1.add_phase_shift(300, np.pi/2)
-
-#    s.MW_qubit_3.add_MW_pulse(250, 300, 60, 50e6)
+    s.q1.add_MW_pulse(0, 300, 20, 2.435e9)
+    s.q1.add_phase_shift(300, np.pi/2)
 
     seg2b = pulse_lib.mk_segment('manip2')
     s = seg2b
     s.vP4.add_ramp_ss(0, 100, 50, 100)
     s.vP4.add_ramp_ss(100, 200, 100, 50)
 
-#    s.MW_qubit_2.add_MW_pulse(50, 150, 50, 200e6, AM=tukey)
-
-    s.MW_qubit_1.add_MW_pulse(0, 300, 20, 35e6)
-
-#    s.MW_qubit_3.add_MW_pulse(250, 300, 20, 20e6)
+    s.q1.add_MW_pulse(0, 300, 20, 2.435e9)
 
     seg3 = pulse_lib.mk_segment('measure', 1e8)
     s = seg3
@@ -136,17 +48,15 @@ def create_seq(pulse_lib):
     s.reset_time()
     s.add_HVI_marker('ping', 99)
 
-
     # segment without data. Will be used for DC compensation with low sample rate
     seg4 = pulse_lib.mk_segment('dc compensation', 1e7)
-    # wait 10 ns (i.e. 1 sample at 1e8 MSa/s)
+    # wait 100 ns (i.e. 10 samples at 1e8 MSa/s)
     seg4.P1.wait(100)
 
     # generate the sequence and upload it.
     my_seq = pulse_lib.mk_sequence([seg1, seg2, seg2b, seg3, seg4])
     my_seq.set_hw_schedule(HardwareScheduleMock())
     my_seq.n_rep = 1
-#    my_seq.sample_rate = 2e8
 
     return my_seq
 
@@ -175,11 +85,12 @@ def play_next():
     my_seq.play([index], release=True)
 
 
-awg1 = MockM3202A("A1", 0, 2)
-awg2 = MockM3202A("A2", 0, 3)
-awg3 = MockM3202A_fpga("A3", 0, 4)
 
-pulse = init_pulselib(awg1, awg2, awg3)
+# create "AWG1","AWG2"
+awgs, digs = init_hardware()
+
+# create channels
+pulse = init_pulselib(awgs, digs, virtual_gates=True)
 
 my_seq = create_seq(pulse)
 
@@ -189,7 +100,7 @@ job = my_seq.upload([0])
 
 my_seq.play([0], release=False)
 
-plot(my_seq, job, (awg1, awg2, awg3) )
+plot(my_seq, job, awgs)
 pprint(job.upload_info)
 
 my_seq.play([0], release=True)
diff --git a/pulse_lib/examples/example_virtual_matrix_plotting.py b/pulse_lib/examples/example_virtual_matrix_plotting.py
index 9b1aa480d09f3b46adf8cc0f09ffef705ed62820..650c92f6ecb2d668a50b6d0497ee9157e6a7a2e6 100644
--- a/pulse_lib/examples/example_virtual_matrix_plotting.py
+++ b/pulse_lib/examples/example_virtual_matrix_plotting.py
@@ -7,10 +7,10 @@ from utils.plot import plot_awgs
 
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # create channels P1, P2
-p = init_pulselib(awgs, virtual_gates=True)
+p = init_pulselib(awgs, digs, virtual_gates=True)
 
 
 seg1 = p.mk_segment()
diff --git a/pulse_lib/examples/psb_example.py b/pulse_lib/examples/psb_example.py
index 4c4b22a639cc339b7399b36656a5a5c96ddf4003..d36fe34718cec7077be860fe934e5fab58943a0f 100644
--- a/pulse_lib/examples/psb_example.py
+++ b/pulse_lib/examples/psb_example.py
@@ -9,10 +9,10 @@ from utils.plot import plot_awgs
 
 
 # create "AWG1"
-awgs = init_hardware()
+awgs, digs = init_hardware()
 
 # create channels P1, P2
-p = init_pulselib(awgs, virtual_gates=True)
+p = init_pulselib(awgs, digs, virtual_gates=True)
 
 seg  = p.mk_segment()
 
diff --git a/pulse_lib/examples/utils/plot.py b/pulse_lib/examples/utils/plot.py
index c931cdb1a4f22fc518f9e743052ca57e91d0062b..e4f0c6d04c81717ed1f334ed8c5e8c7d00cf926e 100644
--- a/pulse_lib/examples/utils/plot.py
+++ b/pulse_lib/examples/utils/plot.py
@@ -2,10 +2,19 @@
 import matplotlib.pyplot as pt
 
 def plot_awgs(awgs, bias_T_rc_time=None):
+    do_plot = False
+    for awg in awgs:
+        if hasattr(awg, 'plot'):
+            do_plot = True
+
+    if not do_plot:
+        return
+
     pt.figure()
 
     for awg in awgs:
-        awg.plot(bias_T_rc_time=bias_T_rc_time)
+        if hasattr(awg, 'plot'):
+            awg.plot(bias_T_rc_time=bias_T_rc_time)
 
     pt.legend()
     pt.ylabel('amplitude [V]')
diff --git a/pulse_lib/qblox/pulsar_sequencers.py b/pulse_lib/qblox/pulsar_sequencers.py
new file mode 100644
index 0000000000000000000000000000000000000000..02959cdca7365a3edcd2a51b5d0fb0bda04905a1
--- /dev/null
+++ b/pulse_lib/qblox/pulsar_sequencers.py
@@ -0,0 +1,208 @@
+from numbers import Number
+from copy import copy
+import numpy as np
+from .rendering import render_custom_pulse
+
+class SequenceBuilderBase:
+    def __init__(self, name, sequencer):
+        self.name = name
+        self.seq = sequencer
+        self.t_end = 0
+        self.sinewaves = []
+        self.t_next_marker = None
+        self.imarker = 0
+        self.max_output_voltage = sequencer.max_output_voltage
+
+    def register_sinewave(self, waveform):
+        try:
+            index = self.sinewaves.index(waveform)
+            waveid = f'sine{index}'
+        except:
+            index = len(self.sinewaves)
+            self.sinewaves.append(waveform)
+            waveid = f'sine{index}'
+            data = waveform.render()
+            self.seq.add_wave(waveid, data)
+        return waveid
+
+    def register_sinewave_iq(self, waveform):
+        try:
+            index = self.sinewaves.index(waveform)
+            waveids = (f'iq{index}I', f'iq{index}Q')
+        except:
+            index = len(self.sinewaves)
+            self.sinewaves.append(waveform)
+            waveids = (f'iq{index}I', f'iq{index}Q')
+            data = waveform.render_iq()
+            self.seq.add_wave(waveids[0], data[0])
+            self.seq.add_wave(waveids[1], data[1])
+        return waveids
+
+    def add_markers(self, markers):
+        self.markers = markers
+        self.imarker = -1
+        self.set_next_marker()
+
+    def set_next_marker(self):
+        self.imarker += 1
+        if len(self.markers) > self.imarker:
+            self.t_next_marker = self.markers[self.imarker][0]
+        else:
+            self.t_next_marker = None
+
+    def insert_markers(self, t):
+        while self.t_next_marker is not None and t > self.t_next_marker:
+            marker = self.markers[self.imarker]
+            self._set_markers(marker[0], marker[1])
+            self.set_next_marker()
+
+    def _set_markers(self, t, value):
+        self.seq.set_markers(value, t_offset=t)
+
+    def _update_time(self, t, duration):
+        if t < self.t_end:
+            raise Exception(f'Overlapping pulses {t} > {self.t_end} ({self.name})')
+        self.insert_markers(t)
+        self.t_end = t + duration
+
+    def add_comment(self, comment):
+        self.seq.add_comment(comment)
+
+    def close(self):
+        while self.t_next_marker is not None:
+            marker = self.markers[self.imarker]
+            self._set_markers(marker[0], marker[1])
+            self._update_time(self.t_next_marker, 0.0)
+            self.set_next_marker()
+
+
+class VoltageSequenceBuilder(SequenceBuilderBase):
+    def __init__(self, name, sequencer, rc_time=None):
+        super().__init__(name, sequencer)
+        self.rc_time = rc_time
+        self.integral = 0.0
+        self.custom_pulses = []
+        if rc_time is not None:
+            self.compensation_factor = 1 / (1e9 * rc_time)
+        else:
+            self.compensation_factor = 0.0
+
+    def ramp(self, t, duration, v_start, v_end):
+        self._update_time(t, duration)
+        v_start_comp = self._compensate_bias_T(v_start)
+        v_end_comp = self._compensate_bias_T(v_end)
+        self.integral += duration * (v_start + v_end)/2
+        self.seq.ramp(duration, v_start_comp, v_end_comp, t_offset=t, v_after=None)
+
+    def set_offset(self, t, duration, v):
+        # sequencer only updates offset, no continuing action: duration=0
+        self._update_time(t, 0)
+        v_comp = self._compensate_bias_T(v)
+        self.integral += duration * v
+        if self.rc_time and duration > 0.01 * 1e9 * self.rc_time:
+            self._update_time(t, duration)
+            v_end = self._compensate_bias_T(v)
+            self.seq.ramp(duration, v_comp, v_end, t_offset=t, v_after=None)
+        else:
+            self.seq.set_offset(v_comp, t_offset=t)
+
+    def pulse(self, t, duration, amplitude, waveform):
+        self._update_time(t, duration)
+        # TODO @@@ add 2*np.pi*t*frequency*1e-9 to phase ??
+        wave_id = self.register_sinewave(waveform)
+        self.seq.shaped_pulse(wave_id, amplitude, t_offset=t)
+
+    def custom_pulse(self, t, duration, amplitude, custom_pulse):
+        self._update_time(t, duration)
+        wave_id = self.register_custom_pulse(custom_pulse, amplitude)
+        self.seq.shaped_pulse(wave_id, 1.0, t_offset=t)
+
+    def register_custom_pulse(self, custom_pulse, scaling):
+        data = render_custom_pulse(custom_pulse, scaling)
+        for index,wave in enumerate(self.custom_pulses):
+            if np.all(wave == data):
+                return f'pulse{index}'
+        index = len(self.custom_pulses)
+        waveid = f'pulse{index}'
+        self.custom_pulses.append(data)
+        self.seq.add_wave(waveid, data)
+        return waveid
+
+
+    def _compensate_bias_T(self, v):
+        return v + self.integral * self.compensation_factor
+
+
+class IQSequenceBuilder(SequenceBuilderBase):
+    def __init__(self, name, sequencer, nco_frequency):
+        super().__init__(name, sequencer)
+        self.nco_frequency = nco_frequency
+        self.add_comment(f'IQ: NCO={self.nco_frequency/1e6:7.2f} MHz')
+
+    def pulse(self, t, duration, amplitude, waveform):
+        self._update_time(t, duration)
+        self.add_comment(f'MW pulse {waveform.frequency/1e6:6.2f} MHz {duration} ns')
+        waveform = copy(waveform)
+        waveform.frequency -= self.nco_frequency
+
+        if abs(waveform.frequency) > 1:
+            # TODO @@@ Fix coherent pulses
+            print(f'Warning: incorrect phase for pulse at {t} ns')
+            wave_ids = self.register_sinewave_iq(waveform)
+            self.seq.shaped_pulse(wave_ids[0], amplitude,
+                                  wave_ids[1], amplitude,
+                                  t_offset=t)
+        elif not isinstance(waveform.phmod, Number):
+            wave_ids = self.register_sinewave_iq(waveform)
+            self.seq.shaped_pulse(wave_ids[0], amplitude,
+                                  wave_ids[1], amplitude,
+                                  t_offset=t)
+        else:
+            # frequency is less than 1 Hz make it 0.
+            waveform.frequency = 0
+            # phase is constant
+            cycles = 2*np.pi*(waveform.phase + waveform.phmod)
+            if isinstance(waveform.amod, Number):
+                ampI = amplitude * waveform.amod * np.sin(cycles)
+                ampQ = amplitude * waveform.amod * np.cos(cycles)
+                # generate block pulse
+                self.seq.block_pulse(duration, ampI, ampQ, t_offset=t)
+            else:
+                # phase is accounted for in ampI, ampQ
+                waveform.phase = np.pi*0.5
+                waveform.phmod = 0
+                ampI = amplitude * np.sin(cycles)
+                ampQ = amplitude * np.cos(cycles)
+                # same wave for I and Q
+                wave_id = self.register_sinewave(waveform)
+                self.seq.shaped_pulse(wave_id, ampI, wave_id, ampQ, t_offset=t)
+
+    def shift_phase(self, t, phase):
+        self._update_time(t, 0.0)
+        self.seq.shift_phase(phase, t_offset=t)
+
+
+
+class AcquisitionSequenceBuilder(SequenceBuilderBase):
+    def __init__(self, name, sequencer, n_repetitions):
+        super().__init__(name, sequencer)
+        self.n_repetitions = n_repetitions
+        self.n_triggers = 0
+        # allocate minim size later adjust for number of triggers
+        self.seq.add_acquisition_bins('default', n_repetitions)
+
+    def acquire(self, t, t_measure, n=1):
+        self.n_triggers += n
+        if n == 1:
+            self.seq.acquire('default', 'increment', t_offset=t)
+        else:
+            period = t_measure
+            self.seq.repeated_acquire(n, period, 'default', 'increment', )
+
+    def close(self):
+        super().close()
+        num_bins = self.n_triggers * self.n_repetitions
+        self.seq.add_acquisition_bins('default', num_bins)
+
+
+
diff --git a/pulse_lib/qblox/pulsar_uploader.py b/pulse_lib/qblox/pulsar_uploader.py
new file mode 100644
index 0000000000000000000000000000000000000000..04a235eba6152b1cd4c6d593c560e99f7f79dffd
--- /dev/null
+++ b/pulse_lib/qblox/pulsar_uploader.py
@@ -0,0 +1,720 @@
+import time
+from datetime import datetime
+import numpy as np
+import logging
+from dataclasses import dataclass, field
+from typing import List, Dict, Optional, Union
+
+from .rendering import SineWaveform, get_modulation
+from .pulsar_sequencers import (
+        VoltageSequenceBuilder,
+        IQSequenceBuilder,
+        AcquisitionSequenceBuilder,
+        SequenceBuilderBase)
+
+from q1pulse import Q1Instrument
+
+from pulse_lib.segments.data_classes.data_IQ import IQ_data_single
+from pulse_lib.segments.data_classes.data_pulse import (
+        PhaseShift, custom_pulse_element, OffsetRamp)
+
+
+def iround(value):
+    return int(value+0.5)
+
+class PulsarConfig:
+    ALIGNMENT = 4 # pulses must be aligned on 4 ns boundaries
+
+
+class PulsarUploader:
+    verbose = True
+
+    def __init__(self, awg_devices, awg_channels, marker_channels,
+                 IQ_channels, qubit_channels, digitizers, digitizer_channels):
+        self.awg_channels = awg_channels
+        self.marker_channels = marker_channels
+        self.IQ_channels = IQ_channels
+        self.qubit_channels = qubit_channels
+        self.digitizer_channels = digitizer_channels
+
+        self.jobs = []
+
+        q1 = Q1Instrument()
+        self.q1instrument = q1
+
+        for awg in awg_devices.values():
+            q1.add_qcm(awg)
+        for module in digitizers.values():
+            # QRM is passed as digitizer
+            q1.add_qrm(module)
+
+        self._link_markers_to_seq()
+        self._get_voltage_channels()
+
+        for name, awg_ch in self.awg_voltage_channels.items():
+            q1.add_control(name, awg_ch.awg_name, [awg_ch.channel_number])
+
+        for name, qubit_ch in self.qubit_channels.items():
+            iq_out_channels = qubit_ch.iq_channel.IQ_out_channels
+            out_channels = [self.awg_channels[iq_out_ch.awg_channel_name]
+                            for iq_out_ch in iq_out_channels]
+            module_name = out_channels[0].awg_name
+            # TODO @@@ check I and Q phase.
+            q1.add_control(name, module_name, [out_ch.channel_number for out_ch in out_channels])
+
+        for name, dig_ch in self.digitizer_channels.items():
+            q1.add_readout(name, dig_ch.module_name)
+
+        for name, marker_ch in self.marker_channels.items():
+            # TODO implement marker channel inversion
+            if marker_ch.invert:
+                raise Exception(f'Marker channel inversion not (yet) supported')
+
+
+    def _get_voltage_channels(self):
+        iq_out_channels = []
+
+        for IQ_channel in self.IQ_channels.values():
+            iq_pair = IQ_channel.IQ_out_channels
+            if len(iq_pair) != 2:
+                raise Exception(f'IQ-channel should have 2 awg channels '
+                                f'({iq_pair})')
+            out_names = [self.awg_channels[ch_info.awg_channel_name] for ch_info in iq_pair]
+            awg_names = [awg_channel.awg_name for awg_channel in out_names]
+
+            if awg_names[0] != awg_names[1]:
+                raise Exception(f'IQ channels should be on 1 awg: {iq_pair}')
+
+            iq_out_channels += [ch_info.awg_channel_name for ch_info in iq_pair]
+
+        self.awg_voltage_channels = {}
+        for name, awg_channel in self.awg_channels.items():
+            if name not in iq_out_channels:
+                self.awg_voltage_channels[name] = awg_channel
+
+
+
+    def _link_markers_to_seq(self):
+        default_iq_markers = {}
+        for qubit_channel in self.qubit_channels.values():
+            iq_channel = qubit_channel.iq_channel
+            marker_channels = iq_channel.marker_channels
+            for marker_name in marker_channels:
+                awg_module_name = iq_channel.IQ_out_channels[0].awg_channel_name
+                m_ch = self.marker_channels[marker_name]
+                if awg_module_name != m_ch.module_name:
+                    default_iq_markers[m_ch.name] = qubit_channel.channel_name
+
+        seq_markers = {}
+        marker_sequencers = []
+        for channel_name, marker_channel in self.marker_channels.items():
+            if marker_channel.sequencer_name is not None:
+                seq_name = marker_channel.sequencer_name
+            elif channel_name in default_iq_markers:
+                seq_name = default_iq_markers[channel_name]
+            else:
+                seq_name = f'_M_{marker_channel.module_name}'
+                marker_sequencers.append(seq_name)
+                self.q1instrument.add_control(seq_name, marker_channel.module_name)
+            mlist = seq_markers.setdefault(seq_name, [])
+            mlist.append(channel_name)
+
+        self.seq_markers = seq_markers
+        self.marker_sequencers = marker_sequencers
+
+
+    @property
+    def supports_conditionals(self):
+        return False
+
+    def get_effective_sample_rate(self, sample_rate):
+        """
+        Returns the sample rate that will be used by the AWG.
+        """
+        return 1e9
+
+
+    def create_job(self, sequence, index, seq_id, n_rep, sample_rate, neutralize=True):
+        # remove any old job with same sequencer and index
+        self.release_memory(seq_id, index)
+        return Job(self.jobs, sequence, index, seq_id, n_rep, sample_rate, neutralize)
+
+
+    def add_upload_job(self, job):
+        '''
+        add a job to the uploader.
+        Args:
+            job (upload_job) : upload_job object that defines what needs to be uploaded and possible post processing of the waveforms (if needed)
+        '''
+        '''
+        Class taking care of putting the waveform on the right AWG.
+
+        Steps:
+        1) get all the upload data
+        2) perform DC correction (if needed)
+        3) convert data in an aprropriate upload format
+        4) start upload of all data
+        5) store reference to uploaded waveform in job
+        '''
+        start = time.perf_counter()
+
+        aggregator = UploadAggregator(self.q1instrument, self.awg_channels,
+                                      self.marker_channels, self.digitizer_channels,
+                                      self.qubit_channels, self.awg_voltage_channels,
+                                      self.marker_sequencers, self.seq_markers
+                                      )
+
+        aggregator.build(job)
+
+        self.jobs.append(job)
+
+        duration = time.perf_counter() - start
+        logging.debug(f'generated upload data ({duration*1000:6.3f} ms)')
+        print(f'Generated upload data in {duration*1000:6.3f} ms')
+
+
+    def __get_job(self, seq_id, index):
+        """
+        get job data of an uploaded segment
+        Args:
+            seq_id (uuid) : id of the sequence
+            index (tuple) : index that has to be played
+        Return:
+            job (upload_job) :job, with locations of the sequences to be uploaded.
+        """
+        for job in self.jobs:
+            if job.seq_id == seq_id and job.index == index and not job.released:
+                return job
+
+        logging.error(f'Job not found for index {index} of seq {seq_id}')
+        raise ValueError(f'Sequence with id {seq_id}, index {index} not placed for upload .. . Always make sure to first upload your segment and then do the playback.')
+
+
+    def play(self, seq_id, index, release_job = True):
+        """
+        start playback of a sequence that has been uploaded.
+        Args:
+            seq_id (uuid) : id of the sequence
+            index (tuple) : index that has to be played
+            release_job (bool) : release memory on AWG after done.
+        """
+
+        job =  self.__get_job(seq_id, index)
+
+#        # TODO @@@ cleanup frequency update hack
+        for name, qubit_channel in self.qubit_channels.items():
+            nco_frequency = qubit_channel.reference_frequency - qubit_channel.iq_channel.LO
+            self.q1instrument.controllers[name].nco_frequency = nco_frequency
+        self.q1instrument.run_program(job.program)
+
+        if release_job:
+            job.release()
+
+
+    def release_memory(self, seq_id=None, index=None):
+        """
+        Release job memory for `seq_id` and `index`.
+        Args:
+            seq_id (uuid) : id of the sequence. if None release all
+            index (tuple) : index that has to be released; if None release all.
+        """
+        for job in self.jobs:
+            if (seq_id is None
+                or (job.seq_id == seq_id and (index is None or job.index == index))):
+                job.release()
+
+
+    def release_jobs(self):
+        for job in self.jobs:
+            job.release()
+
+
+
+class Job(object):
+    """docstring for upload_job"""
+    def __init__(self, job_list, sequence, index, seq_id, n_rep, sample_rate, neutralize=True, priority=0):
+        '''
+        Args:
+            job_list (list): list with all jobs.
+            sequence (list of list): list with list of the sequence
+            index (tuple) : index that needs to be uploaded
+            seq_id (uuid) : if of the sequence
+            n_rep (int) : number of repetitions of this sequence.
+            sample_rate (float) : sample rate
+            neutralize (bool) : place a neutralizing segment at the end of the upload
+            priority (int) : priority of the job (the higher one will be excuted first)
+        '''
+        self.job_list = job_list
+        self.sequence = sequence
+        self.seq_id = seq_id
+        self.index = index
+        self.n_rep = n_rep
+        self.default_sample_rate = sample_rate
+        self.neutralize = neutralize
+        self.priority = priority
+        self.playback_time = 0 #total playtime of the waveform
+
+        self.released = False
+
+        logging.debug(f'new job {seq_id}-{index}')
+
+
+    def add_hw_schedule(self, hw_schedule, schedule_params):
+        """
+        Add the scheduling to the AWG waveforms.
+        args:
+            hw_schedule (HardwareSchedule) : schedule for repetitively starting the AWG waveforms
+            kwargs : keyword arguments for the hardware schedule (see usage in the examples)
+        """
+        self.hw_schedule = hw_schedule
+        self.schedule_params = schedule_params
+
+    def release(self):
+        if self.released:
+            logging.warning(f'job {self.seq_id}-{self.index} already released')
+            return
+
+        self.upload_info = None
+        logging.debug(f'release job {self.seq_id}-{self.index}')
+        self.released = True
+
+        if self in self.job_list:
+            self.job_list.remove(self)
+
+
+    def __del__(self):
+        if not self.released:
+            logging.warn(f'Job {self.seq_id}-{self.index} was not released. '
+                         'Automatic release in destructor.')
+            self.release()
+
+
+@dataclass
+class ChannelInfo:
+    # static data
+    delay_ns: float = 0
+    amplitude: float = 0
+    attenuation: float = 1.0
+    dc_compensation: bool = False
+    dc_compensation_min: float = 0.0
+    dc_compensation_max: float = 0.0
+    bias_T_RC_time: Optional[float] = None
+    # aggregation state
+    integral: float = 0.0
+
+
+@dataclass
+class JobUploadInfo:
+    dc_compensation_duration_ns: float = 0.0
+    dc_compensation_voltages: Dict[str, float] = field(default_factory=dict)
+
+@dataclass
+class SegmentRenderInfo:
+    # original times from sequence, cummulative start/end times
+    # first segment starts at t_start = 0
+    t_start: float
+    npt: int # sample rate = 1GSa/s
+
+    @property
+    def t_end(self):
+        return self.t_start + self.npt
+
+
+@dataclass
+class DigAcquisition:
+    start: int
+    t_measure: Optional[int] = None
+    n: int = 1
+    threshold: Optional[int] = None
+
+
+class UploadAggregator:
+    verbose = False
+
+    def __init__(self, q1instrument, awg_channels, marker_channels, digitizer_channels,
+                 qubit_channels, awg_voltage_channels, marker_sequencers, seq_markers):
+
+        self.q1instrument = q1instrument
+        self.awg_voltage_channels = awg_voltage_channels
+        self.marker_channels = marker_channels
+        self.digitizer_channels = digitizer_channels
+        self.qubit_channels = qubit_channels
+        self.marker_sequencers = marker_sequencers
+        self.seq_markers = seq_markers
+
+        self.channels = dict()
+
+        delays = []
+        for channel in awg_channels.values():
+            info = ChannelInfo()
+            self.channels[channel.name] = info
+
+            info.attenuation = channel.attenuation
+            info.delay_ns = channel.delay
+            info.amplitude = None # channel.amplitude cannot be taken into account
+            info.bias_T_RC_time = channel.bias_T_RC_time
+            delays.append(channel.delay)
+
+            # Note: Compensation limits are specified before attenuation, i.e. at AWG output level.
+            #       Convert compensation limit to device level.
+            info.dc_compensation_min = channel.compensation_limits[0] * info.attenuation
+            info.dc_compensation_max = channel.compensation_limits[1] * info.attenuation
+            info.dc_compensation = info.dc_compensation_min < 0 and info.dc_compensation_max > 0
+
+        for channel in marker_channels.values():
+            delays.append(channel.delay - channel.setup_ns)
+            delays.append(channel.delay + channel.hold_ns)
+
+        self.max_pre_start_ns = -min(0, *delays)
+        self.max_post_end_ns = max(0, *delays)
+
+
+    def _integrate(self, job):
+
+        if not job.neutralize:
+            return
+
+        for iseg,seg in enumerate(job.sequence):
+            # fixed sample rate
+            sample_rate = 1e9
+
+            for channel_name, channel_info in self.channels.items():
+                if iseg == 0:
+                    channel_info.integral = 0
+
+                if channel_info.dc_compensation:
+                    seg_ch = seg[channel_name]
+                    channel_info.integral += seg_ch.integrate(job.index, sample_rate)
+                    logging.debug(f'Integral seg:{iseg} {channel_name} integral:{channel_info.integral}')
+
+
+    def _process_segments(self, job):
+        self.segments = []
+        segments = self.segments
+        t_start = 0
+        for seg in job.sequence:
+            # work with sample rate in GSa/s
+            sample_rate = 1
+            duration = seg.get_total_time(job.index)
+            npt =  int((duration * sample_rate)+0.5)
+            info = SegmentRenderInfo(t_start, npt)
+            segments.append(info)
+            t_start = info.t_end
+
+        # add DC compensation
+        compensation_time = self.get_max_compensation_time()
+        compensation_time_ns = int(np.ceil(compensation_time*1e9 / 4)) * 4 # ns @@@ add align function
+        logging.debug(f'DC compensation time: {compensation_time_ns} ns')
+
+        job.upload_info.dc_compensation_duration_ns = compensation_time_ns
+
+        job.playback_time = segments[-1].t_end + compensation_time_ns
+        logging.debug(f'Playback time: {job.playback_time} ns')
+
+        if UploadAggregator.verbose:
+            for segment in segments:
+                logging.info(f'segment: {segment}')
+
+
+    def get_markers(self, job, marker_channel):
+        # Marker on periods can overlap, also across segments.
+        # Get all start/stop times and merge them.
+        start_stop = []
+        segments = self.segments
+        for iseg,(seg,seg_render) in enumerate(zip(job.sequence,segments)):
+            offset = seg_render.t_start + marker_channel.delay + self.max_pre_start_ns
+            seg_ch = seg[marker_channel.name]
+            ch_data = seg_ch._get_data_all_at(job.index)
+
+            for pulse in ch_data.my_marker_data:
+                start_stop.append((offset + pulse.start - marker_channel.setup_ns, +1))
+                start_stop.append((offset + pulse.stop + marker_channel.hold_ns, -1))
+
+        # merge markers
+        marker_value = 1 << marker_channel.channel_number
+        markers = []
+        s = 0
+        m = sorted(start_stop, key=lambda e:e[0])
+        for t,on_off in m:
+            s += on_off
+            if s < 0:
+                logging.error(f'Marker error {marker_channel.name} {on_off}')
+            if s == 1 and on_off == 1:
+                markers.append((t, s, marker_value))
+            if s == 0 and on_off == -1:
+                markers.append((t, s, marker_value))
+
+        return markers
+
+    def get_markers_seq(self, job, seq_name):
+        marker_names = self.seq_markers.get(seq_name, [])
+        if len(marker_names) == 0:
+            return []
+
+        markers = []
+        for marker_name in marker_names:
+            marker_channel = self.marker_channels[marker_name]
+
+            markers += self.get_markers(job, marker_channel)
+
+        s = 0
+        last = -1
+        m = sorted(markers, key=lambda e:e[0])
+        seq_markers = []
+        for t,on_off,value in m:
+            if on_off:
+                s |= value
+            else:
+                s &= ~value
+            if t == last:
+                seq_markers[-1] = (t,s)
+            else:
+                seq_markers.append((t,s))
+
+        return seq_markers
+
+    def add_awg_channel(self, job, channel_name):
+        segments = self.segments
+        channel_info = self.channels[channel_name]
+
+        t_offset = int((self.max_pre_start_ns - channel_info.delay_ns) / 4) * 4
+
+        seq = VoltageSequenceBuilder(channel_name, self.program[channel_name],
+                                     rc_time=channel_info.bias_T_RC_time)
+        scaling = 1/(channel_info.attenuation * seq.max_output_voltage*1000)
+
+        markers = self.get_markers_seq(job, channel_name)
+        seq.add_markers(markers)
+
+        for iseg,(seg,seg_render) in enumerate(zip(job.sequence,segments)):
+            seg_start = seg_render.t_start + t_offset
+            seg_ch = seg[channel_name]
+            data = seg_ch._get_data_all_at(job.index)
+            entries = data.get_data_elements()
+            for e in entries:
+                if isinstance(e, OffsetRamp):
+                    t = iround(e.time + seg_start)
+                    v_start = scaling * e.v_start
+                    v_stop = scaling * e.v_stop
+                    duration = iround(e.duration)
+                    if abs(v_start - v_stop) > 6e-5:
+                        # ramp only when > 2 bits on 16-bit signed resolution
+                        seq.ramp(t, duration, v_start, v_stop)
+                    else:
+                        seq.set_offset(t, duration, v_start)
+                elif isinstance(e, IQ_data_single):
+                    t = iround(e.start + seg_start)
+                    duration = iround(e.stop - e.start)
+                    amod, phmod = get_modulation(e.envelope, duration)
+                    sinewave = SineWaveform(duration, e.frequency, e.start_phase, amod, phmod)
+                    seq.pulse(t, duration, e.amplitude*scaling, sinewave)
+                elif isinstance(e, PhaseShift):
+                    t = iround(e.time + seg_start)
+                    e.phase_shift
+                    raise Exception('Phase shift not supported for AWG channel')
+                elif isinstance(e, custom_pulse_element):
+                    t = iround(e.start + seg_start)
+                    duration = iround(e.stop - e.start)
+                    seq.custom_pulse(t, duration, scaling, e)
+                else:
+                    raise Exception('Unknown pulse element {type(e)}')
+
+        t_end = seg_render.t_end + t_offset
+        seq.set_offset(t_end, 0, 0.0)
+
+        compensation_ns = round(job.upload_info.dc_compensation_duration_ns)
+        if job.neutralize and compensation_ns > 0 and channel_info.dc_compensation:
+            compensation_voltage = -channel_info.integral / compensation_ns * 1e9 * scaling
+            job.upload_info.dc_compensation_voltages[channel_name] = compensation_voltage
+            logging.debug(f'DC compensation {channel_name}: {compensation_voltage:6.1f} mV {compensation_ns} ns')
+            seq.add_comment(f'DC compensation: {compensation_voltage:6.1f} mV {compensation_ns} ns')
+            seq.set_offset(t_end, compensation_ns, compensation_voltage)
+            seq.set_offset(t_end + compensation_ns, 0, 0.0)
+
+        seq.close()
+
+    def add_qubit_channel(self, job, qubit_channel):
+        segments = self.segments
+
+        channel_name = qubit_channel.channel_name
+
+        delays = []
+        for i in range(2):
+            awg_channel_name = qubit_channel.iq_channel.IQ_out_channels[i].awg_channel_name
+            delays.append(self.channels[awg_channel_name].delay_ns)
+        if delays[0] != delays[1]:
+            raise Exception(f'I/Q Channel delays must be equal ({channel_name})')
+        t_offset = int((self.max_pre_start_ns + delays[0]) / 4) * 4
+
+        # TODO @@@ LO frequency can change during sweep
+        lo_freq = qubit_channel.iq_channel.LO
+        nco_freq = qubit_channel.reference_frequency-lo_freq
+
+
+        seq = IQSequenceBuilder(channel_name, self.program[channel_name],
+                                nco_freq)
+        attenuation = 1.0 # TODO @@@ check if this is always true..
+        scaling = 1/(attenuation * seq.max_output_voltage*1000)
+
+        markers = self.get_markers_seq(job, channel_name)
+        seq.add_markers(markers)
+
+        for iseg,(seg,seg_render) in enumerate(zip(job.sequence,segments)):
+            seg_start = seg_render.t_start + t_offset
+
+            seg_ch = seg[channel_name]
+            data = seg_ch._get_data_all_at(job.index)
+
+            entries = data.get_data_elements()
+            for e in entries:
+                if isinstance(e, OffsetRamp):
+                    raise Exception('Voltage steps and ramps are not supported for IQ channel')
+                elif isinstance(e, IQ_data_single):
+                    t = iround(e.start + seg_start)
+                    duration = iround(e.stop - e.start)
+                    amod, phmod = get_modulation(e.envelope, duration)
+                    sinewave = SineWaveform(duration, e.frequency-lo_freq,
+                                            e.start_phase, amod, phmod)
+                    seq.pulse(t, duration, e.amplitude*scaling, sinewave)
+                elif isinstance(e, PhaseShift):
+                    t = iround(e.time + seg_start)
+                    seq.shift_phase(t, e.phase_shift)
+                elif isinstance(e, custom_pulse_element):
+                    raise Exception('Custom pulses are not supported for IQ channel')
+                else:
+                    raise Exception('Unknown pulse element {type(e)}')
+
+        # add final markers
+        seq.close()
+
+
+    def add_acquisition_channel(self, job, digitizer_channel):
+        channel_name = digitizer_channel.name
+        t_offset = int(self.max_pre_start_ns / 4) * 4
+        acquisitions = []
+
+        seq = AcquisitionSequenceBuilder(channel_name, self.program[channel_name], job.n_rep)
+
+        markers = self.get_markers_seq(job, channel_name)
+        seq.add_markers(markers)
+
+        for name, value in job.schedule_params.items():
+            if name.startswith('dig_trigger_') or name.startswith('dig_wait'):
+                time = value + t_offset
+                acquisitions.append(DigAcquisition(time))
+
+        for iseg, (seg, seg_render) in enumerate(zip(job.sequence, self.segments)):
+            seg_start = seg_render.t_start + t_offset
+            seg_ch = seg[channel_name]
+            acquisition_data = seg_ch._get_data_all_at(job.index).get_data()
+            for acquisition in acquisition_data:
+                if digitizer_channel.downsample_rate is not None:
+                    period_ns = iround(1e8/digitizer_channel.downsample_rate) * 10
+                    n_cycles = int(acquisition.t_measure / period_ns)
+                    t_measure = period_ns
+                else:
+                    n_cycles = 1
+                    t_measure = acquisition.t_measure
+                t = iround(acquisition.start + seg_start)
+                acquisitions.append(DigAcquisition(t,
+                                                   t_measure,
+                                                   n=n_cycles,
+                                                   threshold=acquisition.threshold))
+        for acq in acquisitions:
+            seq.acquire(acq.start, acq.t_measure, acq.n)
+
+        seq.close()
+
+    def add_marker_seq(self, job, channel_name):
+        seq = SequenceBuilderBase(channel_name, self.program[channel_name])
+
+        markers = self.get_markers_seq(job, channel_name)
+        seq.add_markers(markers)
+        seq.close()
+
+    def build(self, job):
+        job.upload_info = JobUploadInfo()
+        times = []
+        times.append(['start', time.perf_counter()])
+
+        name = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
+        self.program = self.q1instrument.new_program(name)
+        job.program = self.program
+
+        self.program._timeline.disable_update() # @@@ Yuk
+
+        times.append(['init', time.perf_counter()])
+
+        self._integrate(job)
+
+        times.append(['integrate', time.perf_counter()])
+
+        self._process_segments(job)
+
+        times.append(['proc_seg', time.perf_counter()])
+
+        for channel_name in self.awg_voltage_channels:
+            self.add_awg_channel(job, channel_name)
+
+        times.append(['awg', time.perf_counter()])
+
+        for qubit_channel in self.qubit_channels.values():
+            self.add_qubit_channel(job, qubit_channel)
+
+        times.append(['qubit', time.perf_counter()])
+
+        for dig_channel in self.digitizer_channels.values():
+            self.add_acquisition_channel(job, dig_channel)
+
+        times.append(['dig', time.perf_counter()])
+
+        for seq_name in self.marker_sequencers:
+            self.add_marker_seq(job, seq_name)
+
+        times.append(['marker', time.perf_counter()])
+
+        self.program._timeline.enable_update() # @@@ Yuk
+
+        times.append(['done', time.perf_counter()])
+
+        # NOTE: compilation is ~20% faster with listing=False, add_comments=False
+#        self.program.compile(add_comments=False, listing=False)
+        self.program.compile(listing=True)
+
+        times.append(['compile', time.perf_counter()])
+
+        prev = None
+        for step,t in times:
+            if prev:
+                duration = (t - prev)*1000
+                print(f'duration {step:10} {duration:9.3f} ms')
+            prev = t
+
+    def get_max_compensation_time(self):
+        '''
+        generate a DC compensation of the pulse.
+        As usuallly we put capacitors in between the AWG and the gate on the sample, you need to correct
+        for the fact that the low fequencies are not present in your transfer function.
+        This can be done simply by making the total integral of your function 0.
+
+        Args:
+            sample_rate (float) : rate at which the AWG runs.
+        '''
+        return max(self.get_compensation_time(channel_info) for channel_info in self.channels.values())
+
+    def get_compensation_time(self, channel_info):
+        '''
+        return the minimal compensation time that is needed.
+        Returns:
+            compensation_time : minimal duration that is needed for the voltage compensation
+        '''
+        if not channel_info.dc_compensation:
+            return 0
+
+        if channel_info.integral <= 0:
+            result = -channel_info.integral / channel_info.dc_compensation_max
+        else:
+            result = -channel_info.integral / channel_info.dc_compensation_min
+        return result
+
+
diff --git a/pulse_lib/qblox/rendering.py b/pulse_lib/qblox/rendering.py
new file mode 100644
index 0000000000000000000000000000000000000000..3245798395cc4154ebf3ae549d51161f93807497
--- /dev/null
+++ b/pulse_lib/qblox/rendering.py
@@ -0,0 +1,49 @@
+import numpy as np
+from dataclasses import dataclass
+from typing import Union
+
+def get_modulation(envelope_generator, duration):
+    if envelope_generator is None:
+        am_envelope = 1.0
+        pm_envelope = 0.0
+    else:
+        am_envelope = envelope_generator.get_AM_envelope(duration, 1.0)
+        pm_envelope = envelope_generator.get_PM_envelope(duration, 1.0)
+    return am_envelope, pm_envelope
+
+
+@dataclass
+class SineWaveform:
+    duration: int
+    frequency: float = None
+    phase: float = 0
+    amod: Union[None, float, np.ndarray] = None
+    phmod: Union[None, float, np.ndarray] = None
+
+    def __eq__(self, other):
+        res = (self.duration == other.duration
+                and self.frequency == other.frequency
+                and self.phase == other.phase
+                and np.all(self.amod == other.amod)
+                and np.all(self.phmod == other.phmod)
+                )
+        return res
+
+    def render(self, sample_rate=1e9):
+        total_phase = self.phase + self.phmod
+        t = np.arange(int(self.duration))
+        return self.amod * np.sin(2*np.pi*self.frequency/sample_rate*t + total_phase)
+
+    def render_iq(self, sample_rate=1e9):
+        total_phase = self.phase + self.phmod
+        t = np.arange(int(self.duration))
+        cycles = 2*np.pi*self.frequency/sample_rate*t + total_phase
+        return (self.amod * np.cos(cycles),
+                self.amod * np.sin(cycles))
+
+def render_custom_pulse(custom_pulse, scaling, sample_rate=1e9):
+    duration = custom_pulse.stop - custom_pulse.start
+    data = custom_pulse.func(duration, sample_rate,
+                             custom_pulse.amplitude, **custom_pulse.kwargs)
+    return data * scaling
+
diff --git a/pulse_lib/segments/data_classes/data_pulse.py b/pulse_lib/segments/data_classes/data_pulse.py
index 9d218d29f1e6aa50e0d929fd2f7afabd067beab5..aff6194b1ffc9229c8ecfa1b4226b15300b732ed 100644
--- a/pulse_lib/segments/data_classes/data_pulse.py
+++ b/pulse_lib/segments/data_classes/data_pulse.py
@@ -10,7 +10,7 @@ from typing import Any, Dict, Callable, List
 
 from pulse_lib.segments.utility.rounding import iround
 from pulse_lib.segments.data_classes.data_generic import parent_data
-from pulse_lib.segments.data_classes.data_IQ import envelope_generator
+from pulse_lib.segments.data_classes.data_IQ import envelope_generator, IQ_data_single
 
 total_pulse_deltas = 0
 
@@ -109,6 +109,21 @@ class PhaseShift:
     phase_shift: float
     channel_name: str
 
+    @property
+    def start(self):
+        return self.time
+
+@dataclass
+class OffsetRamp:
+    time: float
+    duration: float # time till next OffsetRamp
+    v_start: float
+    v_stop: float
+
+    @property
+    def start(self):
+        return self.time
+
 # keep till end: start = np.inf
 # slicing:
     # consolidate all in slice on `end`, keep `inf`. delta_new = sum(p.delta for p in slice), ...
@@ -551,6 +566,7 @@ class pulse_data(parent_data):
                 steps = np.zeros(n)
                 ramps = np.zeros(n)
                 amplitudes = np.zeros(n)
+                amplitudes_end = np.zeros(n)
                 for i,delta in enumerate(self.pulse_deltas):
                     times[i] = delta.time
                     steps[i] = delta.step
@@ -561,7 +577,7 @@ class pulse_data(parent_data):
                 ramps = np.cumsum(ramps)
                 amplitudes[1:] = ramps[:-1] * intervals[:-1]
                 amplitudes = np.cumsum(amplitudes) + np.cumsum(steps)
-                amplitudes_end = amplitudes - steps
+                amplitudes_end[:-1] = amplitudes[1:] - steps[1:]
     #            logging.debug(f'points: {list(zip(times, amplitudes))}')
             self._times = times
             self._intervals = intervals
@@ -583,7 +599,7 @@ class pulse_data(parent_data):
         integrated_value = 0
 
         if len(self.pulse_deltas) > 0:
-            integrated_value = 0.5*np.dot((self._amplitudes[:-1] + self._amplitudes_end[1:]),
+            integrated_value = 0.5*np.dot((self._amplitudes[:-1] + self._amplitudes_end[:-1]),
                                           self._intervals[:-1])
 
         for custom_pulse in self.custom_pulse_data:
@@ -593,6 +609,28 @@ class pulse_data(parent_data):
 
         return integrated_value
 
+    def get_data_elements(self):
+        def typeorder(obj):
+            if isinstance(obj, PhaseShift):
+                return 0.1
+            if isinstance(obj, OffsetRamp):
+                return 0.2
+            if isinstance(obj, IQ_data_single):
+                return 0.3
+            if isinstance(obj, custom_pulse_element):
+                return 0.4
+
+        elements = []
+        self._pre_process()
+        for time, duration, v_start, v_stop in zip(self._times, self._intervals,
+                                                   self._amplitudes, self._amplitudes_end):
+            elements.append(OffsetRamp(time, duration, v_start, v_stop))
+        elements += self.custom_pulse_data
+        elements += self.MW_pulse_data
+        elements += self.phase_shifts
+        # Sort on rounded time, next: PhaseShift,Offset,MW_pulse,custom
+        elements.sort(key=lambda p:(int(p.start+0.5)+typeorder(p)))
+        return elements
 
     def _render_custom_pulse(self, custom_pulse, sample_rate):
         duration = custom_pulse.stop - custom_pulse.start
@@ -622,7 +660,7 @@ class pulse_data(parent_data):
             pt1 = t_pt[i+1]
             if pt0 != pt1:
                 if self._ramps[i] != 0:
-                    wvf[pt0:pt1] = np.linspace(self._amplitudes[i], self._amplitudes_end[i+1], pt1-pt0+1)[:-1]
+                    wvf[pt0:pt1] = np.linspace(self._amplitudes[i], self._amplitudes_end[i], pt1-pt0+1)[:-1]
                 else:
                     wvf[pt0:pt1] = self._amplitudes[i]
 
@@ -791,7 +829,7 @@ class pulse_data(parent_data):
             start = self._times[i]
             stop = self._times[i+1]
             v_start = self._amplitudes[i]
-            v_stop = self._amplitudes_end[i+1]
+            v_stop = self._amplitudes_end[i]
             if stop == np.inf:
                 stop = self._end_time
             if stop - start < 1 or (v_start == 0 and v_stop == 0):