56
Striptease Documentation Release 0.1.0 LSPE/STRIP team May 13, 2022

Striptease Documentation

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Striptease Documentation

Striptease DocumentationRelease 0.1.0

LSPE/STRIP team

May 13, 2022

Page 2: Striptease Documentation
Page 3: Striptease Documentation

Contents:

1 Installation 11.1 Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Installing the code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.3 C++ GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.4 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

2 Tutorial 52.1 Establising a connection with the server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.2 Sending commands to the instrument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.3 How data are saved and accessed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.4 Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.5 Running complex automatic scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

3 Calibration of housekeeping parameters 93.1 Usage of calibration tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.2 Low-level calibration functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

4 Polarimeters 134.1 Boards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134.2 Polarimeter names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144.3 Amplifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164.4 Phase switches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

5 Reading data saved in HDF5 files 195.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195.2 Accessing one file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205.3 Handling multiple HDF5 files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

6 Writing test procedures 276.1 Pretty-printing test procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296.2 Module contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

7 Run log database 31

8 Accessing unit test data 338.1 Single-component tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368.2 Polarimetric tests (timelines) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

i

Page 4: Striptease Documentation

8.3 Other information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378.4 How caching works . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

9 Developer’s manual 419.1 Graphical User Interfaces (GUIs) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

10 Indices and tables 47

Python Module Index 49

Index 51

ii

Page 5: Striptease Documentation

CHAPTER 1

Installation

1.1 Requirements

To install Striptease, you need the following tools:

• Python 3.6 or later (required)

• A C++ compiler (optional, see below)

1.2 Installing the code

To build the program, you must have the command git installed and working on your computer.

1.2.1 Creating a new virtual environment

You are advised to create a new virtual environment for your work on Striptease; this usually requires the followingsteps:

python -m venv my_virtual_environment?ACTIVATE?

where ?ACTIVATE? is some command that depends on the platform (Windows, Mac OS X, Linux) and shell (Bash,Zsh, Fish. . . ) that you are using. For instance, if you are using Bash/Zsh under Mac OS X or Linux, you must runthese commands:

python -m venv my_virtual_environmentsource my_virtual_environment/bin/activate

If you are running the Command Prompt under Windows, the commands are the following:

python -m venv my_virtual_environmentmy_virtual_environment\scripts\activate.bat

1

Page 6: Striptease Documentation

Striptease Documentation, Release 0.1.0

Refer to the Python documentation for more details.

1.2.2 Downloading Striptease

To install Striptease and its dependencies, use the following commands:

git clone [email protected]:lspestrip/striptease.gitcd stripteasepip install --user -r requirements.txt

Be sure to run your source codes and Jupyter notebooks within the striptease directory, otherwise calibrationtables and other important data files won’t be visible.

However, if you need to access the instrument in real-time, you must first set up the authentication system. To learnhow to do it, continue reading, otherwise you can stop here.

1.3 C++ GUI

Striptease included a C++ program used to send long sequence of commands to the Strip electronics. This program isno longer supported, as the script program_batch_runner.py is now the officially-supported way of running automaticscripts.

If you are curious and want to compile it, you must have a C++ compiler and the Qt libraries installed. Run thefollowing commands within the striptease directory:

cd TestRunnerqmake TestRunner.pro && make

1.4 Authentication

To use the online tools provided by Striptease, you must have an account for the STRIP web portal. (This is notnecessary if you just plan to load data from HDF5 files.) Once you are registered, you must use your username andpassword to authenticate to the portal.

Striptease provides a number of shortcuts to provide your own credentials to the web portal. Here are the possibilities:

1. Save your credentials in a text file, which is read any time Stripeline needs authentication. This requires somesetup, but it’s the best method.

2. Enter username and password every time. This is boring, but it’s the most secure way to authenticate.

3. Use environment variables. This is unsecure, but quick to do.

1.4.1 Using credentials from a text file

We discuss this method first, because it is the most useful and provides a secure yet permanent way to connect to theserver without re-entering your credentials again and again.

You must save your credentials in a JSON file named conf.json and place it in the directory ~/.strip. Here isan example:

2 Chapter 1. Installation

Page 7: Striptease Documentation

Striptease Documentation, Release 0.1.0

{"user": "foo","password": "bar"

}

You can create this file from the command line, using the following commands (on Mac OS X and Linux):

$ mkdir -p ~/.strip && cat <<EOF > ~/.strip/conf.json{

"user": "foo","password": "bar"

}EOF

In order to secure your credentials, you should change the permission of the directory .strip:

$ chmod -R go-rwx ~/.strip

1.4.2 Using environment variables

A quick method to save your username and password is to use environment variables. Striptease recognizes thevariables STRIP_USER and STRIP_PASSWORD, which you can set using the following commands from terminal:

$ export STRIP_USER=foo$ export STRIP_PASSWORD=bar

These variables will be lost once you close the terminal window. You can make them permanent by adding the twoexport commands to your ~/.profile file, but you should instead prefer to save them in conf.json (seeabove), as environment variables can be easily tracked by other malicious users on your system.

1.4. Authentication 3

Page 8: Striptease Documentation

Striptease Documentation, Release 0.1.0

4 Chapter 1. Installation

Page 9: Striptease Documentation

CHAPTER 2

Tutorial

In this tutorial, we will show how to connect to the Strip webserver and run simple commands that drive the electronicsand the instrument.

(Before reading this part, be sure to follow the instructions in the page Authentication, otherwise the commands in thispage will not work. You need to be authenticated if you are going to send commands to the real electronics.)

2.1 Establising a connection with the server

The Python interface provided by Striptease revolves around the object striptease.StripConnection, whichderives from web.rest.base.Connection. It handles authentication, measure unit conversion, and validation ofcommands. If you properly configured the credentials in file ~/.strip/conf.json (see the page Authentication),you can create a new object with no parameters:

import striptease as stconn = st.StripConnection()

These commands create a connection but do not authenticate, as shown by the web.rest.base.Connection.is_connected():

>>> conn.is_connected()False>>> conn.login()>>> conn.is_connected()True

To disconnect, you must call the method web.rest.base.StripConnection.logout():

>>> conn.logout()>>> conn.is_connected()False>>> conn.login() # Log back in

5

Page 10: Striptease Documentation

Striptease Documentation, Release 0.1.0

Using the login and logout commands is handy when running a REPL; if you are writing automatic script, it’s betterto rely to the fact that striptease.StripConnection can be used as a context manager:

with st.StripConnection() as conn:# Use "conn" freely here, as the login has already been donepass

2.2 Sending commands to the instrument

Once you have a working connection to the server, you can use the method striptease.StripConnection.slo_command() to send «slow» commands to the server. This kind of commands is the most common, and you aregoing to use it 95% of the time. The following command retrieves the drain voltage for HEMT #0 from detector R0:

vd0_hk = conn.slo_command(method="GET",board="R",pol=0,kind="BIAS",base_addr="VD0_HK",

)

The example above retrieved a value from the electronic board and saved it into vd0_hk. Changing the method fromGET to SET permits to set new values in the board; in this case, we must change the name of the parameter as well,because of the way the Strip electronics works:

conn.slo_command(method="SET",board="R",pol=0,kind="BIAS",base_addr="VD0_SET",data=vd0_hk,

)

The class StripConnection has more high-level methods that rely on slo_command(): refer to its documen-tation for more details.

2.3 How data are saved and accessed

The web server acquiring data from the instrument distinguishes between «fast» and «slow» timestreams; the scientificoutput of each polarimeter belongs to the first category, while housekeeping parameters like currents, biases, andtemperatures are slow timestreams. This distinction is kept in Striptease, as it acts as a middleware library betweenthe user and the web server.

Scientific and housekeeping data are saved by the web server in HDF5 files; no more than 6 hours of data are savedin a file, for the fear of data loss. When a HDF5 file is closed, the web server immediately creates a new one andcontinues saving data in it. It is possible to force the web server to close the current HDF5 file and to create a new one;this can be useful before running a long test script, for instance:

conn.round_all_files()

6 Chapter 2. Tutorial

Page 11: Striptease Documentation

Striptease Documentation, Release 0.1.0

2.4 Tags

In long tests, it is often useful to mark some events happening: for instance, a time span where the state of theinstrument is considered «stable», or the moment when a polarimeter is being turned off.

The web server implement «tags» to mark specific events during a test. A tag is defined by a start and an end, and itmust therefore be opened and closed:

conn.tag_start("MY_TAG", comment="My comment")

