Antenna Classification Daily Summary¶

by Josh Dillon last updated June 19, 2023

This notebook parses and summarizes the output of the file_calibration notebook to produce a report on per-antenna malfunctions on a daily basis.

Quick links:

• Summary of Per Antenna Issues¶

• Figure 1: Per File Overall Antenna Classification Summary¶

• Figure 2: Per Classifier Antenna Flagging Summary¶

• Figure 3: Array Visualization of Overall Daily Classification¶

In [1]:
import os
os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE'
import h5py
import hdf5plugin  # REQUIRED to have the compression plugins available
import numpy as np
import pandas as pd
import glob
import os
import matplotlib.pyplot as plt
from hera_cal import io, utils
from hera_qm import ant_class
from uvtools.plot import plot_antpos, plot_antclass
%matplotlib inline
from IPython.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))
_ = np.seterr(all='ignore')  # get rid of red warnings
%config InlineBackend.figure_format = 'retina'

Settings¶

In [2]:
# Parse settings from environment
ANT_CLASS_FOLDER = os.environ.get("ANT_CLASS_FOLDER", "./")
SUM_FILE = os.environ.get("SUM_FILE", None)
# ANT_CLASS_FOLDER = "/mnt/sn1/2460330"
# SUM_FILE = "/mnt/sn1/2460330/zen.2460330.25463.sum.uvh5"
OC_SKIP_OUTRIGGERS = os.environ.get("OC_SKIP_OUTRIGGERS", "TRUE").upper() == "TRUE"

for param in ['ANT_CLASS_FOLDER', 'SUM_FILE', 'OC_SKIP_OUTRIGGERS']:
    print(f"{param} = '{eval(param)}'")
ANT_CLASS_FOLDER = '/mnt/sn1/data2/2460704'
SUM_FILE = '/mnt/sn1/data2/2460704/zen.2460704.45926.sum.uvh5'
OC_SKIP_OUTRIGGERS = 'True'
In [3]:
if SUM_FILE is not None:
    from astropy.time import Time, TimeDelta
    utc = Time(float(SUM_FILE.split('zen.')[-1].split('.sum.uvh5')[0]), format='jd').datetime
    print(f'Date: {utc.month}-{utc.day}-{utc.year}')
Date: 1-28-2025
In [4]:
# set thresholds for fraction of the day
overall_thresh = .1
all_zero_thresh = .1
eo_zeros_thresh = .1
xengine_diff_thresh = .1
cross_pol_thresh = .5
bad_fem_thresh = .1
high_power_thresh = .1
low_power_thresh = .1
low_corr_thresh = .1
bad_shape_thresh = .5
excess_rfi_thresh = .1
chisq_thresh = .25

Load classifications and other metadata¶

In [5]:
# Load csvs
csv_files = sorted(glob.glob(os.path.join(ANT_CLASS_FOLDER, '*.ant_class.csv')))
jds = [float(f.split('/')[-1].split('zen.')[-1].split('.sum')[0]) for f in csv_files]
tables = [pd.read_csv(f).dropna(axis=0, how='all') for f in csv_files]
table_cols = tables[0].columns[1::2]
class_cols = tables[0].columns[2::2]
print(f'Found {len(csv_files)} csv files starting with {csv_files[0]}')
Found 1851 csv files starting with /mnt/sn1/data2/2460704/zen.2460704.25234.sum.ant_class.csv
In [6]:
# parse ant_strings
ap_strs = np.array(tables[0]['Antenna'])
ants = sorted(set(int(a[:-1]) for a in ap_strs))
translator = ''.maketrans('e', 'n') | ''.maketrans('n', 'e')
In [7]:
# get node numbers
node_dict = {ant: 'Unknown' for ant in ants}
try:
    from hera_mc import cm_hookup
    hookup = cm_hookup.get_hookup('default')
    for ant_name in hookup:
        ant = int("".join(filter(str.isdigit, ant_name)))
        if ant in node_dict:
            if hookup[ant_name].get_part_from_type('node')['E<ground'] is not None:
                node_dict[ant] = int(hookup[ant_name].get_part_from_type('node')['E<ground'][1:])
