Time-ordered data (TOD)

Stripeline contains functions that create the timeline of scientific data as produced by each polarimeter and distribute it across several parallel processes, potentially running through MPI.

A timeline of scientific data is generically called a TOD, and the polarimeters implemented in Strip produce a complex flow of data, as they output eight series:

  • Four series are called PWR series: PWR_Q1, PWR_Q2, PWR_U1, and PWR_U2;
  • Four series are called DEM series: DEM_Q1, DEM_Q2, DEM_U1, and DEM_U2.

Each of these series is sampled at 50 Hz and contains scientific data (in the preliminary unit tests done in 2017, the sampling frequency was 25 Hz; keep this in mind if you happen to replicate some returns of those ancient tests!); the raw instrument represents them as integer numbers, but Stripeline usually assumes they have already been calibrated to Kelvin and therefore uses floating-point numbers.

A TOD is represented by the structure StripTod, which contains the following fields:

  • polarimeters: a generic sequence of values, which represent each polarimeter stored in the TOD. Very common choices for this field are a range (for example, 1:49 represents all the Q-band polarimeters) or a list of strings, e.g., ["I0", "I3", "V2"].
  • time_range: a range representing the time of each sample in the TOD. Very common choices for this are floating-point ranges like 0.0:(1 / 50):100.0 (100 s sampled at 50 Hz) and ranges of astronomical times, created using the AstroTime package.
  • samples: this is the matrix containing the actual scientific samples. It has a shape of $N \times 8 \times m$, where $N$ is the number of samples along the time axis and must be equal to length(time_range), and $m$ is the number of polarimeters, which must be equal to length(polarimeters). The middle dimension is 8 because there are eight timelines produced by each polarimeter (see above).

Allocating the TOD is a matter of calling the function allocate_tod:

using Stripeline

# One minute of data sampled at 50 Hz, 10 polarimeters
tod = allocate_tod(StripTod, Float32, 0.0:(1.0 / 50.0):60.0, 1:10)
TOD(10 polarimeters, 3001 rows/polarimeter, time range from 0.0 to 60.0)

In a MPI environment, you can pass the keywords mpi_rank and mpi_size to allocate_tod and the function will only allocate the subset of the overall time range that is assigned to the running MPI process.

Once a StripTod variable is allocated, accessing the samples in the TOD can be done through the field samples, which is a matrix:

println("The shape of the TOD is ", size(tod.samples))
The shape of the TOD is (3001, 8, 10)

Accessing the right column can be cumbersome, as there are eight of them (PWR_Q1, PWR_Q2, …), so Stripeline provides the constants PWR_Q1_RANK, PWR_Q2_RANK, … for this:

# Print the minimum sample acquired by PWR_Q1 for polarimeter number #4
println(minimum(tod.samples[:, PWR_Q1_RANK, 4]))
0.0

The result is zero because allocate_tod sets all the samples to zero by default. An alternative approach is to use one of the functions pwrq1, pwrq2, pwru1, pwru2, demq1, demq2, demu1, demu2, which return a view of the original array and can thus be used both for reading and writing:

# Set one element of PWR_Q1 for polarimeter number #4
pwrq1(tod, 4)[142] = 1.36
println(
    "The maximum element of polarimeter #4 is now ",
    maximum(pwrq1(tod, 4)),
)
The maximum element of polarimeter #4 is now 1.36
Stripeline.StripTodType

A matrix of time-ordered PWR/DEM data for a subset of polarimeters

The fields of this structure are the following:

  • time_range: a range representing the time of each sample in the TOD.

  • samples: a N×8×m matrix, where N is the number of samples in the timeline, and m is the number of polarimeters. The middle dimension spans the three Stokes parameters I, Q, and U (in this order). The value N is equal to length(time_range)

  • rng: a pseudo-random number generator to be used for this TOD. It is guaranteed that the generator is uncorrelated with any other generator used for other StripTod objects created by the same call to allocate_tod.

source
Stripeline.allocate_todFunction
allocate_tod(todtype, t, time_range, polarimeters; mpi_rank=0, mpi_size=1, zero_tod=true)

Allocate memory for a time-ordered data matrix appropriate for the set of polarimeters in the variable polarimeters (an array or range, only its length is used and it is copied as-is in the returned StripTod value). The type of each sample is set to t. The variable time_range must be a range of floating-point values or epochs (as defined by the AstroTime package) which represent the full set of samples acquired during an observation.

The optional parameters mpi_rank and mpi_size can be used in MPI scripts to allocate only the subset of samples that must be processed by the current MPI process.