# Do whatever you want, send your commands, etc.

conn.tag_stop("MY_TAG", comment="Another comment")

To ease the use of tags, Striptease implements the striptease.StripTag class, which acts as a context manager:

with StripTag(conn, name="MY_TAG",start_comment="Start", stop_comment="End"):

# Do whatever you want, send your commands, etc.pass

The main purpose of tags is to ease the implementation of analysis scripts. You can query tags using the methodstriptease.StripConnection.tag_query():

from astropy.time import Time

tags = conn.tag_query("MY_TAG",start_mjd=Time("2019-12-20").mjd,end_mjd=Time("2019-12-30").mjd)

for cur_tag in tags:# "cur_tag" is a dictionaryprint(cur_tag)

2.5 Running complex automatic scripts

To run a test on the hardware, one needs to carefully plan the sequence of commands to be sent before the real hardwarebe exercised. Once the list of commands is prepared, they can be sent to the electronics and be ran automatically, oneby one. Therefore, to run a test, Striptease require users to follow a two-step process:

1. A Python script builds the sequence of commands (the so-called test procedure) and save it into a file (writtenin the JSON format;

2. The user runs the sequence of commands using a command-line tool. Striptease provides the fileprogram_batch_runner.py for this purpose.

This approach ensures reproducibility and allows the user to inspect the test procedure (in the JSON file) beforeactually running it.

The JSON file saved by Python scripts contains an ordered list of dictionaries, each providing the parameters for asingle command sent to the electronics.

2.4. Tags 7

Page 12: Striptease Documentation

Striptease Documentation, Release 0.1.0

2.5.1 How to create test procedures

A test procedure is a sequence of commands written in a JSON file that can be used to automatically run a test of theStrip hardware. See the chapter Writing test procedures.

8 Chapter 2. Tutorial

Page 13: Striptease Documentation

CHAPTER 3

Calibration of housekeeping parameters

One of the duty of the electronic boards used by Strip is to measure instrument parameters like currents, voltages,temperatures, etc. These parameters are called housekeepings and are vital to monitor the status of the instrument.Like scientific samples, housekeeping parameters are measured by digital units and are stored as integer numbers,nicknamed ADU (analog-to-digital unit). Calibration curves must be applied to ADU numbers to retrieve a physi-cally meaningful unit (e.g., milliampere, Kelvin, etc.). These calibration curves are provided in the form of Excelspreadsheets, and striptease provides a few utilities to ease the conversion between ADUs and physical units.

3.1 Usage of calibration tables

The most basic functions to apply calibration tables are provided by the class CalibrationTables. This classimplements two methods, named physical_units_to_adu() and adu_to_physical_units(), whichperform the two conversions:

from calibration import CalibrationTables

cal = CalibrationTables()print(cal.physical_units_to_adu(

polarimeter="R0",hk="idrain",component="HA1",value=1000,

))

A CalibrationTable object connects with the server when the constructor is called, because it needs to knowwhich is the association between the board name (in the example above, R) and the board number used by the elec-tronics. Once the object cal has been constructed, the connection is no longer used. If you have already instanced aConfig object, you can pass it to the constructor and it will be reused:

from calibration import CalibrationTablesfrom config import Configfrom striptease import StripConnection

(continues on next page)

9

Page 14: Striptease Documentation

Striptease Documentation, Release 0.1.0

(continued from previous page)

conn = StripConnection()conn.login()

config = Config()config.load(conn)

# Use "conn" and "config"# ...

# Reuse the configuration objectcal = CalibrationTables(config)

class calibration.CalibrationTables(config=None)Calibration tables used to convert HK ADUs into physical units and back

This class loads the calibration tables for housekeeping parameters from a set of Excel files.

You can pass a striptease.Config class to this class. Not doing so is equivalent to the following code:

from calibration import CalibrationTablesfrom config import Configfrom striptease import StripConnection

with StripConnection() as conn:conf = Config()conf.load(conn)

cal_tables = CalibrationTables(conf)

adu_to_physical_units(polarimeter, hk, component, value)Convert ADUs into physical units for an housekeeping parameter.

The meaning of the parameters polarimeter, hk, and component is the same as inget_calibration_curve().

get_calibration_curve(polarimeter, hk, component)Return a CalibrationCurve object for an housekeeping parameter

Parameters

• polarimeter (str) – the name of the polarimeter, e.g., I4 or W3

• hk (str) – one of the following strings:

– vdrain: drain voltage

– idrain: drain current

– vgate: gate voltage

– vphsw: voltage pin for a phase switch

– iphsw: current pin for a phase switch

• component (str) – name of the component within the polarimeter. For LNAs, you canuse whatever string works with striptease.get_lna_num(). For phase switches,you must pass an integer number in the range 0. . . 3.

Returns A CalibrationCurve object.

10 Chapter 3. Calibration of housekeeping parameters

Page 15: Striptease Documentation

Striptease Documentation, Release 0.1.0

physical_units_to_adu(polarimeter, hk, component, value)Convert physical units into ADUs for an housekeeping parameter.

The meaning of the parameters polarimeter, hk, and component is the same as inget_calibration_curve().

3.2 Low-level calibration functions

A calibration curve is stored in a CalibrationCurve object, which has the following fields:

1. slope (float)

2. intercept (float)

3. mul (int)

4. div (int)

5. add (int)

The operation carried out by the electronics to convert physical units to ADU is the following (integer operations):

adu = value * mul / div + add

However, Striptease uses the following floating-point operation:

adu = int(value * slope + intercept)

These operations are implemented by the functions calibration.physical_units_to_adu() andcalibration.adu_to_physical_units(), which require a CalibrationCurve object.

class calibration.CalibrationCurve(slope, intercept, mul, div, add)Linear calibration curve (from physical units to ADU) for an housekeeping parameter.

slopeFloating-point value for the slope of the calibration curve

interceptFloating-point value for the additive offset of the calibration curve

mulInteger numerator of the slope of the calibration curve

divInteger denominator of the slope of the calibration curve

addInteger value for the additive offset of the calibration curve

3.2. Low-level calibration functions 11

Page 16: Striptease Documentation

Striptease Documentation, Release 0.1.0

12 Chapter 3. Calibration of housekeeping parameters

Page 17: Striptease Documentation

CHAPTER 4

Polarimeters

This section explains some general-purpose functions and constants that are useful to manipulate polarimeters.

4.1 Boards

If you need to iterate over all the board names, or if you want to check if the board name provided as input by the useris valid, you can use STRIP_BOARD_NAMES. The following example mocks a processing script that needs to iterateover all the boards but one; it shows how STRIP_BOARD_NAME can make the code more elegant:

from striptease import STRIP_BOARD_NAMES

while True:board_to_skip = input("Enter the board to skip: ")if board_to_skip in STRIP_BOARD_NAMES:

breakelse:

print(f"'{board_to_skip}' is not a valid board, retry")

for cur_board_name in STRIP_BOARD_NAMES:if cur_board_name == board_to_skip:

continue

print(f"Processing board {cur_board_name}")# ...

The output of the code is the following, assuming that the user entered R at the prompt:

Enter the board to skip: RProcessing board VProcessing board GProcessing board BProcessing board Y

(continues on next page)

13

Page 18: Striptease Documentation

Striptease Documentation, Release 0.1.0

(continued from previous page)

Processing board OProcessing board I

4.2 Polarimeter names

4.2.1 Switching between modules and polarimeters

Each «module» (e.g., I0) is associated with a «polarimeter» (STRIP34), and it is often the case that procedures anddata analysis codes need to jump from one representation to another.

The association is stored in the file data/default_biases_warm.xlsx, and it can be accessed using the classInstrumentBiases:

from striptease.biases import InstrumentBiases

biases = InstrumentBiases()print(biases.module_name_to_polarimeter("W3"))# Output:# STRIP70

print(biases.polarimeter_to_module_name("STRIP34"))# Output:# I0

4.2.2 W-band polarimeters

The convention used to name the Strip polarimeters is rather unfortunate, as it follows a different convention for Q andW-band polarimeters. While the former are indicated by a letter identifying the board and a positive number indicatingthe polarimeter index in the board, W-band polarimeters always use the letter W, regardless of the board.

The constant BOARD_TO_W_BAND_POL is a dictionary that associates the uppercase letter of each board to the nameof the W-band polarimeter, or None in the case of board I:

from striptease import STRIP_BOARD_NAMES, BOARD_TO_W_BAND_POL

for cur_board in STRIP_BOARD_NAMES:w_pol = BOARD_TO_W_BAND_POL[cur_board]

if w_pol:print(f"{w_pol} is connected to board {cur_board}")

else:print(f"Board {cur_board} has no W-band polarimeters")

The output of the script is the following:

W3 is connected to board RW4 is connected to board VW6 is connected to board GW5 is connected to board BW1 is connected to board YW2 is connected to board OBoard I has no W-band polarimeters

14 Chapter 4. Polarimeters

Page 19: Striptease Documentation

Striptease Documentation, Release 0.1.0

In some contexts, a saner naming convention is used for W-band polarimeters: they are referred using the name of theboard they belong to, followed by the index 7. For instance, the name R7 can be used to refer to polarimeter W3, asthis polarimeter is connected to board R. The function normalize_polarimeter_name() can be used to turnthe name W3 into the R-based name:

print(normalize_polarimeter_name("W1")) # Prints Y7

4.2.3 Polarimeter index within a board

The function get_polarimeter_index() returns a zero-based index of a polarimeter in a board, given the nameof the polarimeter. W-band polarimeters always use index 7:

print(get_polarimeter_index("R0")) # Print 0print(get_polarimeter_index("G4")) # Print 4print(get_polarimeter_index("W1")) # Print 7

4.2.4 Iterating over polarimeters

It is often the case that some code needs to iterate over all the polarimeters in a board. The functionpolarimeter_iterator() can be used in for loops to efficiently scan the list of polarimeters for a board:

for board_name, pol_idx, pol_name in polarimeter_iterator():print(f"Board '{board_name}': polarimeter '{pol_name}' (#{pol_idx})")

This is the output:

Board 'R': polarimeter 'R0' (#0)Board 'R': polarimeter 'R1' (#1)Board 'R': polarimeter 'R2' (#2)Board 'R': polarimeter 'R3' (#3)Board 'R': polarimeter 'R4' (#4)Board 'R': polarimeter 'R5' (#5)Board 'R': polarimeter 'R6' (#6)Board 'R': polarimeter 'W3' (#7)Board 'V': polarimeter 'V0' (#0)Board 'V': polarimeter 'V1' (#1)Board 'V': polarimeter 'V2' (#2)Board 'V': polarimeter 'V3' (#3)Board 'V': polarimeter 'V4' (#4)Board 'V': polarimeter 'V5' (#5)Board 'V': polarimeter 'V6' (#6)Board 'V': polarimeter 'W4' (#7)Board 'G': polarimeter 'G0' (#0)Board 'G': polarimeter 'G1' (#1)Board 'G': polarimeter 'G2' (#2)Board 'G': polarimeter 'G3' (#3)Board 'G': polarimeter 'G4' (#4)Board 'G': polarimeter 'G5' (#5)Board 'G': polarimeter 'G6' (#6)Board 'G': polarimeter 'W6' (#7)Board 'B': polarimeter 'B0' (#0)Board 'B': polarimeter 'B1' (#1)Board 'B': polarimeter 'B2' (#2)Board 'B': polarimeter 'B3' (#3)

(continues on next page)

4.2. Polarimeter names 15

Page 20: Striptease Documentation

Striptease Documentation, Release 0.1.0

(continued from previous page)

Board 'B': polarimeter 'B4' (#4)Board 'B': polarimeter 'B5' (#5)Board 'B': polarimeter 'B6' (#6)Board 'B': polarimeter 'W5' (#7)Board 'Y': polarimeter 'Y0' (#0)Board 'Y': polarimeter 'Y1' (#1)Board 'Y': polarimeter 'Y2' (#2)Board 'Y': polarimeter 'Y3' (#3)Board 'Y': polarimeter 'Y4' (#4)Board 'Y': polarimeter 'Y5' (#5)Board 'Y': polarimeter 'Y6' (#6)Board 'Y': polarimeter 'W1' (#7)Board 'O': polarimeter 'O0' (#0)Board 'O': polarimeter 'O1' (#1)Board 'O': polarimeter 'O2' (#2)Board 'O': polarimeter 'O3' (#3)Board 'O': polarimeter 'O4' (#4)Board 'O': polarimeter 'O5' (#5)Board 'O': polarimeter 'O6' (#6)Board 'O': polarimeter 'W2' (#7)Board 'I': polarimeter 'I0' (#0)Board 'I': polarimeter 'I1' (#1)Board 'I': polarimeter 'I2' (#2)Board 'I': polarimeter 'I3' (#3)Board 'I': polarimeter 'I4' (#4)Board 'I': polarimeter 'I5' (#5)Board 'I': polarimeter 'I6' (#6)

Valid keywords for polarimeter_iterator() are boards and include_q_band/include_w_band, which permitto pick which boards to scan and to include/exclude Q and W-band polarimeters:

for _, pol_idx, pol_name in polarimeter_iterator(boards=["V"], include_w_band=False):print(f"Polarimeter {pol_name} (#{pol_idx})")

In this case, the output is the following:

Polarimeter V0 (#0)Polarimeter V1 (#1)Polarimeter V2 (#2)Polarimeter V3 (#3)Polarimeter V4 (#4)Polarimeter V5 (#5)Polarimeter V6 (#6)

You can see that the W-band polarimeter of board V (W4) was not included.

4.3 Amplifiers

4.3.1 Indexing

The official naming convention enumerates the amplifiers according to the line exiting the phase switch (A or B) andtheir order within the line (1, 2, 3). Therefore, amplifier HA1 is the first stage of amplification in line A, and it’sfollowed by HA2, which is in turn followed by HA3 (the last amplification stage).

However, there have been two other ways to enumerate the LNAs in a QUIET/Strip polarimeter:

16 Chapter 4. Polarimeters

Page 21: Striptease Documentation

Striptease Documentation, Release 0.1.0

1. The indexes used by JPL for the QUIET experiment (e.g., Q5);

2. The indexes used when designing the electronic boards (e.g., H4).

The web server used to operate Strip in Bologna and in Tenerife uses the indexes understood by the electronic board,which neglects the official naming convention. The function get_lna_num() computes the zero-based index of anLNA from its full name in one of the three conventions named above:

# All these examples refer to the same LNAprint(get_lna_num("HA3")) # Print 4print(get_lna_num("H4")) # Print 4print(get_lna_num("Q5")) # Print 4

4.3.2 Modes of operation

A Strip amplifier can operate either in open loop or closed loop mode, depending on how the gate and drain are set:

1. In open loop mode, the drain voltage 𝑉𝑑 and gate voltage 𝑉𝑔 are set by the user, while the drain current 𝐼𝑑 is setfree to adapt to the voltages.

2. In closed loop mode, the drain current 𝐼𝑑 and drain voltage 𝑉𝑑 are set by the user, and a retro-feedback circuit inthe electronics adapts the gate voltage 𝑉𝑔 to make sure that the drain current $I_d$ is kept at the level specifiedby the user.

The following figure depicts the difference between the two modes, representing the three bias parameters 𝐼𝑑, 𝑉𝑑,and 𝑉𝑔 as knobs; only green knobs can be manipulated by the user, while the blue knob responds automatically tovariations in the green knobs.

The way an amplifier operates can be set using the POL_MODE command, through the method StripConnection.set_pol_mode(). You can use the enumeration class PolMode to specify the flags to be used, but usually youcan stick to the two constants OPEN_LOOP_MODE and CLOSED_LOOP_MODE:

import striptease as st

conn = st.StripConnection()conn.set_pol_mode("I0", OPEN_LOOP_MODE)

4.4 Phase switches

Each polarimeter in Strip implements two phase switches, which are devices able to inject a phase shift in the signalby making it pass through a longer path to induce a phase shift in the wave. They are called «switches» because theyare usually employed to turn this shift on and off at some fixed rate (from several Hz up to kHz).

Each phase switch in the Strip polarimeters can either switch at 4 kHz (fast switching) or at 50 Hz (slow switching). Itimplements two paths, whose length in terms of the wavelength 𝜆 differs by 𝜆/2, so that it effectively induces a 180°phase shift whenever the signal goes through the longer path. Each path can be activated or deactivated by controllinga «pin diode»: if the diode is forward-biased, it lets the radiation pass through its path, but if it is reverse-biased,radiation is blocked. Thus, to make a phase switch induce an alternating phase shift of 0°/180°, it must switch backand forth between a «forward/reverse» and a «reverse/forward» configuration, so that the signal can either pass throughthe first or the second path, but never through both of them (which would cause destructive interference when the twopaths join).

The default configuration of the phase switches is the following:

4.4. Phase switches 17

Page 22: Striptease Documentation

Striptease Documentation, Release 0.1.0

• The first phase switch (leg A) oscillates at 4 kHz;

• The second phase switch (leg B) oscillates at 50 Hz.

There is no need to send sequences of commands to make each pin diode switch, as the electronics is smart enough tounderstand how to do so. You can either tell the electronics to switch or to stay fixed at some position.

The values to be sent to the pin diodes are listed in the enumeration class PhswPinMode; they can be sent tothe diodes using the method StripConnection.set_phsw_status(). Of particular interest is the constantNOMINAL_SWITCHING, which assumes a different meaning depending on the pin diode you are programming: itmakes sure that the default configuration for each of the four pins is set, i.e., switching at 4 kHz for leg A and at 50 Hzfor leg B.

Here is an example where we set the state of the four pin diodes for polarimeter I0 to the nominal state:

conn = StripConnection()for pin_idx in range(4):

conn.set_phsw_status(polarimeter="I0",phsw_index=pin_idx,status=PhswPinMode.NOMINAL_SWITCHING,

)

More complex configurations can of course be employed; the following example makes the signal pass through theshorter path while blocking the longer one:

conn = StripConnection()conn.set_phsw_status("I0", 0, PhswPinMode.STILL_SIGNAL)conn.set_phsw_status("I0", 1, PhswPinMode.NO_SIGNAL)conn.set_phsw_status("I0", 2, PhswPinMode.STILL_SIGNAL)conn.set_phsw_status("I0", 3, PhswPinMode.NO_SIGNAL)

18 Chapter 4. Polarimeters

Page 23: Striptease Documentation

CHAPTER 5

Reading data saved in HDF5 files

5.1 Introduction

Striptease provides an interface to the data acquired by the instrument and stored in HDF5 files which allows the userto access both scientific and housekeepings data returning them into time, data numpy arrays.

Before reading this chapter, you should be aware of the way data are saved to disk when LSPE/Strip operates. Theinstrument sends all the scientific (PWR and DEM timelines) and housekeeping (biases, temperatures. . . ) timelines to acomputer, where a software called data server takes them and writes them in HDF5 files. The stream of data is usuallycontinuous, but occasionally the data server stops writing in a file and opens a new one. This ensures that files do notget too big and that you can grab one of them for analysis while the instrument is still running. These files are usuallysaved in a hierarchical structure of directories, grouped according to the year/month of acquisition. So, assuming thatthese data files are stored in /storage/strip, you might find the following files in the Strip data storage:

/storage/strip/2021/102021_10_31_15-42-44.h52021_10_31_19-42-44.h52021_10_31_23-42-44.h5

/storage/strip/2021/112021_11_01_03-42-44.h52021_11_01_07-42-44.h52021_11_01_11-42-44.h5

It is now time to see the kind of tools that Striptease provides to access these files. The interface is based on twoclasses:

• DataFile provides a high-level access to a single HDF5 file.

• DataStorage provides a high-level access to a directory containing several HDF5 files, like the one above.It basically abstracts over the concept of a «file» and instead considers the data as being acquired continuously.

19

Page 24: Striptease Documentation

Striptease Documentation, Release 0.1.0

5.2 Accessing one file

Let’s begin with a small example that shows how to use the class DataFile:

from striptease import DataFilefname = '/path_to/data.h5'

with DataFile(fname) as my_data:# Load a HK time seriestime, data = my_data.load_hk("POL_Y6", "BIAS","VG4A_SET")

# Load some scientific datatime, data = my_data.load_sci("POL_G0", "DEM", "Q1")

The class DataFile assumes that the name of the file follows the convention used by the acquisition software usedin the system-level tests in Bologna and during nominal data acquisition in Tenerife. Be sure not to mess with HDF5file names!

Since the kind of HDF5 file used in Strip has a complex structure, Striptease provides a few facilities to handle them.To load timelines of housekeeping parameter and detector outputs, the DataFile class provides two methods:

• DataFile.load_hk()

• DataFile.load_sci()

Moreover, a method DataFile.get_average_biases() can be used to retrieve the average level of biaseswithin some time frame.

class striptease.DataFile(filepath, filemode=’r’)A HDF5 file containing timelines acquired by Strip

This is basically a high-level wrapper over a h5py.File object. It assumes that the HDF5 file was saved by theacquisition software used in Bologna and Tenerife, and it provides some tools to navigate through the data savedin the file.

Creating a DataFile object does not automatically open the file; this is done to preserve space. The file is lazilyopened once you call one of the methods that need to access file data.

The two methods you are going to use most of the time are:

• load_hk()

• load_sci()

You can access these class fields directly:

• filepath: a Path object containing the full path of the HDF5 file

• datetime: a Python datetime object containing the time when the acquisition started

• mjd_range: a pair of float numbers representing the MJD of the first and last sample in the file.To initialize this field, you must call DataFile.read_file_metadata first.

• computed_mjd_range: a Boolean that is set to True if the field mjd_range was computedthrough a complete scanning of the datasets in the file or read from the file attributes. (In the for-mer case, the user might want to save the MJD range back in the file and have it ready for the future,as the scanning of a HDF5 file can take considerable time.)

• hdf5_groups: a list of str objects containing the names of the groups in the HDF5 file. To initial-ize this field, you must call DataFile.read_file_metadata first.

20 Chapter 5. Reading data saved in HDF5 files

Page 25: Striptease Documentation

Striptease Documentation, Release 0.1.0

• polarimeters: a Python set object containing the names of the polarimeters whose measure-ments have been saved in this file. To initialize this field, you must call DataFile.read_file_metadata first.

• hdf5_file: if the file has been opened using read_file_metadata(), this is the h5py.File ob-ject.

• tags: a list of Tag objects; you must call read_file_metadata() before reading it.

This class can be used in with statements; in this case, it will automatically open and close the file:

with DataFile(myfile) as inpf:# The variable "inpf" is a DataFile object in this context

get_average_biases(polarimeter, time_range=None, calibration_tables=None) →striptease.biases.BiasConfiguration

Return a BiasConfiguration object containing the average values of biases for a polarimeter.

The parameter polarimeter must be a string containing the name of the polarimeter, e.g., Y0.The parameter time_range, if specified, is a 2-element tuple containing the start and end MJDsto consider in the average. If calibration_tables is specified, it must be an instance of theCalibrationTables class.

The return value of this function is a BiasConfiguration object

If calibration_tables is specified, the values returned by this method are calibrated to physicalunits; otherwise, they are expressed in ADUs.

load_hk(group, subgroup, par, verbose=False)Loads scientific data from one detector of a given polarimeter

Parameters

• group (str) – Either BIAS or DAQ

• subgroup (str) – Name of the housekeeping group. It can either be POL_XY orBOARD_X, with X being the letter identifying the module, and Y the polarimeter num-ber within the module. Possible examples are POL_G0 and BOARD_Y.

• par (str) – Name of the housekeeping parameter, e.g. ID4_DIV.

• verbose (bool) – whether to echo the HK being loaded. Default is FALSE

Returns the stream of times (using the astropy.time.Time datatype), and the stream of data.

Return type A tuple containing two NumPy arrays

Example:

from striptease.hdf5files import DataFile

f = DataFile(filename)time, data = f.load_hk("POL_Y6", "BIAS", "VG4A_SET")

load_sci(polarimeter, data_type, detector=[])Loads scientific data from one detector of a given polarimeter

Parameters

• polarimeter (str) – Name of the polarimeter, in the form POL_XY or XY for short,with X being the module letter and Y the polarimeter number within the module.

• data_type (str) – Type of data to load, either DEM or PWR.

5.2. Accessing one file 21

Page 26: Striptease Documentation

Striptease Documentation, Release 0.1.0

• detector (str) – Either Q1, Q2, U1 or U2. You can also pass a list, e.g., ["Q1","Q2"]. If no value is provided for this parameter, all the four detectors will be returned.

Returns the stream of times (using the astropy.time.Time datatype), and the stream of data. Formultiple detectors, the latter will be a list of tuples, where each column is named eitherDEMnn or PWRnn, where nn is the name of the detector.

Return type A tuple containing two NumPy arrays

Examples:

from striptease.hdf5files import DataFileimport numpy as np

f = DataFile(filename)

# Load the output of only one detectortime, data = my_data.load_sci("POL_G0", "DEM", "Q1")print(f"Q1 mean output: {np.mean(data)}")

# Load the output of several detectors at oncetime, data = my_data.load_sci("POL_G0", "DEM", ("Q1", "Q2"))print(f"Q1 mean output: {np.mean(data['DEMQ1'])}")

# Load the output of all the four detectorstime, data = my_data.load_sci("POL_G0", "DEM")print(f"Q1 mean output: {np.mean(data['DEMQ1'])}")

read_file_metadata(force=False)Open the file and retrieve some basic metadata

This function opens the HDF5 file and retrieves the following information:

• List of groups under the root node

• List of boards for whom some data was saved in the file

• List of polarimeters that have some data saved in the file

• List of tags

• MJD of the first and last scientific/housekeeping sample in the file

This function is idempotent, in the sense that calling it twice will not force a re-read of the metadata. Tooverride this behavior, pass force=True: the function will re-open the file and read all the metadataagain.

5.2.1 Accessing the list of tags

Every DataFile object keeps the list of tags in the tags attribute, which is a list of object of type Tag. Here is acode that searches for all the tags containing the string STABLE_ACQUISITION in their name:

from striptease import DataFile

with DataFile("test.h5") as inpf:list_of_tags = [

tfor t in inpf.tagsif "STABLE_ACQUISITION" in t.name

](continues on next page)

22 Chapter 5. Reading data saved in HDF5 files

Page 27: Striptease Documentation

Striptease Documentation, Release 0.1.0

(continued from previous page)

for cur_tag in list_of_tags:print("Found a tag: ", cur_tag.name)

# Possible output:## Found a tag: STABLE_ACQUISITION_R0# Found a tag: STABLE_ACQUISITION_B0# Found a tag: STABLE_ACQUISITION_R1# Found a tag: STABLE_ACQUISITION_B1

class striptease.Tag(id: int, mjd_start: float, mjd_end: float, name: str, start_comment: str,end_comment: str)

5.2.2 Information about housekeeping parameters

As there are hundreds of housekeeping parameters used in Strip HDF5 files, Striptease provides the functionget_hk_descriptions(). You pass the name of a group and a subgroup to it, and it returns a dict-like objectof type HkDescriptionList that associates the name of each housekeeping in the group/subgroup with a textualdescription. The object can be printed using print: it will produce a (long!) table containing all the housekeepingparameters and descriptions in alphabetic order.

class striptease.HkDescriptionList(group, subgroup, hklist)Result of a call to get_hk_descriptions

This class acts like a dictionary that associates the name of an housekeeping parameter with a description. Itprovides a nice textual representation when printed on the screen:

l = get_hk_descriptions("BIAS", "POL")

# Print the description of one parameterif "VG4A_SET" in l:

print(l["VG4A_SET"])

# Print all the descriptions in a nicely-formatted tableprint(l)

striptease.get_hk_descriptions(group, subgroup)Reads the list of housekeeping parameters with their own description.

Parameters

• group (str) – The subgroup. It must either be BIAS or DAQ.

• subgroup (str) – The group to load. It can either be POL_XY or BOARD_X, with Xbeing the module letter, and Y the number of the polarimeter.

Returns A dictionary containing the association between the name of the housekeeping parameterand its description.

Examples:

list = get_hk_descriptions("DAQ", "POL_G0")

5.2. Accessing one file 23

Page 28: Striptease Documentation

Striptease Documentation, Release 0.1.0

5.3 Handling multiple HDF5 files

It is often the case that the data you are looking for spans more than one HDF5 file. In this case, it is a tedious processto read chunks of data from several files and knit them together. Luckly, Striptease provides the DataStorageclass, which implements a database of HDF5 files and provides methods for accessing scientific and housekeepingdata without bothering of which files should be read.

Here is an example:

from striptease import DataStorage

# This call might take some time if you have never used DataStorage# before, as it needs to build an index of all the filesds = DataStorage("/storage/strip")

# Wow! We are reading one whole day of housekeeping data!times, data = ds.load_hk(

mjd_range=(59530.0, 59531.0),group="BIAS",subgroup="POL_R0",par="VG1_HK",

)

Note that the script provides the range of times as a MJD range; the DataStorage object looks in the list of files anddecides which files contain this information and reads them. The return value is the same as for a call to DataFile.load_hk().

For the class DataStorage to work, a database of the HDF5 files in the specified path must be already present. Youcan create one using the command-line script build_hdf5_database.py:

./build_hdf5_database.py /storage/strip

5.3.1 Accessing data in a storage

The DataStorage provides the following methods to access tags, scientific data and housekeeping parameters:

• DataStorage.get_tags() retrieves a list of tags;

• DataStorage.load_sci() retrieves scientific timelines;

• DataStorage.load_hk() retrieves housekeeping timelines.

All these functions accept either a 2-tuple containing the start and end MJD or a Tag object that specifies the timerange.

class striptease.DataStorage(path: Union[str, pathlib.Path], database_name=’index.db’, up-date_database=False, update_hdf5=False)

The storage where HDF5 files are kept

This class builds an index of all the files in a directory containing the HDF5 files saved by the LSPE/Strip dataserver. It can be used to load scientific/housekeeping data without caring of file boundaries.

Example:

from striptease import DataStorage

ds = DataStorage("/database/STRIP/HDF5/")# One whole day of tags!

(continues on next page)

24 Chapter 5. Reading data saved in HDF5 files

Page 29: Striptease Documentation

Striptease Documentation, Release 0.1.0

(continued from previous page)

tags = ds.get_tags(mjd_range=(59530.0, 59531.0))for cur_tag in tags:

print(cur_tag)

files_in_range(mjd_range: Union[Tuple[float, float], Tuple[astropy.time.core.Time,astropy.time.core.Time], Tuple[str, str], striptease.hdf5files.Tag]) →List[striptease.hdf5db.HDF5FileInfo]

Return a list of the files that contain data within the MJD range

get_list_of_files()→ List[striptease.hdf5db.HDF5FileInfo]Return a list of all the files in the storage path

get_tags(mjd_range: Union[Tuple[float, float], Tuple[astropy.time.core.Time, as-tropy.time.core.Time], Tuple[str, str], striptease.hdf5files.Tag]) →List[striptease.hdf5files.Tag]

Return a list of all the tags falling within a MJD range

The function returns a list of all the tags found in the HDF5 files in the storage directory that fall (evenpartially) within the range of MJD specified by mjd_range. The range can either be:

1. A pair of floating-point values, each representing a MJD date;

2. A pair of strings, each representing a date (e.g., 2021-12-10 10:39:45);

3. A pair of instances of astropy.time.Time;

4. A single instance of the Tag class.

The list of tags is always sorted in chronological order.

The function is quite fast because it uses a cache instead of reading the HDF5 files themselves.

load_hk(mjd_range: Union[Tuple[float, float], striptease.hdf5files.Tag], *args, **kwargs)Load housekeeping data within a specified MJD time range

This function operates in the same way as DataFile.load_hk(), but it takes as input a time rangethat can cross the HDF5 file boundaries.The parameter mjd_time range can be one of the following:

1. A pair of floating-point values, each representing a MJD date;

2. A pair of strings, each representing a date (e.g., 2021-12-10 10:39:45);

3. A pair of instances of astropy.time.Time;

4. A single instance of the Tag class.

Example:

from striptease import DataStorage

ds = DataStorage("/database/STRIP/HDF5/")# Caution! One whole day of scientific data!times, data = ds.load_hk(

mjd_range=(59530.0, 59531.0),group="BIAS",subgroup="POL_R0",par="VG1_HK",

)

load_sci(mjd_range: Union[Tuple[float, float], Tuple[astropy.time.core.Time, as-tropy.time.core.Time], Tuple[str, str], striptease.hdf5files.Tag], *args, **kwargs)

Load scientific data within a specified MJD time range

5.3. Handling multiple HDF5 files 25

Page 30: Striptease Documentation

Striptease Documentation, Release 0.1.0

This function operates in the same way as DataFile.load_sci(), but it takes as input a time rangethat can cross the HDF5 file boundaries. The parameter mjd_time range can be one of the following:

1. A pair of floating-point values, each representing a MJD date;

2. A pair of strings, each representing a date (e.g., 2021-12-10 10:39:45);

3. A pair of instances of astropy.time.Time;

4. A single instance of the Tag class.

Example:

from striptease import DataStorage

ds = DataStorage("/database/STRIP/HDF5/")# Caution! One whole day of scientific data!

times, data = ds.load_sci(mjd_range=(59530.0, 59531.0),polarimeter="R0",data_type="DEM",detector=["Q1"],

)

You can access a list of the files indexed by a DataStorage object using the method DataStorage.get_list_of_files(), which returns a list of :.HDF5FileInfo objects.

class striptease.HDF5FileInfo(path, size, mjd_range)Basic information about a HDF5 data file

Fields are:

• path: a pathlib.Path object containing the path to the file

• size: size of the file, in bytes

• mjd_range: a 2-tuple containing the MJD of the first and last scientific/housekeeping sample in the file(float values)

26 Chapter 5. Reading data saved in HDF5 files

Page 31: Striptease Documentation

CHAPTER 6

Writing test procedures

To create a new script that builds a sequence of JSON commands, the easiest way is to create a new class derived fromstriptease.procedures.StripProcedure:

from striptease import StripProcedurefrom calibration import CalibrationTables

class MyProcedure(StripProcedure):def __init__(self):

super(MyProcedure, self).__init__()self.calibr = CalibrationTables()

def run(self):# Run all your commands using "self.conn": it is a# StripConnection object that writes command to a JSON# buffer, instead of sending them to the server

# Here is an example: set the drain current of one# amplifier to some value (in ADU)self.conn.set_id(

polarimeter="R0",lna="HA1",value_adu=1345,

)

# A more complex example: set the drain current of# another amplifier to a *physical* value in 𝜇Aself.conn.set_id(

polarimeter="B3",lna="HB2",value_adu=self.calibr.physical_units_to_adu(

polarimeter="B3",hk="idrain",component="HB2",value=11_000,

(continues on next page)

27

Page 32: Striptease Documentation

Striptease Documentation, Release 0.1.0

(continued from previous page)

),)