except:
    pass
nodes = sorted(set(node_dict.values()))
In [8]:
def classification_array(col):
    class_array = np.vstack([t[col] for t in tables])
    class_array[class_array == 'good'] = 1.7
    class_array[class_array == 'suspect'] = 1
    class_array[class_array == 'bad'] = 0
    return class_array.astype(float)
In [9]:
if SUM_FILE is not None:
    hd = io.HERADataFastReader(SUM_FILE)
    ap_tuples = [(int(ap[:-1]), {'e': 'Jee', 'n': 'Jnn'}[ap[-1]]) for ap in ap_strs]
    bad_bools = np.mean(classification_array('Antenna Class') == 0, axis=0) > overall_thresh
    bad_aps = [ap_tuples[i] for i in np.arange(len(ap_tuples))[bad_bools]]
    suspect_bools = np.mean(classification_array('Antenna Class') == 1, axis=0) > overall_thresh
    suspect_aps = [ap_tuples[i] for i in np.arange(len(ap_tuples))[suspect_bools] if ap_tuples[i] not in bad_aps]
    good_aps = [ap for ap in ap_tuples if ap not in bad_aps and ap not in suspect_aps]
    overall_class = ant_class.AntennaClassification(bad=bad_aps, suspect=suspect_aps, good=good_aps)
    autos, _, _ = hd.read(bls=[bl for bl in hd.bls if utils.split_bl(bl)[0] == utils.split_bl(bl)[1]], read_flags=False, read_nsamples=False)
    avg_unflagged_auto = {}
    for pol in ['ee', 'nn']:
        unflagged_autos = [autos[bl] for bl in autos if bl[2] == pol and overall_class[utils.split_bl(bl)[0]] != 'bad']
        if len(unflagged_autos) > 0:
            avg_unflagged_auto[pol] = np.mean(unflagged_autos, axis=(0, 1))
        else:
            avg_unflagged_auto[pol] = np.zeros(len(hd.freqs), dtype=complex)

Figure out and summarize per-antenna issues¶

In [10]:
def print_issue_summary(bad_ant_strs, title, notes='', plot=False):
    '''Print report for list of bad antenna polarizations strings'''
    unique_bad_antnums = [int(ap[:-1]) for ap in bad_ant_strs]
    display(HTML(f'<h2>{title}: ({len(bad_ant_strs)} antpols across {len(set([ba[:-1] for ba in bad_ant_strs]))} antennas)</h2>'))
    if len(notes) > 0:
        display(HTML(f'<h4>{notes}</h4>'))
    if len(bad_ant_strs) > 0:
        print(f'All Bad Antpols: {", ".join(bad_ant_strs)}\n')
    for node in nodes:
        if np.any([node == node_dict[a] for a in unique_bad_antnums]):
            aps = [ap for ap in bad_ant_strs if node_dict[int(ap[:-1])] == node]
            whole_ants = [str(wa) for wa in set([int(ap[:-1]) for ap in aps if ap.translate(translator) in bad_ant_strs])]
            single_pols =  [ap for ap in aps if ap.translate(translator) not in bad_ant_strs]
            print(f'Node {node}:')
            print(f'\tAntpols ({len(aps)} total): {", ".join(aps)}')
            print(f'\tWhole Ants ({len(whole_ants)} total): {", ".join(whole_ants)}')
            print(f'\tSingle Pols ({len(single_pols)} total): {", ".join(single_pols)}')
            if plot and SUM_FILE is not None:
                fig, axes = plt.subplots(1, 2, figsize=(12,4), dpi=70, sharey=True, gridspec_kw={'wspace': 0})
                for ax, pol in zip(axes, ['ee', 'nn']):                    
                    ax.semilogy(autos.freqs / 1e6, avg_unflagged_auto[pol], 'k--', label='Average\nUnflagged\nAuto')
                    for ap in aps:
                        ant = int(ap[:-1]), utils.comply_pol(ap[-1])
                        auto_bl = utils.join_bl(ant, ant)
                        if auto_bl[2] == pol:
                            ax.semilogy(autos.freqs / 1e6, np.mean(autos[auto_bl], axis=0), label=ap)
                    ax.legend()
                    ax.set_xlim([40, 299])
                    ax.set_title(f'{title} on Node {node} ({pol}-antennas)')
                    ax.set_xlabel('Frequency (MHz)')
                axes[0].set_ylabel('Single File Raw Autocorrelation')
                plt.tight_layout()
                plt.show() 
