Download

Download this file as Jupyter notebook: hdf5_file_interface.ipynb.

Interfacing with HDF5 files

After executing a program, its results can be exported from the database to a portable HDF5 file with the to_hdf5() method, as shown in Running a program on hardware. In this tutorial, we go over of the structure of these files.

[2]:
import h5py
import keysight.qcs as qcs
import numpy as np

file = h5py.File("swept_program.hdf5")

Accessing the program and data through QCS

The program that was executed can be loaded from the file by calling load().

[3]:
program = qcs.load("swept_program.hdf5")

We can inspect the program that was executed.

[4]:
program.draw()
keysight-logo-svg
Program
Program
Duration 89.375 ns
Layers 1
Targets 8
Repetitions Sweep with 11 repetitions
Associations
rf Array(name=freq_vals, shape=(11, 4), dtype=float, unit=none, value=[[3.85 GHz, 3.9 GHz, 3.95 GHz, 4 GHz], [3.89 GHz, 3.94 GHz, 3.99 GHz, 4.04 GHz], [3.93 GHz, 3.98 GHz, 4.03 GHz, 4.08 GHz], [3.97 GHz, 4.02 GHz, 4.07 GHz, 4.12 GHz], [4.01 GHz, 4.06 GHz, 4.11 GHz, 4.16 GHz], [4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz], [4.09 GHz, 4.14 GHz, 4.19 GHz, 4.24 GHz], [4.13 GHz, 4.18 GHz, 4.23 GHz, 4.28 GHz], [4.17 GHz, 4.22 GHz, 4.27 GHz, 4.32 GHz], [4.21 GHz, 4.26 GHz, 4.31 GHz, 4.36 GHz], ... ])
Repeat with 10 repetitions
Layer #0
Layer #0
Duration 89.375 ns
awgs 0
RFWaveform on ('awgs', 0)

Parameters
Duration 80 ns
Amplitude Scalar(name=amps, value=0.2, dtype=float, unit=none)
Frequency Array(name=rf, shape=(4,), dtype=float, unit=Hz, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz])
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Delay on ('awgs', 0)

Parameters
Duration 9.375 ns
1
RFWaveform on ('awgs', 1)

Parameters
Duration 80 ns
Amplitude Scalar(name=amps, value=0.2, dtype=float, unit=none)
Frequency Array(name=rf, shape=(4,), dtype=float, unit=Hz, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz])
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Delay on ('awgs', 1)

Parameters
Duration 9.375 ns
2
RFWaveform on ('awgs', 2)

Parameters
Duration 80 ns
Amplitude Scalar(name=amps, value=0.2, dtype=float, unit=none)
Frequency Array(name=rf, shape=(4,), dtype=float, unit=Hz, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz])
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Delay on ('awgs', 2)

Parameters
Duration 9.375 ns
3
RFWaveform on ('awgs', 3)

Parameters
Duration 80 ns
Amplitude Scalar(name=amps, value=0.2, dtype=float, unit=none)
Frequency Array(name=rf, shape=(4,), dtype=float, unit=Hz, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz])
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Delay on ('awgs', 3)

Parameters
Duration 9.375 ns
digs 0
Delay on ('digs', 0)

Parameters
Duration 9.375 ns
Acquisition on ('digs', 0)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency Array(name=int_rf, shape=(4,), dtype=float, unit=Hz, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz])
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Classifier Classifier(Array(name=ref_pts, shape=(4, 2), dtype=complex, unit=none))
1
Delay on ('digs', 1)

Parameters
Duration 9.375 ns
Acquisition on ('digs', 1)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency Array(name=int_rf, shape=(4,), dtype=float, unit=Hz, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz])
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Classifier Classifier(Array(name=ref_pts, shape=(4, 2), dtype=complex, unit=none))
2
Delay on ('digs', 2)

Parameters
Duration 9.375 ns
Acquisition on ('digs', 2)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency Array(name=int_rf, shape=(4,), dtype=float, unit=Hz, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz])
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Classifier Classifier(Array(name=ref_pts, shape=(4, 2), dtype=complex, unit=none))
3
Delay on ('digs', 3)

Parameters
Duration 9.375 ns
Acquisition on ('digs', 3)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency Array(name=int_rf, shape=(4,), dtype=float, unit=Hz, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz])
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Classifier Classifier(Array(name=ref_pts, shape=(4, 2), dtype=complex, unit=none))

We can load the data. This will load from the hdf5 file, rather than the database.

