19def generate_LF_antenna_pairs(antennas: pl.DataFrame) -> pl.DataFrame:
22 antenna_pair_matrix = np.triu(np.ones((8, 8), dtype=int), 1)
24 antidx = antennas[
"AntIdx"].to_numpy()
25 i, j = np.where(antenna_pair_matrix > 0)
26 pair_df = pl.DataFrame({
"1": antidx[i],
"2": antidx[j],})
33 .join(antennas, right_on=
"AntIdx", left_on=
'1', coalesce=
False)
34 .select(pl.all().exclude(
'1',
'2').name.prefix(
"A1_"),
'2')
35 .join(antennas, right_on=
"AntIdx", left_on=
'2', coalesce=
False)
38 pl.all().exclude(
r"^A1_.*$",
'2').name.prefix(
"A2_")
44def generate_MI_antenna_pairs(antennas: pl.DataFrame,
45 phi_sector_group_size: int = 3) -> pl.DataFrame:
46 r"""! Pairs up neighboring antennas in the main instrument.
48 @param[in] antennas Column `AntIdx` is **required**
49 @param[in] phi_sector_group_size (optional) Make sure \f$\geq 1\f$
50 @retval antenna_pairs See the sample output below.
53 * `antennas`: The `AntIdx` column is required, all other columns are optional.
54 See the following examples.
56 [placeholder integers](#calibration.generate_antenna_phase_center_pairs_with_placeholders())
57 as antenna phase center positions, or
58 -# Directly from [qrh.dat](#antenna_attributes.read_MI_antenna_geometry)
60 * `phi_sector_group_size`: Actually this is (roughly) _half_ the group size.
61 Check out the [plots](@ref ant_pair_img) in the file description
62 to see what effect this parameter has on the number of pairs generated.
63 @warning For non-default values, make sure @p phi_sector_group_size \f$\geq 1\f$
65* Example input `antennas`:
67 ┌────────┬──────┬───────────┬────────┬───────┬────────┬───────┬────────────┬──────────┬───────────┐
68 │ AntNum ┆ Ring ┆ PhiSector ┆ AntIdx ┆ X[m] ┆ Y[m] ┆ Z[m] ┆ Pitch[deg] ┆ Yaw[deg] ┆ Roll[deg] │
69 │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
70 │ enum ┆ u8 ┆ u8 ┆ u32 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
71 ╞════════╪══════╪═══════════╪════════╪═══════╪════════╪═══════╪════════════╪══════════╪═══════════╡
72 │ 101 ┆ 1 ┆ 1 ┆ 0 ┆ 1.488 ┆ 0.0 ┆ 5.114 ┆ -10.0 ┆ 0.0 ┆ 0.0 │
73 │ 201 ┆ 2 ┆ 1 ┆ 1 ┆ 2.596 ┆ 0.0 ┆ 1.457 ┆ -10.0 ┆ 0.0 ┆ 0.0 │
74 │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │
75 │ 324 ┆ 3 ┆ 24 ┆ 94 ┆ 2.507 ┆ -0.672 ┆ 0.728 ┆ -10.0 ┆ -15.0 ┆ 0.0 │
76 │ 424 ┆ 4 ┆ 24 ┆ 95 ┆ 2.507 ┆ -0.672 ┆ 0.0 ┆ -10.0 ┆ -15.0 ┆ 0.0 │
77 └────────┴──────┴───────────┴────────┴───────┴────────┴───────┴────────────┴──────────┴───────────┘
79* With default @p phi_sector_group_size and the example input above, the output schema looks like
100 $ A2_Pitch[deg] <f64>
106 from antenna_attributes
import NUM_PHI_SECTORS
107 from scipy.sparse
import diags_array
112 building_block = np.ones((4, 4), dtype=int)
113 if phi_sector_group_size < 1:
114 raise ValueError(
"Group size has to be greater than 0!")
116 dia_idx = np.unique(np.arange(-phi_sector_group_size + 1, phi_sector_group_size) % NUM_PHI_SECTORS)
118 phi_neighbors = diags_array(np.ones(len(dia_idx)), offsets=dia_idx, dtype=int,
119 shape=(NUM_PHI_SECTORS, NUM_PHI_SECTORS)).toarray()
122 antenna_pair_matrix = np.triu(np.kron(phi_neighbors, building_block), 1)
127 pl.DataFrame(np.where(antenna_pair_matrix > 0), schema=[
'1',
'2'])
128 .join(antennas, right_on=
"AntIdx", left_on=
'1', coalesce=
False)
129 .select(pl.all().exclude(
'1',
'2').name.prefix(
"A1_"),
'2')
130 .join(antennas, right_on=
"AntIdx", left_on=
'2', coalesce=
False)
133 pl.all().exclude(
r"^A1_.*$",
'2').name.prefix(
"A2_")
140def __make_plot_for_sanity_check(square_matrix, titlename):
142 import matplotlib.pyplot
as plt
143 plt.rcParams[
"figure.figsize"] = (15, 15)
144 plt.rcParams[
"font.size"] = 9
146 assert (np.shape(square_matrix)[0] == np.shape(square_matrix)[1]),
"Not a square matrix."
148 square_matrix_side_length = np.shape(square_matrix)[0]
149 minor = np.arange(.5, square_matrix_side_length)
150 plt.xticks(np.arange(square_matrix_side_length),
151 np.arange(square_matrix_side_length) + 1, rotation=90)
152 plt.xticks(minor, minor=
True)
153 plt.yticks(np.arange(square_matrix_side_length), np.arange(square_matrix_side_length) + 1)
154 plt.yticks(minor, minor=
True)
155 plt.grid(which=
'minor', color=
'k', alpha=.3,)
156 plt.tick_params(axis=
'both', which=
'both', top=
True, right=
True, bottom=
True, left=
True,
157 labeltop=
True, labelright=
True)
158 plt.imshow(square_matrix, cmap=
'Greys', alpha=.6)
160 plt.savefig(f
"{titlename}.svg")
164if __name__ ==
"__main__":
166 from pathlib
import Path
167 from antenna_attributes
import read_antenna_geometry
169 uncalibrated_antenna_positions: Path = (
170 os.environ.get(
"PUEO_UTIL_INSTALL_DIR") / Path(
"share/pueo/geometry/aug25/qrh.dat")
173 antennas: pl.DataFrame = read_antenna_geometry(uncalibrated_antenna_positions)
177 bad = antennas.select(pl.all().exclude(
"AntIdx"))
178 generate_LF_antenna_pairs(antennas=bad)
179 except pl.exceptions.ColumnNotFoundError:
180 print(
"Column AntIdx is required, so this should fail!")
182 print(
"On the other hand, keeping only AntIdx is okay:")
183 good = antennas.select(
"AntIdx")
184 print(generate_MI_antenna_pairs(antennas=good))