In [11]:
# precompute various helpful quantities
all_slopes = np.vstack([t['Autocorr Slope'] for t in tables])
median_slope = np.median(all_slopes)
bad_slopes = np.vstack([t['Autocorr Slope Class'] for t in tables]) == 'bad'
suspect_slopes = np.vstack([t['Autocorr Slope Class'] for t in tables]) == 'suspect'
bad_shapes = np.vstack([t['Autocorr Shape Class'] for t in tables]) == 'bad'
suspect_shapes = np.vstack([t['Autocorr Shape Class'] for t in tables]) == 'suspect'
all_powers = np.vstack([t['Autocorr Power'] for t in tables])
median_power = np.median(all_powers)
bad_powers = np.vstack([t['Autocorr Power Class'] for t in tables]) == 'bad'
suspect_powers = np.vstack([t['Autocorr Power Class'] for t in tables]) == 'suspect'
bad_rfi = np.vstack([t['Auto RFI RMS Class'] for t in tables]) == 'bad'
suspect_rfi = np.vstack([t['Auto RFI RMS Class'] for t in tables]) == 'suspect'
In [12]:
# find all zeros
all_zeros_strs = ap_strs[np.mean(np.vstack([t['Dead? Class'] for t in tables]) == 'bad', axis=0) > all_zero_thresh]
In [13]:
# find even/odd zeros
eo_zeros_strs = ap_strs[np.mean(np.vstack([t['Even/Odd Zeros Class'] for t in tables]) == 'bad', axis=0) > eo_zeros_thresh]
eo_zeros_strs = [ap for ap in eo_zeros_strs if ap not in all_zeros_strs] 
In [14]:
# find cross-polarized antennas
cross_pol_strs = ap_strs[np.mean(np.vstack([t['Cross-Polarized Class'] for t in tables]) == 'bad', axis=0) > cross_pol_thresh]
cross_pol_strs = [ap for ap in cross_pol_strs if ap not in all_zeros_strs] 
In [15]:
# find FEM power issues: must be low power, high slope, and bad or suspect in power, slope, rfi, and shape
fem_off_prod = (bad_powers + .5 * suspect_powers) * (bad_slopes + .5 * suspect_slopes)
fem_off_prod *= (bad_rfi + .5 * suspect_rfi) * (bad_shapes + .5 * suspect_shapes)
fem_off_strs = ap_strs[np.mean(fem_off_prod * (all_powers < median_power) * (all_slopes > median_slope), axis=0) > .1]
In [16]:
# find high power issues
high_power_strs = ap_strs[np.mean(bad_powers & (all_powers > median_power), axis=0) > high_power_thresh]
In [17]:
# find other low power issues
low_power_strs = ap_strs[np.mean(bad_powers & (all_powers < median_power), axis=0) > low_power_thresh]
low_power_strs = [ap for ap in low_power_strs if ap not in all_zeros_strs and ap not in fem_off_strs] 
In [18]:
# find low correlation (but not low power)
low_corr_strs = ap_strs[np.mean(np.vstack([t['Low Correlation Class'] for t in tables]) == 'bad', axis=0) > low_corr_thresh]
low_corr_strs = [ap for ap in low_corr_strs if ap not in (set(low_power_strs) | set(all_zeros_strs) | set(fem_off_strs))] 
In [19]:
# find bad bandpasses
bad_bandpass_strs = ap_strs[np.mean(bad_shapes, axis=0) > bad_shape_thresh]
bad_bandpass_strs = [ap for ap in bad_bandpass_strs if ap not in (set(low_power_strs) | set(all_zeros_strs) | set(high_power_strs) | set(fem_off_strs))]
In [20]:
# find antennas with excess RFI
excess_rfi_strs = ap_strs[np.mean(np.vstack([t['Auto RFI RMS Class'] for t in tables]) == 'bad', axis=0) > excess_rfi_thresh]
excess_rfi_strs = [ap for ap in excess_rfi_strs if ap not in (set(low_power_strs) | set(all_zeros_strs) |  set(fem_off_strs) |
                                                              set(bad_bandpass_strs) | set(high_power_strs))] 