if __name__ == "__main__":proc = MyProcedure()proc.run()proc.output_json()

The procedure above will simply set the drain currents of two amplifiers in polarimeters R0 and B3 to some hard-coded value, using StripConnection.set_id(). Finally, the test procedure will be written to stdout as aJSON object:

[{

"path": "/rest/slo","kind": "command","command": {

"board": "R","pol": "R0","base_addr": "ID0_SET","type": "BIAS","method": "SET","size": 1,"timeout": 500,"data": [

1345]

}},{

"path": "/rest/slo","kind": "command","command": {

"board": "B","pol": "B3","base_addr": "ID3_SET","type": "BIAS","method": "SET","size": 1,"timeout": 500,"data": [

1919]

}}

]

Note that the names of the LNAs (HA1 and HB2) have been converted in the naming scheme used by the electronics (theindexes 0 and 3 used in the names ID0_SET and ID3_SET), and that the value 11,000 𝜇A has been converted into1919 ADU using the appropriate calibration curve for amplifier HB2 in polarimeter B3. You must always instantiate aCalibrationTables object, if you want to use physical units for biases.

For a real-world example of test procedure, have a look at program_pinchoff.py.

28 Chapter 6. Writing test procedures

Page 33: Striptease Documentation

Striptease Documentation, Release 0.1.0