If zero_tod is true, each sample in the TOD is set to zero; otherwise, its value is undefined. Using zero_tod = false is slightly faster, but it should be used only if you are going to set by hand every sample in the TOD once allocate_tod returns.

Return a StripTod value.

Example

julia> tod = allocate_tod(StripTod, Float32, 0.0:0.01:100.0, 1:5)
TOD(5 polarimeters, 10001 rows/polarimeter, time range from 0.0 to 100.0)
source
Stripeline.pwrq1Function
pwrq1(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel PWR_Q1 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, PWR_Q1_RANK, pol]
source
Stripeline.pwrq2Function
pwrq2(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel PWR_Q2 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, PWR_Q2_RANK, pol]
source
Stripeline.pwru1Function
pwru1(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel PWR_U1 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, PWR_U1_RANK, pol]
source
Stripeline.pwru2Function
pwru2(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel PWR_U2 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, PWR_U2_RANK, pol]
source
Stripeline.demq1Function
demq1(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel DEM_Q1 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, DEM_Q1_RANK, pol]
source
Stripeline.demq2Function
demq2(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel DEM_Q2 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, DEM_Q2_RANK, pol]
source
Stripeline.demu1Function
demu1(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel DEM_U1 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, DEM_U1_RANK, pol]
source
Stripeline.demu2Function
demu2(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel DEM_U2 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, DEM_U2_RANK, pol]
source

I/Q/U TODs

The scientific information kept in a StripTod variable is encoded in eight timelines (PWRQ1,PWRQ2,PWRU1,PWRU2,DEMQ1,DEMQ2,DEMU1,DEMU2`), but most of the information in these timelines is redundant and is essentially correlated/uncorrelated noise.

The eight timelines can be combined to provide a direct estimate of the four Stokes parameters $I$, $Q$, and $U$ in the reference frame of the detector, using these formulae:

\[\begin{aligned} I &= \frac{\text{PWRQ1} + \text{PWRQ2} + \text{PWRU1} + \text{PWRU2}}4,\\ Q &= \frac{\text{PWRQ1} - \text{PWRQ2}}2,\\ U &= \frac{\text{PWRU1} - \text{PWRU2}}2. \end{aligned}\]

The three $I$/$Q$/$U$ timelines can be stored in a StokesTod variable, which behaves exactly like a StripTod but only contains three timeseries instead of eight. To allocate it, you can use allocate_tod again, passing a StokesTod parameter:

stokestod = allocate_tod(StokesTod, Float32, 0.0:(1.0 / 50.0):60.0, 1:10)
StokesTOD(10 polarimeters, 3001 rows/polarimeter, time range from 0.0 to 60.0)

Or you can compute it starting from a StokesTod variable:

stokestod = stokes(tod)
StokesTOD(10 polarimeters, 3001 rows/polarimeter, time range from 0.0 to 60.0)

The latter call works out-of-the-box in a MPI environment, of course. You can access the $I$/$Q$/$U$ timelines either using the explicit indexes STOKES_I_RANK, STOKES_Q_RANK, STOKES_U_RANK, or the three functions stokesi, stokesq, stokesu, similarly to what was described above for StripTod.

Stripeline.StokesTodType

A matrix of time-ordered PWR/DEM data for a subset of polarimeters

The fields of this structure are the following:

  • time_range: a range representing the time of each sample in the TOD.

  • samples: a N×8×m matrix, where N is the number of samples in the timeline, and m is the number of polarimeters. The middle dimension spans the three Stokes parameters I, Q, and U (in this order). The value N is equal to length(time_range)

  • rng: a pseudo-random number generator to be used for this TOD. It is guaranteed that the generator is uncorrelated with any other generator used for other StripTod objects created by the same call to allocate_tod.

source
Stripeline.stokesFunction
stokes(tod::StripTod{T, S, R})

Return a StokesTod variable containing the estimated $I$/$Q$/$U$ Stokes parameters associated with the parameter tod, which is a time-ordered data variable containing the eight outputs PWR_Q1, PWR_Q2, PWR_U1, PWR_U2, DEM_Q1, DEM_Q2, DEM_U1, DEM_U2 of a set of Strip polarimeters.

source
Stripeline.stokesiFunction
stokesi(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel STOKES_I in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, STOKES_I_RANK, pol]
source
Stripeline.stokesqFunction
stokesq(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel STOKES_Q in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, STOKES_Q_RANK, pol]
source
Stripeline.stokesuFunction
stokesu(tod::Stripeline.StripTod, pol::T) where T <: Integer

Return a view of the timeline for channel STOKES_U in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for

tod.samples[:, STOKES_U_RANK, pol]
source