# Hello LabOne Q

This example will get you up an running by showing you how to generate pulses on a Zurich Instruments HDAWG (or SHFSG or SHFQC with a quick modification to the descriptor) using LabOne Q. The example code can be executed without having access to a physical instrument. If a device is available, an oscilloscope can be connected in order to verify the output signal. We’ll perform the following three tasks: generating a RF pulse, an IQ pulse, and then both simultaneously.

Once you have played your first pulse, you will be ready to tackle more complex experiments. We recommend checking out Device Setup, Workflow, and Experiments to dive in to LabOne Q.

## Basic Setup

You will begin by importing all commands from LabOne Q and defining a descriptor containing a single HDAWG device. You will define two logical signal lines: a single-channel RF signal on output 1 and a dual-channel IQ signal on outputs 3 and 4. The two signals are part of the logical signals group "q0".

%config IPCompleter.greedy=True

from laboneq.simple import *

descriptor="""
instrument_list:
HDAWG:
uid: device_hdawg
connections:
device_hdawg:
- rf_signal: q0/flux_line
ports: [SIGOUTS/0]
- iq_signal: q0/drive_line
ports: [SIGOUTS/2, SIGOUTS/3]
"""

The descriptor is now used to define the device setup. Here, you will also provide the IP address and port number of the data server running on your host computer, which is connected to the instrument.

device_setup = DeviceSetup.from_descriptor(
descriptor,
server_host="111.22.33.44",
server_port="8004",
setup_name="ZI_HDAWG",
)

### Generating a Pulse on a Single Channel

Now you will define an experiment with a single experiment signal line. It represents a general signal in the experiment, with no assumed information about the device that will play it.

# create experiment object
exp = Experiment(
uid="minimal_experiment",
signals=[
ExperimentSignal("simple_pulse"),
],
)

Next, you will define a pulse sequence on the experiment signal line. You can start by defining a rectangular pulse using the pulse library. Then, place this pulse on the signal line by using a 'play' instruction. Note, that the pulse is surrounded by a section and an acquire loop, as described in detail in the sections and pulses chapter.

# define a pulse to be played, simple constant pulse of 1 us
my_first_pulse = pulse_library.const(length=1e-6, amplitude=0.8)

## experimental pulse sequence
with exp.acquire_loop_rt(
count=1,
uid="shots",
):
with exp.section(uid="pulse"):
#play a simple pulse
exp.play(
signal="simple_pulse",
pulse=my_first_pulse,
)

you will then map the logical signal line we calibrated to the signal of the experiment.

# shortcut to the logical signal group q0
lsg = device_setup.logical_signal_groups["q0"].logical_signals

# define signal map
map_signals = {
"simple_pulse" : lsg["flux_line"]
}

# apply map to the experiment
exp.set_signal_map(map_signals)

you will be ready to run the experiment after opening a session using your device setup.

# connect to session
session = Session(device_setup=device_setup)
session.connect(
do_emulation=True, # If True, no actual connection is performed, experimental chain is only simulated
)

# run experiment
session.run(exp)

In this case the experiment will run in emulation mode, where no actual connection to the device is established. However, the sequencer code and instrument settings are still compiled. The output can also be simulated by using the compiled experiment with the laboneq.simulator.output_simulator.OutputSimulator class. By switching off the emulation mode, you can execute the instructions on your setup. See if you can verify this with a scope!

### Generating an IQ Signal Pair

You will now generate a IQ baseband pulse with a frequency of 500 MHz on outputs 3 and 4 of the HDAWG. As the first step, you will perform a minimal calibration for your logical signal line "drive_line". Applying a calibration to it sets the specified physical device settings on the channel associated with the logical signal in the descriptor.

# calibration of the IQ pulse
lsg["drive_line"].calibration = SignalCalibration(
oscillator=Oscillator(
uid="drive_q0_osc",
frequency=500e6,  # 500 MHz
)
)

Since you will only want to play a single pulse, we can reuse the experiment defined in the first example. To do so, just change the signal map, and link the experiment signal to the logical signal line which we just calibrated:

# define new signal map
map_signals = {
"simple_pulse" : lsg["drive_line"]
}

# apply the signal map
exp.set_signal_map(map_signals)

That’s it! You can now run the same experiment object in the session again. Now, the pulse will be played as an IQ signal on channels 3 and 4 of the HDAWG.

# run the experiment
session.run(exp)

### Sending Both Pulses

For the last part of this introduction, you will now send both the RF pulse and the IQ pulse simultaneously. You will define a new experiment as follows:

# create new experiment
exp2 = Experiment(
uid="simple_q0",
signals=[
ExperimentSignal("flux"),
ExperimentSignal("drive"),
]
)

with exp2.acquire_loop_rt(
uid="shots",
count=1,
):
with exp2.section(uid="bias"):
exp2.play(signal="flux",  pulse=my_first_pulse)
with exp2.section(uid="excitation"):
exp2.play(signal="drive", pulse=my_first_pulse)

Then map the two new signals to the previously used logical signal lines and apply the map to the new experiment:

# map signals appropriately
map_signals = {
"flux"  :  lsg["flux_line"],
"drive" :  lsg["drive_line"],
}

exp2.set_signal_map(map_signals)

The experiment will run on the same device, so what is left is to use the already defined session to run it.

# run the experiment
session.run(exp2)

Now you are ready to explore all the features LabOne Q has to offer!