6.1 Pretty-printing test procedures

When debugging a procedure, it is useful to have a quick look of the commands it contains. Striptease provides aprogram, pretty_print_procedure.py, that outputs the list of commands in a procedure using colors. Thefollowing demo shows how to use it:

6.2 Module contents

class striptease.procedures.JSONCommandEmitter(conn)Bases: object

This class captures commands sent to the board and outputs a JSON representation on some text stream.

post_command(url, cmd)

tag_start(name, comment=”)

tag_stop(name, comment=”)

wait(seconds)

class striptease.procedures.StripProcedureBases: object

A test procedure that records commands in JSON objects

This is a base class to be used when you need to implement a test procedure. Instead of sending commands tothe true instrument, the class records all the commands in a JSON object, which can be saved in a text file andran using the command-line program program_batch_runner.py.

You must define a new class from this, and redefine the run method. In this method, you can use self.conn as aStripConnection object.

clear_command_list()Remove all the commands produced so far from memory

get_command_list()Return a list object containing the commands executed so far.

This list will be dumped as a JSON object by output_json().

output_json(output_filename=None)Write the list of commands executed so far in a JSON object.

Parameters (str or Path) (output_filename) – created that will contain the list ofcommands in JSON format. If None is used, the commands will be printed to stdout.

run()Redefine this method in derived classes