In [21]:
# find bad x-engine diffs
xengine_diff_strs = ap_strs[np.mean(np.vstack([t['Bad Diff X-Engines Class'] for t in tables]) == 'bad', axis=0) > xengine_diff_thresh]
xengine_diff_strs = [ap for ap in xengine_diff_strs if ap not in (set(bad_bandpass_strs) | set(low_power_strs) | set(excess_rfi_strs) | set(low_corr_strs) |
                                                                  set(all_zeros_strs) | set(high_power_strs) | set(fem_off_strs) | set(eo_zeros_strs))]
In [22]:
# find antennas with high redcal chi^2
chisq_strs = ap_strs[np.mean(np.vstack([t['Redcal chi^2 Class'] for t in tables]) == 'bad', axis=0) > chisq_thresh]
chisq_strs = [ap for ap in chisq_strs if ap not in (set(bad_bandpass_strs) | set(low_power_strs) | set(excess_rfi_strs) | set(low_corr_strs) |
                                                    set(all_zeros_strs) | set(high_power_strs) | set(fem_off_strs) | set(eo_zeros_strs) | set(xengine_diff_strs))]
if OC_SKIP_OUTRIGGERS:
    chisq_strs = [ap for ap in chisq_strs if int(ap[:-1]) < 320]
In [23]:
# collect all results
to_print = [(all_zeros_strs, 'All-Zeros', 'These antennas have visibilities that are more than half zeros.'),
            (eo_zeros_strs, 'Excess Zeros in Either Even or Odd Spectra', 
             'These antennas are showing evidence of packet loss or X-engine failure.', True),
            (xengine_diff_strs, 'Excess Power in X-Engine Diffs', 
             'These antennas are showing evidence of mis-written packets in either the evens or the odds.', True),            
            (cross_pol_strs, 'Cross-Polarized', 'These antennas have their east and north cables swapped.'),
            (fem_off_strs, 'Likely FEM Power Issue', 'These antennas have low power and anomolously high slopes.', True),
            (high_power_strs, 'High Power', 'These antennas have high median power.', True),
            (low_power_strs, 'Other Low Power Issues', 'These antennas have low power, but are not all-zeros and not FEM off.', True),
            (low_corr_strs, 'Low Correlation, But Not Low Power', 'These antennas are low correlation, but their autocorrelation power levels look OK.'),
            (bad_bandpass_strs, 'Bad Bandpass Shapes, But Not Bad Power', 
             'These antennas have unusual bandpass shapes, but are not all-zeros, high power, low power, or FEM off.', True),
            (excess_rfi_strs, 'Excess RFI', 'These antennas have excess RMS after DPSS filtering (likely RFI), but not low or high power or a bad bandpass.', True),
            (chisq_strs, 'Redcal chi^2', 'These antennas have been idenfied as not redundantly calibrating well, even after passing the above checks.')]
