Polarimeters¶
This section explains some general-purpose functions and constants that are useful to manipulate polarimeters.
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 user is valid, you
can use STRIP_BOARD_NAMES
. The following example mocks a
processing script that needs to iterate over 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:
break
else:
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: R
Processing board V
Processing board G
Processing board B
Processing board Y
Processing board O
Processing board I
Polarimeter names¶
Switching between modules and polarimeters¶
Each «module» (e.g., I0) is associated with a «polarimeter»
(STRIP34
), and it is often the case that procedures and data
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
class InstrumentBiases
:
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
W-band polarimeters¶
The convention used to name the Strip polarimeters is rather
unfortunate, as it follows a different convention for Q and W-band
polarimeters. While the former are indicated by a letter identifying
the board and a positive number indicating the 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 name of 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 R
W4 is connected to board V
W6 is connected to board G
W5 is connected to board B
W1 is connected to board Y
W2 is connected to board O
Board I has no W-band polarimeters
In some contexts, a saner naming convention is used for W-band
polarimeters: they are referred using the name of the board they
belong to, followed by the index 7
. For instance, the name R7
can be used to refer to polarimeter W3
, as this polarimeter is
connected to board R
. The function
normalize_polarimeter_name()
can be used to turn the name W3
into the R-based name:
print(normalize_polarimeter_name("W1")) # Prints Y7
Polarimeter index within a board¶
The function get_polarimeter_index()
returns a zero-based index
of a polarimeter in a board, given the name of the polarimeter. W-band
polarimeters always use index 7
:
print(get_polarimeter_index("R0")) # Print 0
print(get_polarimeter_index("G4")) # Print 4
print(get_polarimeter_index("W1")) # Print 7
Iterating over polarimeters¶
It is often the case that some code needs to iterate over all the
polarimeters in a board. The function polarimeter_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)
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 permit to 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.
Amplifiers¶
Indexing¶
The official naming convention enumerates the amplifiers according to
the line exiting the phase switch (A
or B
) and their order
within the line (1
, 2
, 3
). Therefore, amplifier HA1
is
the first stage of amplification in line A
, and it’s followed 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:
- The indexes used by JPL for the QUIET experiment (e.g.,
Q5
); - 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 an LNA from its full name in one of the three
conventions named above:
# All these examples refer to the same LNA
print(get_lna_num("HA3")) # Print 4
print(get_lna_num("H4")) # Print 4
print(get_lna_num("Q5")) # Print 4
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:
- In open loop mode, the drain voltage \(V_d\) and gate voltage \(V_g\) are set by the user, while the drain current \(I_d\) is set free to adapt to the voltages.
- In closed loop mode, the drain current \(I_d\) and drain voltage \(V_d\) are set by the user, and a retro-feedback circuit in the electronics adapts the gate voltage \(V_g\) to make sure that the drain current $I_d$ is kept at the level specified by the user.
The following figure depicts the difference between the two modes, representing the three bias parameters \(I_d\), \(V_d\), and \(V_g\) as knobs; only green knobs can be manipulated by the user, while the blue knob responds automatically to variations 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 you can 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)
Phase switches¶
Each polarimeter in Strip implements two phase switches, which are devices able to inject a phase shift in the signal by making it pass through a longer path to induce a phase shift in the wave. They are called «switches» because they are 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). It implements 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 controlling a «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 back and forth between a «forward/reverse» and a «reverse/forward» configuration, so that the signal can either pass through the first or the second path, but never through both of them (which would cause destructive interference when the two paths join).
The default configuration of the phase switches is the following:
- 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 to understand 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 to the diodes using the
method StripConnection.set_phsw_status()
. Of particular interest
is the constant NOMINAL_SWITCHING
, which assumes a different
meaning depending on the pin diode you are programming: it makes 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 Hz for 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 the shorter 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)