wait(seconds)Make the test procedure wait for some time before continuing

striptease.procedures.dump_procedure_as_json(outf, obj, indent_level=0,use_newlines=True)

Dump a list of commands into a JSON file.

This is similar to what the standard function json.dump does, but it uses less carriage returns to make theoutput easier to read and to search with grep.

6.1. Pretty-printing test procedures 29

Page 34: Striptease Documentation

Striptease Documentation, Release 0.1.0

30 Chapter 6. Writing test procedures

Page 35: Striptease Documentation

CHAPTER 7

Run log database

It is handy to keep track of all the test procedures that are executed on the Strip instrument. The Striptease librarysupports the creation and update of a database that can be used whenever a JSON procedure is issued.

The program program_batch_runner.py makes use of the function append_to_run_log() to updatethe database, which is stored in ~/.strip/run_log.db. The database can be queried using the programdump_run_log.py; use the --help command to learn how to use it. It can print the contents of the databaseor save them in the JSON or CSV format; it can also dump the commands for a specific log in a JSON file.

class striptease.runlog.RunLogEntry(id, start_time, end_time, wait_time_s, wait_cmd_time_s,full_path, number_of_commands, zstd_json_procedure)

Bases: tuple

end_timeAlias for field number 2

full_pathAlias for field number 5