In [24]:
def print_high_level_summary():
    for tp in sorted(to_print, key=lambda x: len(x[0]), reverse=True):
        print(f'{len(tp[0])} antpols (on {len(set([ap[:-1] for ap in tp[0]]))} antennas) frequently flagged for {tp[1]}.')
        
def print_all_issue_summaries():
    for tp in to_print:
        print_issue_summary(*tp)

Summary of Per-Antenna Issues¶

In [25]:
print_high_level_summary()
237 antpols (on 155 antennas) frequently flagged for Excess Power in X-Engine Diffs.
45 antpols (on 42 antennas) frequently flagged for Excess RFI.
42 antpols (on 21 antennas) frequently flagged for All-Zeros.
35 antpols (on 23 antennas) frequently flagged for Likely FEM Power Issue.
30 antpols (on 19 antennas) frequently flagged for Low Correlation, But Not Low Power.
30 antpols (on 27 antennas) frequently flagged for Redcal chi^2.
17 antpols (on 12 antennas) frequently flagged for Other Low Power Issues.
13 antpols (on 12 antennas) frequently flagged for Bad Bandpass Shapes, But Not Bad Power.
2 antpols (on 2 antennas) frequently flagged for High Power.
0 antpols (on 0 antennas) frequently flagged for Excess Zeros in Either Even or Odd Spectra.
0 antpols (on 0 antennas) frequently flagged for Cross-Polarized.
In [26]:
print_all_issue_summaries()

All-Zeros: (42 antpols across 21 antennas)

These antennas have visibilities that are more than half zeros.

All Bad Antpols: 22e, 22n, 34e, 34n, 35e, 35n, 47e, 47n, 61e, 61n, 63e, 63n, 64e, 64n, 77e, 77n, 78e, 78n, 88e, 88n, 90e, 90n, 107e, 107n, 147e, 147n, 148e, 148n, 149e, 149n, 179e, 179n, 241e, 241n, 242e, 242n, 243e, 243n, 329e, 329n, 333e, 333n

Node 6:
	Antpols (18 total): 22e, 22n, 34e, 34n, 35e, 35n, 47e, 47n, 61e, 61n, 63e, 63n, 64e, 64n, 77e, 77n, 78e, 78n
	Whole Ants (9 total): 64, 34, 35, 77, 78, 47, 22, 61, 63
	Single Pols (0 total): 
Node 9:
	Antpols (6 total): 88e, 88n, 90e, 90n, 107e, 107n
	Whole Ants (3 total): 88, 90, 107
	Single Pols (0 total): 
Node 12:
	Antpols (6 total): 179e, 179n, 329e, 329n, 333e, 333n
	Whole Ants (3 total): 329, 179, 333
	Single Pols (0 total): 
Node 15:
	Antpols (6 total): 147e, 147n, 148e, 148n, 149e, 149n
	Whole Ants (3 total): 147, 148, 149
	Single Pols (0 total): 
Node 19:
	Antpols (6 total): 241e, 241n, 242e, 242n, 243e, 243n
	Whole Ants (3 total): 241, 242, 243
	Single Pols (0 total): 

Excess Zeros in Either Even or Odd Spectra: (0 antpols across 0 antennas)

These antennas are showing evidence of packet loss or X-engine failure.

Excess Power in X-Engine Diffs: (237 antpols across 155 antennas)

These antennas are showing evidence of mis-written packets in either the evens or the odds.