[5]:
program.get_trace()
[5]:
(((Channels(labels=[0], name=digs, absolute_phase=True)))) ... (((Channels(labels=[3], name=digs, absolute_phase=True))))
(rf, [3.85 GHz, 3.9 GHz, 3.95 GHz, ... ]) ... (rf, [4.25 GHz, 4.3 GHz, 4.35 GHz, ... ])
0 1 2 3 4 5 6 7 8 9 ... 0 1 2 3 4 5 6 7 8 9
0 0.007910 0.007690 0.006812 0.008130 0.005054 0.005933 0.007471 0.008130 0.008789 0.007471 ... 0.007031 0.005933 0.005273 0.003076 0.003955 0.007471 0.005054 0.005933 0.006372 0.005273
1 0.007690 0.007251 0.007251 0.008350 0.008350 0.007910 0.007031 0.009009 0.006592 0.007690 ... 0.006372 0.005713 0.005273 0.004175 0.006812 0.005273 0.007471 0.007251 0.004175 0.005713
2 0.009229 0.005933 0.006812 0.005273 0.007690 0.008350 0.006812 0.007690 0.006152 0.007471 ... 0.003076 0.006152 0.005493 0.005054 0.005273 0.007251 0.004395 0.004395 0.005054 0.003296
3 0.007031 0.009668 0.010107 0.006592 0.006592 0.009668 0.008130 0.006812 0.009888 0.010547 ... 0.007251 0.004834 0.005933 0.005273 0.004834 0.005273 0.005713 0.005933 0.005273 0.005493
4 0.008350 0.006592 0.005713 0.010767 0.007910 0.007251 0.007251 0.009668 0.007471 0.007031 ... 0.004614 0.006812 0.005713 0.008130 0.007251 0.004834 0.007031 0.006812 0.005713 0.005933
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
379 0.005493 0.001099 0.002417 0.005273 0.001538 0.004175 0.005713 0.002637 0.004614 0.002417 ... 0.008130 0.003955 0.005273 0.007031 0.008350 0.008789 0.008789 0.007031 0.008350 0.008789
380 0.000879 0.001538 0.001318 0.002856 0.003076 0.004834 0.003516 0.003296 0.003516 0.004395 ... 0.006592 0.007251 0.005713 0.007471 0.007910 0.005273 0.004614 0.007690 0.003735 0.005713
381 0.004395 0.002637 0.001099 0.003516 0.002856 0.003735 0.002856 0.002197 0.004614 0.002637 ... 0.009229 0.005713 0.007251 0.007690 0.006812 0.005713 0.006812 0.005933 0.006152 0.008130
382 0.005713 0.003516 0.000659 0.003296 0.004175 0.001538 0.004175 0.001758 0.000000 0.003296 ... 0.008789 0.007690 0.006812 0.006592 0.005713 0.005273 0.006152 0.009448 0.005493 0.006592
383 0.004614 0.002417 0.002637 0.001099 0.004614 0.005933 0.004395 0.003296 0.003296 0.001538 ... 0.007031 0.008569 0.006812 0.006592 0.005933 0.007690 0.005493 0.004834 0.005933 0.005273

384 rows × 440 columns

Inspecting the file directly

We can also use the hdf5 python library to inspect the file directly. The attributes of the HDF5 are accessed with the attrs property.

[6]:
list(file.attrs.keys())
[6]:
['Program', 'ChannelMapper', 'FPGAPostprocessing', 'Shape', 'Version']

The FPGAPostprocessing attribute specifies whether these results are obtained with (True) or without (False) hardware demodulation. The file contains either IQ or trace data, respectively. This file contains trace data.

[7]:
file.attrs["FPGAPostprocessing"]
[7]:
False

The Shape attribute describes how the program was repeated during execution and is similar to the shape of a program’s repetitions. As we flatten the data, this value describes how the data should be reshaped. For trace data the innermost value is set to \(-1\) to account for different numbers of samples, following the the convention of, e.g., numpy.reshape().

[8]:
file.attrs["Shape"]
[8]:
array([[11, 10, -1]], dtype=int32)

The above value indicates that the trace data should be interpreted as an array of shape \((11, 10, n / (11 * 10))\), where \(n\) is the total number of data points.

Finally, the Program and the ChannelMapper used during its execution are serialized and stored in the file.

Datasets on the file

The file structure is different dependent on whether the program contains IQ or trace data.

Trace data

An HDF5 file containing trace data will store each repeated acquisition in its own dataset. The datasets are stored in groups named DutChannel_{channel_number}_Acquisition_{acquisition_number} that are keys of the file, each of which corresponds to a physical channel.

[9]:
list(file.keys())
[9]:
['DutChannel_20_Acquisition_0']

This file contains a single group for the acquisitions on DutChannel_20. The group has a dataset with no additional attributes named trace.

[10]:
acquisition = file["DutChannel_20_Acquisition_0"]["trace"]
acquisition.shape
[10]:
(42240,)

We then reshape to and display the innermost value, the number of samples per repetition.

[11]:
np.reshape(acquisition, file.attrs["Shape"][0]).shape[-1]
[11]:
384

IQ data

We now load a file that contains results from the same program run with hardware demodulation.

[12]:
file_demod = h5py.File("swept_program_demod.hdf5")
file_demod.attrs["FPGAPostprocessing"]
[12]:
True

Instead of using physical channels as keys, the IQ data uses virtual channels to label the groups as Channel_{channel_name}_{channel_label}_Acquisition_{acquisition_number}.

[13]:
list(file_demod.keys())
[13]:
['Channel_digs_0_Acquisition_0',
 'Channel_digs_1_Acquisition_0',
 'Channel_digs_2_Acquisition_0',
 'Channel_digs_3_Acquisition_0']

The IQ data is separated into real and imaginary parts.

[14]:
acquisition_demod = file_demod["Channel_digs_0_Acquisition_0"]
print(acquisition_demod["iq_real"])
print(acquisition_demod["iq_imaginary"])
<HDF5 dataset "iq_real": shape (110,), type "<f8">
<HDF5 dataset "iq_imaginary": shape (110,), type "<f8">

The shape of the IQ data is exactly the program’s repetitions’ shape.

[15]:
np.reshape(acquisition_demod["iq_real"], file_demod.attrs["Shape"][0]).shape
[15]:
(11, 10, 1)

Download

Download this file as Jupyter notebook: hdf5_file_interface.ipynb.

On this page