idAlias for field number 0

number_of_commandsAlias for field number 6

start_timeAlias for field number 1

wait_cmd_time_sAlias for field number 4

wait_time_sAlias for field number 3

zstd_json_procedureAlias for field number 7

31

Page 36: Striptease Documentation

Striptease Documentation, Release 0.1.0

striptease.runlog.append_to_run_log(start_time: datetime.datetime, end_time: date-time.datetime, wait_time_s: Union[int, float],wait_cmd_time_s: Union[int, float], full_path: str,procedure: List[Any])

Add a new entry to the log of procedures that have been executed.

This function adds a new entry to the database that keeps track of all the JSON procedures that have been exe-cuted by this user. The procedure is saved in the database using the JSON format and the Zstandard compression,and it should not take too much space (the average compression ratio is ~103).

striptease.runlog.connect_to_run_log()→ sqlite3.ConnectionConnect to the run log database or create one if it does not exist

32 Chapter 7. Run log database

Page 37: Striptease Documentation

CHAPTER 8

Accessing unit test data

Striptease implements a few functions to download and access the data acquired during the unit tests of the Strippolarimeters. These tests were done at the cryogenic laboratory of the University of Milan Bicocca, and they areused as reference in some of the system-level tests. The tests are currently available at https://striptest.fisica.unimi.it/unittests/; the library provides a simple interface to download the data from there and load them into Python objects.

Here is an example that shows how to use these functionalities:

import striptease.unittests as u

# Download the test with ID #354 from the web server. This might# take a few seconds...test = u.get_unit_test(354)

# ...but if you ask for it again, it will use a local cache and will# return the value instantly_ = u.get_unit_test(354)

# The result of the call to "u.get_unit_test" is a UnitTest objectprint(type(test))# Output:# <class 'striptease.unittests.UnitTest'>

print(f"{test.url}, {test.polarimeter_name}")# Output:# https://striptest.fisica.unimi.it/unittests/tests/354, STRIP02

# The "test" variable only contains metadata about the test.# To actually load the datasets, use "u.load_unit_test_data".# It returns the actual data from the HDF5 file itself.data = u.load_unit_test_data(test)

# The type of "data" depends on the test; it can either be a# "UnitTestDC" class or a "UnitTestTimestream" class.print(type(data))

(continues on next page)

33

Page 38: Striptease Documentation

Striptease Documentation, Release 0.1.0

(continued from previous page)

# Output:# <class 'striptease.unittests.UnitTestDC'>

As you see in the example, there are two functions to access the test data: the first one, get_unit_test(),downloads the metadata for a test, while the second one, load_unit_test_data(), download the full data. Asthe second one can take some time, if you are just interested in the general characteristics of the test (e.g., when thetest was done, or on which polarimeter) you should call just get_unit_test().