All Bad Antpols: 3e, 3n, 4e, 4n, 5e, 5n, 15e, 16e, 16n, 17e, 17n, 18e, 28n, 29e, 29n, 30e, 30n, 40e, 41e, 41n, 42e, 54e, 55n, 56e, 56n, 57e, 57n, 69n, 70e, 70n, 71e, 71n, 79n, 80n, 81e, 81n, 82e, 82n, 83e, 83n, 84e, 84n, 85e, 85n, 86n, 87n, 89e, 89n, 91e, 91n, 92n, 94e, 94n, 96e, 96n, 97e, 98e, 99e, 99n, 100e, 101e, 101n, 102n, 103e, 103n, 105e, 105n, 106n, 108e, 109e, 110e, 110n, 111e, 111n, 112e, 112n, 115e, 115n, 116e, 116n, 117e, 117n, 118n, 119e, 119n, 122e, 122n, 123e, 123n, 124n, 125e, 125n, 126n, 127e, 127n, 128e, 128n, 129e, 129n, 130e, 131n, 132n, 133e, 133n, 134n, 135n, 136e, 137n, 138e, 138n, 139n, 140e, 140n, 141e, 141n, 142e, 143e, 143n, 144n, 145e, 145n, 146e, 150e, 150n, 151n, 152e, 152n, 153e, 153n, 154n, 155n, 159e, 159n, 160e, 160n, 162e, 162n, 163e, 163n, 164e, 164n, 165e, 165n, 166e, 166n, 167e, 167n, 168e, 168n, 169e, 169n, 170n, 171e, 172n, 173e, 173n, 174e, 175e, 175n, 176n, 177e, 177n, 178e, 178n, 180e, 181e, 181n, 183e, 183n, 184n, 185e, 185n, 186e, 186n, 187n, 188e, 190e, 190n, 191e, 191n, 193n, 194e, 195n, 196n, 197e, 197n, 204e, 204n, 206e, 206n, 207n, 208n, 209e, 209n, 210e, 210n, 213n, 214e, 216e, 216n, 217n, 221e, 221n, 223e, 223n, 226n, 227e, 227n, 228n, 229n, 232n, 234n, 235e, 237e, 237n, 239e, 244e, 244n, 251n, 252n, 253e, 256e, 256n, 261n, 266e, 266n, 267n, 270n, 281n, 283n, 285n, 295e, 295n, 325n, 331n, 336e, 336n

Node 1:
	Antpols (17 total): 3e, 3n, 4e, 4n, 5e, 5n, 15e, 16e, 16n, 17e, 17n, 18e, 28n, 29e, 29n, 30e, 30n
	Whole Ants (7 total): 3, 4, 5, 16, 17, 29, 30
	Single Pols (3 total): 15e, 18e, 28n
Casting complex values to real discards the imaginary part
Casting complex values to real discards the imaginary part
No description has been provided for this image
Node 4:
	Antpols (15 total): 40e, 41e, 41n, 42e, 54e, 55n, 56e, 56n, 57e, 57n, 69n, 70e, 70n, 71e, 71n
	Whole Ants (5 total): 70, 71, 41, 56, 57
	Single Pols (5 total): 40e, 42e, 54e, 55n, 69n
No description has been provided for this image
Node 7:
	Antpols (20 total): 81e, 81n, 82e, 82n, 83e, 83n, 98e, 99e, 99n, 100e, 116e, 116n, 117e, 117n, 118n, 119e, 119n, 137n, 138e, 138n
	Whole Ants (8 total): 99, 138, 81, 82, 83, 116, 117, 119
	Single Pols (4 total): 98e, 100e, 118n, 137n
No description has been provided for this image
Node 8:
	Antpols (15 total): 84e, 84n, 85e, 85n, 86n, 87n, 101e, 101n, 102n, 103e, 103n, 122e, 122n, 123e, 123n
	Whole Ants (6 total): 101, 103, 84, 85, 122, 123
	Single Pols (3 total): 86n, 87n, 102n
No description has been provided for this image
Node 9:
	Antpols (13 total): 89e, 89n, 91e, 91n, 105e, 105n, 106n, 108e, 124n, 125e, 125n, 126n, 325n
	Whole Ants (4 total): 89, 91, 125, 105
	Single Pols (5 total): 106n, 108e, 124n, 126n, 325n
No description has been provided for this image
Node 10:
	Antpols (17 total): 92n, 94e, 94n, 109e, 110e, 110n, 111e, 111n, 112e, 112n, 127e, 127n, 128e, 128n, 129e, 129n, 130e
	Whole Ants (7 total): 128, 129, 110, 111, 112, 94, 127
	Single Pols (3 total): 92n, 109e, 130e
No description has been provided for this image
Node 11:
	Antpols (12 total): 79n, 80n, 96e, 96n, 97e, 115e, 115n, 131n, 132n, 133e, 133n, 134n
	Whole Ants (3 total): 96, 115, 133
	Single Pols (6 total): 79n, 80n, 97e, 131n, 132n, 134n
No description has been provided for this image
Node 12:
	Antpols (8 total): 135n, 136e, 155n, 176n, 177e, 177n, 178e, 178n
	Whole Ants (2 total): 177, 178
	Single Pols (4 total): 135n, 136e, 155n, 176n
No description has been provided for this image
Node 13:
	Antpols (17 total): 139n, 140e, 140n, 141e, 141n, 142e, 159e, 159n, 160e, 160n, 162e, 162n, 180e, 181e, 181n, 183e, 183n
	Whole Ants (7 total): 160, 162, 140, 141, 181, 183, 159
	Single Pols (3 total): 139n, 142e, 180e
No description has been provided for this image
Node 14:
	Antpols (20 total): 143e, 143n, 144n, 145e, 145n, 146e, 163e, 163n, 164e, 164n, 165e, 165n, 166e, 166n, 184n, 185e, 185n, 186e, 186n, 187n
	Whole Ants (8 total): 163, 164, 165, 166, 143, 145, 185, 186
	Single Pols (4 total): 144n, 146e, 184n, 187n
No description has been provided for this image
Node 15:
	Antpols (14 total): 150e, 150n, 167e, 167n, 168e, 168n, 169e, 169n, 170n, 188e, 190e, 190n, 191e, 191n
	Whole Ants (6 total): 167, 168, 169, 150, 190, 191
	Single Pols (2 total): 170n, 188e
No description has been provided for this image
Node 16:
	Antpols (14 total): 151n, 152e, 152n, 153e, 153n, 154n, 171e, 172n, 173e, 173n, 174e, 193n, 194e, 213n
	Whole Ants (3 total): 152, 153, 173
	Single Pols (8 total): 151n, 154n, 171e, 172n, 174e, 193n, 194e, 213n
No description has been provided for this image
Node 17:
	Antpols (8 total): 196n, 197e, 197n, 216e, 216n, 217n, 234n, 235e
	Whole Ants (2 total): 216, 197
	Single Pols (4 total): 196n, 217n, 234n, 235e
No description has been provided for this image
Node 18:
	Antpols (5 total): 221e, 221n, 237e, 237n, 239e
	Whole Ants (2 total): 237, 221
	Single Pols (1 total): 239e
No description has been provided for this image
Node 19:
	Antpols (8 total): 204e, 204n, 206e, 206n, 207n, 223e, 223n, 226n
	Whole Ants (3 total): 204, 206, 223
	Single Pols (2 total): 207n, 226n
No description has been provided for this image
Node 20:
	Antpols (12 total): 208n, 209e, 209n, 210e, 210n, 227e, 227n, 228n, 229n, 244e, 244n, 261n
	Whole Ants (4 total): 209, 210, 227, 244
	Single Pols (4 total): 208n, 228n, 229n, 261n
No description has been provided for this image
Node 21:
	Antpols (8 total): 175e, 175n, 195n, 214e, 232n, 331n, 336e, 336n
	Whole Ants (2 total): 336, 175
	Single Pols (4 total): 195n, 214e, 232n, 331n
No description has been provided for this image
Node 22:
	Antpols (10 total): 251n, 252n, 253e, 266e, 266n, 267n, 281n, 283n, 295e, 295n
	Whole Ants (2 total): 266, 295
	Single Pols (6 total): 251n, 252n, 253e, 267n, 281n, 283n