striptease.get_unit_test(test_num: int, server=’https://striptest.fisica.unimi.it’, lo-cal_cache=PosixPath(’/home/docs/.strip/unittests/unittests.db’)) →Optional[striptease.unittests.UnitTest]

Return a UnitTest object referring to a unit test.

This function is used to access the data files acquired during the Strip Unit Tests done in Milano Bicocca. Eachtest is referenced by an unique number, which is passed in the parameter test_num. The parameter server mustbe a string containing the name of the web server hosting the test database. The parameter local_cache pointsto a file that will contain a local copy of each test downloaded from the webserver.

The following code prints some information about test #354:

from striptease.unittests import get_unit_test

test = get_unit_test(354)print(f"The test was acquired on {test.acquisition_date}")

Once you retrieve the information for a test, you can use load_unit_test_data() to load the actual dataacquired during the test.

Parameters

• test_num (int) – the unique ID of the unit test to download

• server (str) – the protocol+name/IP address of the web server that hosts the unit tests.The default is the variable DEFAULT_UNIT_TEST_SERVER, and it should probably beok for any situation.

• local_cache (Path) – the path to a file that will contain a local copy of each test down-loaded using this function. This speeds up subsequent queries to the same test.

Returns An instance of UnitTest.

striptease.load_unit_test_data(input_file: Union[str, pathlib.Path,striptease.unittests.UnitTest]) →Union[striptease.unittests.UnitTestTimestream,striptease.unittests.UnitTestDC]

Load an HDF5 file into a Timestream object

Return either an instance of the class UnitTestTimestream or UnitTestDC. The choice between the twotypes depends on the type of the test (i.e., the value of the key test_type in the metadata of the test):

1. DC tests return a UnitTestDc; these tests were acquired by directly sampling the biases at the compo-nents of the polarimeters, and no proper time streams were recorded.

2. All other types of tests return a UnitTestTimestream.

The typical usage for this function is to call it on the result of a call to get_unit_test(), as in the followingexample:

34 Chapter 8. Accessing unit test data

Page 39: Striptease Documentation

Striptease Documentation, Release 0.1.0

import striptease.unittests as u

test = u.get_unit_test(354)data = load_unit_test_data(test)

Parameters input_file – Either a string containing the path to a HDF5 file, a pathlib.Pathobject, or a UnitTest object.

Returns (1) a dictionary containing a set of attributes read from the root object of the HDF5 file,and (2) either an instance of the class UnitTestTimestream or UnitTestDC, dependingon the type of the test.

Return type A pair containing two elements

The type of the return value of the function load_unit_test_data() depends on the kind of test. During theunit tests in the cryogenic laboratory of the University of Milano Bicocca, tests were grouped in two categories:

• DC tests exercised every single component of each polarimetric unit. They were done using an apparatusthat provided biases and measured currents and voltages. Data acquired during this kind of test is stored in aUnitTestDC object.

• The tests that exercised the polarimeter as a whole used a different acquisition system, which was able to recordthe timing of each measurement. Data acquired during this kind of test is stored in a UnitTestTimestreamobject.

In the next chapters we detail the structure of the two classes UnitTestDC and UnitTestTimestream.

class striptease.UnitTestTypeAn enumeration.

class striptease.UnitTest(url: str = ”, metadata: Dict[str, Any] = <factory>, hdf5_file_path:pathlib.Path = PosixPath(’.’))

Information about a polarimetric unit test

This class hold some information about the data of a polarimetric unit test ran in Bologna, and it is returned bythe function get_unit_test(). It contains the following fields:

• url: a string containing the base URL of the test

• metadata: a dictionary containing the JSON record downloaded from the web server. This dictionarycontains many details about the test itself: the name of the operators that actually executed the test, thedate when the data was acquired, etc.

• hdf5_file_path: a pathlib.Path object pointing to the HDF5 file saved in the local cache. In-stead of accessing this file directly, you should instead call load_unit_test_data().

class striptease.UnitTestDC(acquisition_date: datetime.date, band: str, is_cryogenic:bool, polarimeter_name: str, url: str, components: Dict[str,striptease.unittests.UnitTestDCCurves] = <factory>)

Detailed information about a unit-level DC test of a polarimeter.

This class is created by the function load_unit_test_data() whenever a DC test is requested. It containsthe following fields:

• acquisition_date: a datetime.date object containing the date when the test was acquired.

• band: a string containing either Q or W;

• is_cryogenic: a Boolean value indicating whether the test was done in cryogenic or warm conditions;

• polarimeter_name: the name of the polarimeter being tested (e.g., STRIP02);

35

Page 40: Striptease Documentation

Striptease Documentation, Release 0.1.0

• url: a string containing the URL of the test page

• components: a dictionary associating the name of each component of the polarimeter (e.g., HA1 for thefirst amplifier of the first leg) with a UnitTestDCCurves object, which contains the data of the variouscurves characterizing the polarimetric component.

class striptease.UnitTestTimestream(acquisition_date: datetime.date, band: str, is_cryogenic:bool, polarimeter_name: str, url: str, time_s: Any =None, pctime: Any = None, phb: Any = None, record:Any = None, demodulated: Any = None, power: Any =None, rfpower_db: Any = None, freq_hz: Any = None)

A time stream acquired from one polarimeter during the Unit Level tests in Bicocca.

This class is created by the function load_unit_test_data() whenever the caller asks to load a unit-leveltest where a timestream is acquired (e.g., bandpass measurement, noise temperature characterization, etc.). Itcontains the following fields:

• acquisition_date: a datetime.date object containing the date when the test was acquired.

• band: a string containing either Q or W;

• is_cryogenic: a Boolean value indicating whether the test was done in cryogenic or warm conditions;

• polarimeter_name: the name of the polarimeter being tested (e.g., STRIP02);

• url: a string containing the URL of the test page

• time_s: A NumPy array containing the time in seconds

• pctime: A NumPy array containing the On-board time, in clock ticks

• phb: A NumPy array containing the phase of the slow phase switch

• record: A NumPy array containing an undocumented field

• demodulated: A 4×N NumPy matrix containing the output of DEM0, DEM1, DEM2, DEM3 chan-nels

• power: A 4×N NumPy matrix containing the output of PWR0, PWR1, PWR2, PWR3 channels

• rfpower_db: A NumPy array containing the power of the -radiofrequency generator, in dB, or 1 ifturned off

• freq_hz: A NumPy array containing the frequency of the signal injected by the radiofrequency gen-erator, in Hertz. If the generator is turned off, this is -1.

8.1 Single-component tests

Single-component tests, also known as «DC tests», are represented by an instance of the class UnitTestDC. Theclass provides the data measured while exercising each of the components in the components field, which is aninstance of the class UnitTestDCCurves. You can think of both classes as wrappers to dict types.

Here is an example, using test #354 as a testbed:

import striptease.unittests as uimport matplotlib.pylab as plt

data = u.load_unit_test_data(u.get_unit_test(354))ha1_ivdv_data = data.components['HA1'].curves['IDVD']

_, axes = plt.subplots(nrows=3, figsize=(6, 14))

(continues on next page)

36 Chapter 8. Accessing unit test data

Page 41: Striptease Documentation

Striptease Documentation, Release 0.1.0

(continued from previous page)

axes[0].plot(ha1_ivdv_data['DrainV'],ha1_ivdv_data['DrainI'],

)axes[0].set_title(r'$V_d \times I_d$ curves for HA1')axes[0].set_xlabel('Drain voltage')axes[0].set_ylabel('Drain current')

axes[1].plot(ha1_ivdv_data['GateI'], '.')axes[1].set_ylabel('Gate current')

axes[2].plot(ha1_ivdv_data['GateV'], '.')axes[2].set_ylabel('Gate voltage')

The result is the following plot:

Note that in this test the drain current and voltage were measured every time the gate biases were changed. The actualbehavior of the data depends of course on the component and the test being done.

8.2 Polarimetric tests (timelines)

Timeline data acquired during a Strip unit test is stored in objects of type UnitTestTimestream. The followingexample shows how to plot the demodulated data acquired during test #355 (STRIP02):

import striptease.unittests as uimport matplotlib.pylab as plt

data = u.load_unit_test_data(u.get_unit_test(355))_, axes = plt.subplots(nrows=4, figsize=(6, 17))

# Plot the output of DEM0, DEM1, DEM2, and DEM3for i in range(4):

axes[i].plot(data.time_s, data.demodulated[:, i])axes[i].set_xlabel("Time [s]")axes[i].set_ylabel("Signal [ADU]")

And here is the result:

8.3 Other information

There are a few other functions implemented by the framework that you might find useful, e.g., when you are producinga report and want to put links to a unit test you used in your calculations.

striptease.unit_test_url(test_num: int, server=’https://striptest.fisica.unimi.it’)→ strReturn a string containing the URL of a unit-level test

striptease.unit_test_json_url(test_num: int, server=’https://striptest.fisica.unimi.it’)→ strReturn a string containing the URL of the JSON record for a unit-level test

striptease.unit_test_download_url(test_num: int, server=’https://striptest.fisica.unimi.it’) →str

Return a string containing the URL used to download the HDF5 file of a test

8.2. Polarimetric tests (timelines) 37

Page 42: Striptease Documentation

Striptease Documentation, Release 0.1.0

Fig. 1: Plot generated by the example script

38 Chapter 8. Accessing unit test data

Page 43: Striptease Documentation

Striptease Documentation, Release 0.1.0

Fig. 2: Plot generated by the example script

8.3. Other information 39

Page 44: Striptease Documentation

Striptease Documentation, Release 0.1.0

8.4 How caching works

Every time the function get_unit_test() is called, it saves a copy of the HDF5 file and the metadata downloadedfrom the unit test webserver into a hidden directory within the home folder of the current user. The metadata are savedin a SQLite3 database, and the HDF5 file is saved within the same folder.

The path to the SQLite3 database can be accessed using the constant DEFAULT_UNIT_TEST_CACHE_PATH. How-ever, you will probably never need to mess with this directory, as it is handled internally by the library.

40 Chapter 8. Accessing unit test data

Page 45: Striptease Documentation

CHAPTER 9

Developer’s manual

This section of the documentation describes how to develop new features.

9.1 Graphical User Interfaces (GUIs)

In this section, we provide a few recipes that show how to implement features commonly found in GUI programs.

9.1.1 Creating new forms

A form is a window on the screen containing controls, called widgets. Striptease uses the PyQt bindings to the Qtlibrary to show and manipulate forms.

As the visual layout of forms can be extremely complex, Qt provides a GUI tool to design and test forms, called QtDesigner. Although Qt Designer has been developed with C++ programmers in mind, it can be used to create formsto be used in Python programs too.

Here is a screenshot of Qt Designer:

9.1.2 Using Websocket plots in forms

A common feature in Striptease programs is to show a real-time plot of the data read from the electronic boards.Striptease provides a class, widgets.plot.pol.PolMplCanvas, that helps in implementing this kind of widget.Here we provide an explanation of how to use it in forms designed with Qt Designer.

Once you have created a new form in Qt Designer, insert a plain Widget control in the form. This form is generic, i.e.,it will be shown as a transparent rectangular surface: Qt Designer has no way to understand how it will appear oncethe program is completed.

We must however tell Qt Designer that this widget should be of the proper type. This is called promotion: select thenew transparent widget and pick the entry «Promote to. . . » from the contextual menu under the object inspector, asshown in the following figure.

41

Page 46: Striptease Documentation

Striptease Documentation, Release 0.1.0

Fig. 1: QT Designer 5.9

A new window will appear. This window contains all the custom widgets defined so far in Qt Designer, and it shouldbe empty. Fill the fields with the following values:

• Base class name: leave as it is now (QWidget);

• Promoted class name: enter PolMplCanvas (case is important!);

• Header file: enter widgets.plot.pol.h;

• Global include: leave it blank.

The header file we have specified is widgets.plot.pol.h, which is simply the Python module containing thedefinition for PolMplCanvas, with a .h appended at the end. The .h extension must be inserted because QtDesigner always thinks that modules are defined in C++ header files; this extension will be silently dropped once weload the form in our Python script.

Once you have entered the values listed above, press Add; the new promotion should be added to the list Promotedclasses:

So far we have defined a promotion from a generic widget to a widgets.plot.pol.PolMplCanvas, but wehave not asked Qt Designer to use it on our widget in the main form (see the text «Not used» under the columnUsage?). Just press the button Promote to promote the widget.

We need only one final touch. From the point of view of Qt Designer, the widget is no longer a generic QWidget, butyet the program does not know how to plot it. This means that the widget has no size, and thus the scaling of the formmight look weird when opened in Qt Designer. To fix this, you can set the field minimumSize in the Property Editoron the left. Setting it to 640×480 is usually fine:

42 Chapter 9. Developer’s manual

Page 47: Striptease Documentation

Striptease Documentation, Release 0.1.0

9.1. Graphical User Interfaces (GUIs) 43

Page 48: Striptease Documentation

Striptease Documentation, Release 0.1.0

44 Chapter 9. Developer’s manual

Page 49: Striptease Documentation

Striptease Documentation, Release 0.1.0

9.1. Graphical User Interfaces (GUIs) 45

Page 50: Striptease Documentation

Striptease Documentation, Release 0.1.0

46 Chapter 9. Developer’s manual

Page 51: Striptease Documentation

CHAPTER 10

Indices and tables

• genindex

• modindex

• search

47

Page 52: Striptease Documentation

Striptease Documentation, Release 0.1.0

48 Chapter 10. Indices and tables

Page 53: Striptease Documentation

Python Module Index

sstriptease.procedures, 29striptease.runlog, 31

49

Page 54: Striptease Documentation

Striptease Documentation, Release 0.1.0

50 Python Module Index

Page 55: Striptease Documentation

Index

Aadd (calibration.CalibrationCurve attribute), 11adu_to_physical_units() (calibra-

tion.CalibrationTables method), 10append_to_run_log() (in module

striptease.runlog), 31

CCalibrationCurve (class in calibration), 11CalibrationTables (class in calibration), 10clear_command_list()

(striptease.procedures.StripProcedure method),29

connect_to_run_log() (in modulestriptease.runlog), 32

DDataFile (class in striptease), 20DataStorage (class in striptease), 24div (calibration.CalibrationCurve attribute), 11dump_procedure_as_json() (in module

striptease.procedures), 29

Eend_time (striptease.runlog.RunLogEntry attribute),

31

Ffiles_in_range() (striptease.DataStorage

method), 25full_path (striptease.runlog.RunLogEntry attribute),

31

Gget_average_biases() (striptease.DataFile

method), 21get_calibration_curve() (calibra-

tion.CalibrationTables method), 10

get_command_list()(striptease.procedures.StripProcedure method),29

get_hk_descriptions() (in module striptease), 23get_list_of_files() (striptease.DataStorage

method), 25get_tags() (striptease.DataStorage method), 25get_unit_test() (in module striptease), 34

HHDF5FileInfo (class in striptease), 26HkDescriptionList (class in striptease), 23

Iid (striptease.runlog.RunLogEntry attribute), 31intercept (calibration.CalibrationCurve attribute),

11

JJSONCommandEmitter (class in

striptease.procedures), 29

Lload_hk() (striptease.DataFile method), 21load_hk() (striptease.DataStorage method), 25load_sci() (striptease.DataFile method), 21load_sci() (striptease.DataStorage method), 25load_unit_test_data() (in module striptease), 34

Mmul (calibration.CalibrationCurve attribute), 11

Nnumber_of_commands

(striptease.runlog.RunLogEntry attribute),31

Ooutput_json() (striptease.procedures.StripProcedure

method), 29

51

Page 56: Striptease Documentation

Striptease Documentation, Release 0.1.0

Pphysical_units_to_adu() (calibra-

tion.CalibrationTables method), 10post_command() (striptease.procedures.JSONCommandEmitter

method), 29

Rread_file_metadata() (striptease.DataFile

method), 22run() (striptease.procedures.StripProcedure method),

29RunLogEntry (class in striptease.runlog), 31

Sslope (calibration.CalibrationCurve attribute), 11start_time (striptease.runlog.RunLogEntry at-

tribute), 31StripProcedure (class in striptease.procedures), 29striptease.procedures (module), 29striptease.runlog (module), 31

TTag (class in striptease), 23tag_start() (striptease.procedures.JSONCommandEmitter

method), 29tag_stop() (striptease.procedures.JSONCommandEmitter

method), 29

Uunit_test_download_url() (in module

striptease), 37unit_test_json_url() (in module striptease), 37unit_test_url() (in module striptease), 37UnitTest (class in striptease), 35UnitTestDC (class in striptease), 35UnitTestTimestream (class in striptease), 36UnitTestType (class in striptease), 35

Wwait() (striptease.procedures.JSONCommandEmitter

method), 29wait() (striptease.procedures.StripProcedure method),

29wait_cmd_time_s (striptease.runlog.RunLogEntry

attribute), 31wait_time_s (striptease.runlog.RunLogEntry at-

tribute), 31

Zzstd_json_procedure

(striptease.runlog.RunLogEntry attribute),31

52 Index