No description has been provided for this image
Node 23:
	Antpols (4 total): 256e, 256n, 270n, 285n
	Whole Ants (1 total): 256
	Single Pols (2 total): 270n, 285n
No description has been provided for this image

Cross-Polarized: (0 antpols across 0 antennas)

These antennas have their east and north cables swapped.

Likely FEM Power Issue: (35 antpols across 23 antennas)

These antennas have low power and anomolously high slopes.

All Bad Antpols: 7e, 7n, 8e, 8n, 9e, 9n, 10e, 10n, 15n, 19e, 19n, 20e, 20n, 21e, 21n, 31e, 31n, 32n, 33e, 33n, 68e, 68n, 109n, 120e, 170e, 182e, 200e, 218e, 238n, 320n, 321n, 323e, 323n, 332e, 332n

Node 1:
	Antpols (1 total): 15n
	Whole Ants (0 total): 
	Single Pols (1 total): 15n
Data has no positive values, and therefore cannot be log-scaled.
No description has been provided for this image
Node 2:
	Antpols (22 total): 7e, 7n, 8e, 8n, 9e, 9n, 10e, 10n, 19e, 19n, 20e, 20n, 21e, 21n, 31e, 31n, 32n, 33e, 33n, 321n, 323e, 323n
	Whole Ants (10 total): 33, 323, 7, 8, 9, 10, 19, 20, 21, 31
	Single Pols (2 total): 32n, 321n
No description has been provided for this image
Node 3:
	Antpols (3 total): 68e, 68n, 320n
	Whole Ants (1 total): 68
	Single Pols (1 total): 320n
No description has been provided for this image
Node 8:
	Antpols (1 total): 120e
	Whole Ants (0 total): 
	Single Pols (1 total): 120e
No description has been provided for this image
Node 10:
	Antpols (1 total): 109n
	Whole Ants (0 total): 
	Single Pols (1 total): 109n
No description has been provided for this image
Node 13:
	Antpols (1 total): 182e
	Whole Ants (0 total): 
	Single Pols (1 total): 182e
No description has been provided for this image
Node 15:
	Antpols (1 total): 170e
	Whole Ants (0 total): 
	Single Pols (1 total): 170e
No description has been provided for this image
Node 17:
	Antpols (1 total): 218e
	Whole Ants (0 total): 
	Single Pols (1 total): 218e
No description has been provided for this image
Node 18:
	Antpols (2 total): 200e, 238n
	Whole Ants (0 total): 
	Single Pols (2 total): 200e, 238n
No description has been provided for this image
Node 21:
	Antpols (2 total): 332e, 332n
	Whole Ants (1 total): 332
	Single Pols (0 total): 
No description has been provided for this image

High Power: (2 antpols across 2 antennas)

These antennas have high median power.

All Bad Antpols: 201n, 232e

Node 18:
	Antpols (1 total): 201n
	Whole Ants (0 total): 
	Single Pols (1 total): 201n
No description has been provided for this image
Node 21:
	Antpols (1 total): 232e
	Whole Ants (0 total): 
	Single Pols (1 total): 232e
No description has been provided for this image

Other Low Power Issues: (17 antpols across 12 antennas)

These antennas have low power, but are not all-zeros and not FEM off.

All Bad Antpols: 32e, 48e, 48n, 49e, 49n, 62e, 62n, 100n, 104n, 114e, 114n, 137e, 182n, 218n, 251e, 262e, 262n

Node 2:
	Antpols (1 total): 32e
	Whole Ants (0 total): 
	Single Pols (1 total): 32e
No description has been provided for this image
Node 6:
	Antpols (6 total): 48e, 48n, 49e, 49n, 62e, 62n
	Whole Ants (3 total): 48, 49, 62
	Single Pols (0 total):