_array[iblt, ifreq, ipol], visdata_dset, indices
                                )
                            else:
                                visdata_dset[blt_idx, freq_idx, pol_idx] = data_array[
                                    iblt, ifreq, ipol
                                ]
                            flags_dset[blt_idx, freq_idx, pol_idx] = flag_array[
                                iblt, ifreq, ipol
                            ]
                            nsamples_dset[blt_idx, freq_idx, pol_idx] = nsample_array[
                                iblt, ifreq, ipol
                            ]

            # append to history if desired
            if add_to_history is not None:
                history = np.string_(self.history) + np.string_(add_to_history)
                if "history" in f["Header"]:
                    # erase dataset first b/c it has fixed-length string datatype
                    del f["Header"]["history"]
                f["Header"]["history"] = np.string_(history)

        if revert_fas:
            with warnings.catch_warnings():
                warnings.filterwarnings(
                    "ignore",
                    message="This method will be removed in version 3.0 when the "
                    "current array shapes are no longer supported.",
                )
                self.use_current_array_shapes()

        return


# pyuvdata/uvdata/tests/__init__.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2019 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License


# pyuvdata/uvdata/tests/conftest.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2021 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""pytest fixtures for UVData tests."""
import os

import pytest

import pyuvdata.tests as uvtest
from pyuvdata import UVData
from pyuvdata.data import DATA_PATH
from pyuvdata.uvdata.mir_parser import MirParser

casa_tutorial_uvfits = os.path.join(
    DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits"
)
paper_miriad_file = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")


@pytest.fixture(scope="session")
def casa_uvfits_main():
    """Read in CASA tutorial uvfits file."""
    uv_in = UVData()
    with uvtest.check_warnings(
        UserWarning,
        [
            "Telescope EVLA is not in known_telescopes",
            "The uvw_array does not match the expected values",
        ],
    ):
        uv_in.read(casa_tutorial_uvfits, use_future_array_shapes=True)

    yield uv_in

    # cleanup
    del uv_in


@pytest.fixture(scope="function")
def casa_uvfits(casa_uvfits_main):
    """Make function level CASA tutorial uvfits object."""
    casa_uvfits = casa_uvfits_main.copy()
    yield casa_uvfits

    # clean up when done
    del casa_uvfits

    return


@pytest.fixture(scope="session")
def hera_uvh5_main():
    # read in test file for the resampling in time functions
    uv_object = UVData()
    testfile = os.path.join(DATA_PATH, "zen.2458661.23480.HH.uvh5")
    uv_object.read(testfile, use_future_array_shapes=True)

    yield uv_object

    # cleanup
    del uv_object


@pytest.fixture(scope="function")
def hera_uvh5(hera_uvh5_main):
    # read in test file for the resampling in time functions
    uv_object = hera_uvh5_main.copy()

    yield uv_object

    # cleanup
    del uv_object

    return


@pytest.fixture(scope="session")
def paper_miriad_main():
    """Read in PAPER miriad file."""
    pytest.importorskip("pyuvdata.uvdata.aipy_extracts")
    uv_in = UVData()
    uv_in.read(paper_miriad_file, use_future_array_shapes=True)

    yield uv_in

    # cleanup
    del uv_in


@pytest.fixture(scope="function")
def paper_miriad(paper_miriad_main):
    """Make function level PAPER miriad object."""
    uv_in = paper_miriad_main.copy()

    yield uv_in

    # cleanup
    del uv_in


@pytest.fixture(scope="session")
def sma_mir_main():
    # read in test file for the resampling in time functions
    uv_object = UVData()
    testfile = os.path.join(DATA_PATH, "sma_test.mir")
    uv_object.read(testfile, use_future_array_shapes=True)

    yield uv_object


@pytest.fixture(scope="function")
def sma_mir(sma_mir_main):
    # read in test file for the resampling in time functions
    uv_object = sma_mir_main.copy()

    yield uv_object


@pytest.fixture(scope="session")
def mir_data_main():
    testfile = os.path.join(DATA_PATH, "sma_test.mir")
    mir_data = MirParser(testfile, load_cross=True, load_auto=True, has_auto=True)

    yield mir_data


@pytest.fixture(scope="function")
def mir_data(mir_data_main):
    mir_data = mir_data_main.copy()

    yield mir_data


@pytest.fixture(scope="session")
def uv_phase_comp_main():
    file1 = os.path.join(DATA_PATH, "1133866760.uvfits")
    file2 = os.path.join(DATA_PATH, "1133866760_rephase.uvfits")
    # These files came from an external source, don't want to rewrite them, so use
    # checkwarnings to capture the warning about non-real autos
    with uvtest.check_warnings(
        UserWarning,
        match=[
            "Fixing auto-correlations to be be real-only, after some imaginary "
            "values were detected in data_array."
        ]
        * 2,
    ):
        uvd1 = UVData.from_file(file1, use_future_array_shapes=True)
        uvd2 = UVData.from_file(file2, use_future_array_shapes=True)

    yield uvd1, uvd2


@pytest.fixture(scope="function")
def uv_phase_comp(uv_phase_comp_main):
    uvd1, uvd2 = uv_phase_comp_main
    uvd1_copy = uvd1.copy()
    uvd2_copy = uvd2.copy()

    yield uvd1_copy, uvd2_copy


# pyuvdata/uvdata/tests/test_aipy_extracts.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for aipy_extracts

"""
import os
import shutil

import numpy as np
import pytest

from pyuvdata.data import DATA_PATH

aipy_extracts = pytest.importorskip("pyuvdata.uvdata.aipy_extracts")


def test_bl2ij():
    """Test bl2ij function"""
    # small baseline number
    bl = 258
    assert aipy_extracts.bl2ij(bl)[0] == 0
    assert aipy_extracts.bl2ij(bl)[1] == 1

    # large baseline number
    bl = 67587
    assert aipy_extracts.bl2ij(bl)[0] == 0
    assert aipy_extracts.bl2ij(bl)[1] == 2
    return


def test_ij2bl():
    """Test ij2bl function"""
    # test < 256 antennas
    i = 1
    j = 2
    assert aipy_extracts.ij2bl(i, j) == 515

    # test > 256 antennas
    i = 2
    j = 257
    assert aipy_extracts.ij2bl(i, j) == 71938

    # test case where i > j
    i = 257
    j = 2
    assert aipy_extracts.ij2bl(i, j) == 71938
    return


def test_parse_ants():
    """Test parsing ant strings to tuples"""
    nants = 4
    cases = {
        "all": [],
        "auto": [("auto", 1)],
        "cross": [("auto", 0)],
        "-auto": [("auto", 0)],
        "-cross": [("auto", 1)],
        "all,cross": [("auto", 0)],  # Masks are and'd, should be equal to just cross
        "0_1": [(aipy_extracts.ij2bl(0, 1), 1)],
        "0_1,1_2": [(aipy_extracts.ij2bl(0, 1), 1), (aipy_extracts.ij2bl(1, 2), 1)],
        "0x_1x": [(aipy_extracts.ij2bl(0, 1), 1, "xx")],
        "0_1x": [
            (aipy_extracts.ij2bl(0, 1), 1, "xx"),
            (aipy_extracts.ij2bl(0, 1), 1, "yx"),
        ],
        "0y_1": [
            (aipy_extracts.ij2bl(0, 1), 1, "yx"),
            (aipy_extracts.ij2bl(0, 1), 1, "yy"),
        ],
        "(0x,0y)_1x": [
            (aipy_extracts.ij2bl(0, 1), 1, "xx"),
            (aipy_extracts.ij2bl(0, 1), 1, "yx"),
        ],
        "(0,1)_2": [(aipy_extracts.ij2bl(0, 2), 1), (aipy_extracts.ij2bl(1, 2), 1)],
        "0_(1,2)": [(aipy_extracts.ij2bl(0, 1), 1), (aipy_extracts.ij2bl(0, 2), 1)],
        "(0,1)_(2,3)": [
            (aipy_extracts.ij2bl(0, 2), 1),
            (aipy_extracts.ij2bl(0, 3), 1),
            (aipy_extracts.ij2bl(1, 2), 1),
            (aipy_extracts.ij2bl(1, 3), 1),
        ],
        "0_(1,-2)": [(aipy_extracts.ij2bl(0, 1), 1), (aipy_extracts.ij2bl(0, 2), 0)],
        "(-0,1)_(2,-3)": [
            (aipy_extracts.ij2bl(0, 2), 0),
            (aipy_extracts.ij2bl(0, 3), 0),
            (aipy_extracts.ij2bl(1, 2), 1),
            (aipy_extracts.ij2bl(1, 3), 0),
        ],
        "0,1,all": [],
    }
    for i in range(nants):
        cases[str(i)] = [(aipy_extracts.ij2bl(x, i), 1) for x in range(nants)]
        cases["-" + str(i)] = [(aipy_extracts.ij2bl(x, i), 0) for x in range(nants)]
    # inelegantly paste on the new pol parsing flag on the above tests
    # XXX really should add some new tests for the new pol parsing
    for k in cases:
        cases[k] = [(v + (-1,))[:3] for v in cases[k]]
    for ant_str in cases:
        assert aipy_extracts.parse_ants(ant_str, nants) == cases[ant_str]

    # check that malformed antstr raises and error
    pytest.raises(ValueError, aipy_extracts.parse_ants, "(0_1)_2", nants)
    return


def test_uv_wrhd(tmp_path):
    """Test _wrdh method on UV object"""
    test_file = str(tmp_path / "miriad_test.uv")
    uv = aipy_extracts.UV(test_file, status="new", corrmode="r")

    # test writing freqs
    freqs = [3, 1, 0.1, 0.2, 2, 0.2, 0.3, 3, 0.3, 0.4]
    uv._wrhd("freqs", freqs)

    # test writing other values
    uv._wrhd("nchan0", 1024)

    # test that we wrote something
    del uv
    assert os.path.isdir(test_file)

    # clean up
    shutil.rmtree(test_file)
    return


def test_uv_wrhd_special(tmp_path):
    """Test _wrhd_special method on UV object"""
    test_file = str(tmp_path / "miriad_test.uv")
    uv = aipy_extracts.UV(test_file, status="new", corrmode="r")
    freqs = [3, 1, 0.1, 0.2, 2, 0.2, 0.3, 3, 0.3, 0.4]
    uv._wrhd_special("freqs", freqs)

    # check that we wrote something to disk
    assert os.path.isdir(test_file)

    # check that anything besides 'freqs' raises an error
    pytest.raises(ValueError, uv._wrhd_special, "foo", 12)

    # clean up after ourselves
    del uv
    shutil.rmtree(test_file)
    return


def test_uv_rdhd_special(tmp_path):
    """Test _rdhd_special method on UV object"""
    infile = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
    test_file = str(tmp_path / "miriad_test.uv")
    if os.path.exists(test_file):
        shutil.rmtree(test_file)
    # make a new file using an old one as a template
    uv1 = aipy_extracts.UV(infile)
    uv2 = aipy_extracts.UV(test_file, status="new", corrmode="r")
    uv2.init_from_uv(uv1)

    # define freqs to write
    freqs = [3, 1, 0.1, 0.2, 2, 0.2, 0.3, 3, 0.3, 0.4]
    uv2._wrhd_special("freqs", freqs)

    # add a single record; otherwise, opening the file fails
    preamble, data = uv1.read()
    uv2.write(preamble, data)
    del uv1
    del uv2

    # open a new file and check that freqs match the written ones
    uv3 = aipy_extracts.UV(test_file)
    freqs2 = uv3._rdhd_special("freqs")
    assert freqs == freqs2

    # rdhr should correctly handle "special" cases as well -- verify that it does so
    freqs2 = uv3._rdhd("freqs")
    assert freqs == freqs2

    # check that anything besides 'freqs' raises an error
    pytest.raises(ValueError, uv3._rdhd_special, "foo")

    # cleean up after ourselves
    shutil.rmtree(test_file)
    return


@pytest.mark.parametrize("raw", [False, True])
@pytest.mark.parametrize("insert_blank", [False, True])
def test_pipe(tmp_path, raw, insert_blank):
    """Test pipe method on UV object"""
    infile = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
    test_file = os.path.join(tmp_path, "miriad_test.uv")

    aipy_uv = aipy_extracts.UV(infile)
    aipy_uv2 = aipy_extracts.UV(test_file, status="new")
    aipy_uv2.init_from_uv(aipy_uv)
    if insert_blank:
        # Insert a blank record, which _should_ get skipped on write
        # such that pipe will still produce an identical file
        aipy_uv2.write((np.zeros(3), 0.0, (0, 0)), None)
    aipy_uv2.pipe(aipy_uv, raw=raw)
    aipy_uv2.close()
    aipy_uv2 = aipy_extracts.UV(test_file)
    aipy_uv.rewind()
    with pytest.raises(OSError, match="No data read"):
        while True:
            rec_orig = aipy_uv.read()
            rec_new = aipy_uv2.read()
            assert np.all(rec_new[0][0] == rec_orig[0][0])
            assert rec_new[0][1] == rec_orig[0][1]
            assert rec_new[0][2] == rec_orig[0][2]
            assert np.ma.allequal(rec_orig[1], rec_new[1])
    aipy_uv.close()
    aipy_uv2.close()


@pytest.mark.parametrize("exclude", [[], "telescop", "history", "dummy"])
def test_init_from_uv_exclude(tmp_path, exclude):
    infile = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
    test_file = os.path.join(tmp_path, "miriad_test.uv")

    aipy_uv = aipy_extracts.UV(infile)
    ((uvw, time, (idx, jdx)), data) = aipy_uv.read()

    aipy_uv2 = aipy_extracts.UV(test_file, status="new")
    # Manually add the variable to the object to make sure the exclude works
    # even if the value has already been initiated. Check to make sure we aren't
    # adding an empty list up front.
    if exclude:
        aipy_uv2.add_var(exclude, "d")
    aipy_uv2.init_from_uv(aipy_uv, exclude=exclude)
    aipy_uv2.write((uvw, time, (idx, jdx)), data)
    aipy_uv2.close()

    aipy_uv2 = aipy_extracts.UV(test_file)

    # Again check that exclude isn't just an empty list, and then make sure that
    # the excluded variable isn't actually in the data set.
    if exclude:
        assert exclude not in aipy_uv2.vartable

    for item in aipy_uv.variables():
        if item != exclude:
            assert np.all(aipy_uv[item] == aipy_uv2[item])

    aipy_uv.close()
    aipy_uv2.close()


@pytest.mark.parametrize(
    "var_dict", [{}, {"latitud": 0.0}, {"longitu": 0.0}, {"history": "abc"}]
)
def test_init_from_uv_override(tmp_path, var_dict):
    infile = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
    test_file = os.path.join(tmp_path, "miriad_test.uv")

    aipy_uv = aipy_extracts.UV(infile)
    ((uvw, time, (idx, jdx)), data) = aipy_uv.read()

    aipy_uv2 = aipy_extracts.UV(test_file, status="new")
    aipy_uv2.init_from_uv(aipy_uv, override=var_dict)
    aipy_uv2.write((uvw, time, (idx, jdx)), data)
    aipy_uv2.close()

    aipy_uv2 = aipy_extracts.UV(test_file)

    for item in aipy_uv.variables():
        if item in var_dict.keys():
            assert aipy_uv2[item] == var_dict[item]
        else:
            assert np.all(aipy_uv[item] == aipy_uv2[item])

    aipy_uv.close()
    aipy_uv2.close()


def test_write_no_flags(tmp_path):
    """
    Verify that if flags are deleted, write will recreate them appropriately
    """
    infile = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
    test_file = os.path.join(tmp_path, "miriad_test.uv")

    aipy_uv = aipy_extracts.UV(infile)
    ((uvw, time, (idx, jdx)), data) = aipy_uv.read()

    aipy_uv2 = aipy_extracts.UV(test_file, status="new")
    aipy_uv2.init_from_uv(aipy_uv)

    # If the mask is remove (all set to False), then write write will create
    # a mask based on where the data have been zeroed out (i.e., default MIRIAD
    # behavior).
    data.mask = 0.0
    aipy_uv2.write((uvw, time, (idx, jdx)), data)
    # Clear the mask entirely, which will also set all the flags to zero.
    data = np.ma.array(data.data, fill_value=data.fill_value)
    aipy_uv2.write((uvw, time, (idx, jdx)), data)
    aipy_uv2.close()

    aipy_uv2 = aipy_extracts.UV(test_file)
    (_, data2) = aipy_uv2.read()
    # Check for equality w/ both masks and data
    assert np.all(data.data == data2.data)
    # Next, check that the masks all look correct
    assert np.all(data.mask == data2.mask)
    (_, data2) = aipy_uv2.read()
    assert np.all(data.mask == data2.mask)
    aipy_uv.close()
    aipy_uv2.close()


def test_add_to_header(tmp_path):
    """
    Verify that if flags are deleted, write will create a mask based on where
    the data have been zeroed out (i.e., default MIRIAD behavior).
    """
    infile = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
    test_file = os.path.join(tmp_path, "miriad_test.uv")

    # Create a fake entry in the item table so that we can run this test -- the double
    # values means an array of length 2 for aipy_extracts.
    aipy_extracts.itemtable["xyz"] = "dd"

    aipy_uv = aipy_extracts.UV(infile)
    aipy_uv2 = aipy_extracts.UV(test_file, status="new")

    aipy_uv2["xyz"] = (2.0, 2.0)
    aipy_uv2.init_from_uv(aipy_uv)
    aipy_uv2.pipe(aipy_uv)
    aipy_uv2.close()

    aipy_uv2 = aipy_extracts.UV(test_file)
    assert np.all(aipy_uv2["xyz"] == np.array([2.0, 2.0]))
    aipy_uv.close()
    aipy_uv2.close()


@pytest.mark.parametrize(
    "polstr,antstr,ants_exp,nrec_exp",
    [
        [-1, "all", (0, 1, 2, 3, 4, 5), 399],
        [-1, "cross", (0, 1, 2, 3, 4, 5), 285],
        [-1, "auto", (0, 1, 2, 3, 4, 5), 114],
        [-1, "(0,1)_(2,3)", (0, 1, 2, 3), 76],
        [-1, "(0,-1)_(-2,3)", (0, 1, 2, 3, 4, 5), 342],
        [-1, "(0x,1x)_(2y,3y)", (0, 1, 2, 3), 76],
        [-1, "4,5,", (0, 1, 2, 3, 4, 5), 209],
        ["xx", -1, (), 0],
        ["xy", -1, (0, 1, 2, 3, 4, 5), 399],
        ["yx", -1, (), 0],
        ["yy", -1, (), 0],
        ["xy", "(0,1)_(2,3)", (0, 1, 2, 3), 76],
        ["xy", "4,5,", (0, 1, 2, 3, 4, 5), 209],
        ["xy", "0,1,(2)_(3)", (0, 1, 2, 3, 4, 5), 228],
    ],
)
def test_uv_selector(polstr, antstr, ants_exp, nrec_exp):
    infile = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
    aipy_uv = aipy_extracts.UV(infile)

    aipy_extracts.uv_selector(aipy_uv, ants=antstr, pol_str=polstr)
    nrec = 0
    with pytest.raises(OSError, match="No data read"):
        while True:
            (_, _, bl_ants), _ = aipy_uv.read()
            nrec += 1
            # Make sure we only have ants we expect
            assert np.all(np.isin(bl_ants, ants_exp))
            if isinstance(polstr, str):
                assert aipy_uv["pol"] == aipy_extracts.str2pol[polstr]

    assert nrec == nrec_exp
    aipy_uv.close()


# pyuvdata/uvdata/tests/test_fhd.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for FHD object.

"""
import glob
import os
from shutil import copyfile

import numpy as np
import pytest

import pyuvdata.tests as uvtest
import pyuvdata.utils as uvutils
from pyuvdata import UVData
from pyuvdata.data import DATA_PATH
from pyuvdata.uvdata.uvdata import _future_array_shapes_warning

# set up FHD file list
testdir = os.path.join(DATA_PATH, "fhd_vis_data/")
testfile_prefix = "1061316296_"
# note: 1061316296_obs.sav isn't used -- it's there to test handling of unneeded files
testfile_suffix = [
    "flags.sav",
    "vis_XX.sav",
    "params.sav",
    "vis_YY.sav",
    "vis_model_XX.sav",
    "vis_model_YY.sav",
    "layout.sav",
    "settings.txt",
    "obs.sav",
]
testfiles = []
for s in testfile_suffix:
    testfiles.append(testdir + testfile_prefix + s)


@pytest.fixture(scope="function")
def fhd_data():
    fhd_uv = UVData()
    fhd_uv.read(testfiles, use_future_array_shapes=True)

    return fhd_uv


@pytest.fixture(scope="function")
def fhd_model():
    fhd_uv = UVData()
    fhd_uv.read(testfiles, use_model=True, use_future_array_shapes=True)

    return fhd_uv


@pytest.mark.filterwarnings("ignore:Telescope location derived from obs")
def test_read_fhd_write_read_uvfits(fhd_data, tmp_path):
    """
    FHD to uvfits loopback test.

    Read in FHD files, write out as uvfits, read back in and check for object
    equality.
    """
    fhd_uv = fhd_data
    uvfits_uv = UVData()

    outfile = str(tmp_path / "outtest_FHD_1061316296.uvfits")
    fhd_uv.write_uvfits(outfile)
    uvfits_uv.read_uvfits(outfile, use_future_array_shapes=True)

    # make sure filename attributes are correct
    assert set(fhd_uv.filename) == {os.path.basename(fn) for fn in testfiles}
    assert uvfits_uv.filename == [os.path.basename(outfile)]
    fhd_uv.filename = uvfits_uv.filename
    fhd_uv._filename.form = (1,)

    # fix up the phase_center_catalogs
    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=fhd_uv.phase_center_catalog
    )
    assert fhd_uv == uvfits_uv


@pytest.mark.filterwarnings("ignore:Telescope location derived from obs")
def test_read_fhd_metadata_only(fhd_data):
    fhd_uv = UVData()
    fhd_uv.read_fhd(testfiles, read_data=False, use_future_array_shapes=True)

    assert fhd_uv.metadata_only

    fhd_uv2 = fhd_data
    fhd_uv3 = fhd_uv2.copy(metadata_only=True)

    assert fhd_uv == fhd_uv3


def test_read_fhd_metadata_only_error():
    fhd_uv = UVData()
    with pytest.raises(
        ValueError, match="No obs file included in file list and read_data is False."
    ):
        fhd_uv.read_fhd(testfiles[:7], read_data=False, use_future_array_shapes=True)


def test_read_fhd_select():
    """
    test select on read with FHD files.

    Read in FHD files with generic read & select on read, compare to read fhd
    files then do select
    """
    fhd_uv = UVData()
    fhd_uv2 = UVData()
    with uvtest.check_warnings(
        [UserWarning, UserWarning, DeprecationWarning],
        [
            'Warning: select on read keyword set, but file_type is "fhd" which '
            "does not support select on read. Entire file will be read and then "
            "select will be performed",
            "Telescope location derived from obs lat/lon/alt values does not match the "
            "location in the layout file. Using the value from known_telescopes.",
            _future_array_shapes_warning,
        ],
    ):
        fhd_uv2.read(testfiles, freq_chans=np.arange(2))

    with uvtest.check_warnings(
        [UserWarning, DeprecationWarning],
        [
            "Telescope location derived from obs lat/lon/alt values does not match the "
            "location in the layout file. Using the value from known_telescopes.",
            _future_array_shapes_warning,
        ],
    ):
        fhd_uv.read(testfiles)

    fhd_uv.select(freq_chans=np.arange(2))
    assert fhd_uv == fhd_uv2


def test_read_fhd_write_read_uvfits_no_layout():
    """
    Test errors/warnings with with no layout file.
    """
    fhd_uv = UVData()
    files_use = testfiles[:-3] + [testfiles[-2]]

    # check warning raised
    with uvtest.check_warnings(UserWarning, "No layout file"):
        fhd_uv.read(files_use, run_check=False, use_future_array_shapes=True)

    with pytest.raises(
        ValueError, match="Required UVParameter _antenna_positions has not been set"
    ):
        with uvtest.check_warnings(UserWarning, "No layout file"):
            fhd_uv.read(files_use, use_future_array_shapes=True)


@pytest.mark.filterwarnings("ignore:Telescope location derived from obs")
@pytest.mark.filterwarnings("ignore:telescope_location is not set. ")
@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
def test_fhd_antenna_pos(fhd_data):
    """
    Check that FHD antenna positions are handled as rotated ECEF coords, like uvfits.
    """
    mwa_corr_dir = os.path.join(DATA_PATH, "mwa_corr_fits_testfiles/")

    mwa_corr_files = [
        "1131733552.metafits",
        "1131733552_20151116182537_mini_gpubox01_00.fits",
    ]
    mwa_corr_file_list = [os.path.join(mwa_corr_dir, fname) for fname in mwa_corr_files]

    mwa_corr_obj = UVData()
    mwa_corr_obj.read(
        mwa_corr_file_list,
        correct_cable_len=True,
        phase_to_pointing_center=True,
        read_data=False,
        use_future_array_shapes=True,
    )

    assert fhd_data._antenna_names == mwa_corr_obj._antenna_names
    assert fhd_data._antenna_positions == mwa_corr_obj._antenna_positions

    cotter_file = os.path.join(DATA_PATH, "1061316296.uvfits")
    cotter_obj = UVData()
    cotter_obj.read(cotter_file, use_future_array_shapes=True)

    # don't test antenna_numbers, they will not match.
    # mwa_corr_fits now uses antenna_numbers that correspond to antenna_names
    # instead of following the cotter convention of using 0-127.
    assert fhd_data._antenna_names == cotter_obj._antenna_names
    assert fhd_data._antenna_positions == cotter_obj._antenna_positions

    assert mwa_corr_obj._antenna_positions == cotter_obj._antenna_positions


@pytest.mark.filterwarnings("ignore:Telescope location derived from obs")
def test_read_fhd_write_read_uvfits_variant_flag(tmp_path):
    """
    FHD to uvfits loopback test with variant flag file.

    Read in FHD files, write out as uvfits, read back in and check for object
    equality.
    """
    fhd_uv = UVData()
    uvfits_uv = UVData()
    variant_flag_file = testdir + testfile_prefix + "variant_flags.sav"
    files_use = testfiles[1:] + [variant_flag_file]
    fhd_uv.read(files_use, use_future_array_shapes=True)

    outfile = str(tmp_path / "outtest_FHD_1061316296.uvfits")
    fhd_uv.write_uvfits(outfile)
    uvfits_uv.read_uvfits(outfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(fhd_uv.filename) == {os.path.basename(fn) for fn in files_use}
    assert uvfits_uv.filename == ["outtest_FHD_1061316296.uvfits"]
    fhd_uv.filename = uvfits_uv.filename
    fhd_uv._filename.form = (1,)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=fhd_uv.phase_center_catalog
    )

    assert fhd_uv == uvfits_uv


@pytest.mark.filterwarnings("ignore:Telescope location derived from obs lat/lon/alt")
def test_read_fhd_write_read_uvfits_fix_layout(tmp_path):
    """
    FHD to uvfits loopback test with fixed array center layout file.

    Read in FHD files, write out as uvfits, read back in and check for object
    equality.
    """
    fhd_uv = UVData()
    uvfits_uv = UVData()
    layout_fixed_file = testdir + testfile_prefix + "fixed_arr_center_layout.sav"
    files_use = testfiles[0:6] + [layout_fixed_file, testfiles[7]]
    fhd_uv.read(files_use, use_future_array_shapes=True)
    outfile = str(tmp_path / "outtest_FHD_1061316296.uvfits")

    fhd_uv.write_uvfits(outfile)
    uvfits_uv.read_uvfits(outfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(fhd_uv.filename) == {os.path.basename(fn) for fn in files_use}
    assert uvfits_uv.filename == ["outtest_FHD_1061316296.uvfits"]
    fhd_uv.filename = uvfits_uv.filename
    fhd_uv._filename.form = (1,)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=fhd_uv.phase_center_catalog
    )

    assert fhd_uv == uvfits_uv


def test_read_fhd_write_read_uvfits_fix_layout_bad_obs_loc(tmp_path):
    """
    FHD to uvfits loopback test with fixed array center layout file, bad obs location.

    Read in FHD files, write out as uvfits, read back in and check for object
    equality.
    """
    fhd_uv = UVData()
    uvfits_uv = UVData()
    bad_obs_loc_file = testdir + testfile_prefix + "bad_obs_loc_vis_XX.sav"
    layout_fixed_file = testdir + testfile_prefix + "fixed_arr_center_layout.sav"
    files_use = [
        testfiles[0],
        testfiles[2],
        bad_obs_loc_file,
        layout_fixed_file,
        testfiles[7],
    ]
    messages = [
        "Telescope location derived from obs",
        "tile_names from obs structure does not match",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        fhd_uv.read(files_use, use_future_array_shapes=True)

    outfile = str(tmp_path / "outtest_FHD_1061316296.uvfits")
    fhd_uv.write_uvfits(outfile)
    uvfits_uv.read_uvfits(outfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(fhd_uv.filename) == {os.path.basename(fn) for fn in files_use}
    assert uvfits_uv.filename == ["outtest_FHD_1061316296.uvfits"]
    fhd_uv.filename = uvfits_uv.filename
    fhd_uv._filename.form = (1,)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=fhd_uv.phase_center_catalog
    )
    assert fhd_uv == uvfits_uv


def test_read_fhd_write_read_uvfits_bad_obs_loc(tmp_path):
    """
    FHD to uvfits loopback test with bad obs location (and bad layout location).

    Read in FHD files, write out as uvfits, read back in and check for object
    equality.
    """
    fhd_uv = UVData()
    uvfits_uv = UVData()
    bad_obs_loc_file = testdir + testfile_prefix + "bad_obs_loc_vis_XX.sav"
    files_use = [
        testfiles[0],
        testfiles[2],
        bad_obs_loc_file,
        testfiles[6],
        testfiles[7],
    ]
    messages = [
        "Telescope location derived from obs",
        "tile_names from obs structure does not match",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        fhd_uv.read(files_use, use_future_array_shapes=True)

    outfile = str(tmp_path / "outtest_FHD_1061316296.uvfits")
    fhd_uv.write_uvfits(outfile)
    uvfits_uv.read_uvfits(outfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(fhd_uv.filename) == {os.path.basename(fn) for fn in files_use}
    assert uvfits_uv.filename == ["outtest_FHD_1061316296.uvfits"]
    fhd_uv.filename = uvfits_uv.filename
    fhd_uv._filename.form = (1,)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=fhd_uv.phase_center_catalog
    )
    assert fhd_uv == uvfits_uv


def test_read_fhd_write_read_uvfits_altered_layout(tmp_path):
    """
    FHD to uvfits loopback test with altered layout file.

    Read in FHD files, write out as uvfits, read back in and check for object
    equality.
    """
    fhd_uv = UVData()
    uvfits_uv = UVData()

    # bad layout structure values
    altered_layout_file = testdir + testfile_prefix + "broken_layout.sav"
    files_use = testfiles[0:6] + [altered_layout_file, testfiles[7]]
    fhd_uv.read(files_use, use_future_array_shapes=True)

    outfile = str(tmp_path / "outtest_FHD_1061316296.uvfits")
    fhd_uv.write_uvfits(outfile)
    uvfits_uv.read_uvfits(outfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(fhd_uv.filename) == {os.path.basename(fn) for fn in files_use}
    assert uvfits_uv.filename == ["outtest_FHD_1061316296.uvfits"]
    fhd_uv.filename = uvfits_uv.filename
    fhd_uv._filename.form = (1,)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=fhd_uv.phase_center_catalog
    )
    assert fhd_uv == uvfits_uv


def test_read_fhd_write_read_uvfits_no_settings(tmp_path):
    """
    FHD to uvfits loopback test with no settings file.

    Read in FHD files, write out as uvfits, read back in and check for object
    equality.
    """
    fhd_uv = UVData()
    uvfits_uv = UVData()
    messages = [
        "No settings file included in file list",
        "Telescope location derived from obs lat/lon/alt values does not match the "
        "location in the layout file. Using the value from known_telescopes.",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        fhd_uv.read(testfiles[:-2], use_future_array_shapes=True)

    # Check only pyuvdata history with no settings file
    assert fhd_uv.history == fhd_uv.pyuvdata_version_str

    outfile = str(tmp_path / "outtest_FHD_1061316296.uvfits")
    fhd_uv.write_uvfits(outfile)
    uvfits_uv.read_uvfits(outfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(fhd_uv.filename) == {os.path.basename(fn) for fn in testfiles[:-2]}
    assert uvfits_uv.filename == ["outtest_FHD_1061316296.uvfits"]
    fhd_uv.filename = uvfits_uv.filename
    fhd_uv._filename.form = (1,)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=fhd_uv.phase_center_catalog
    )
    assert fhd_uv == uvfits_uv


def test_break_read_fhd():
    """Try various cases of incomplete file lists."""
    fhd_uv = UVData()
    # missing flags
    with pytest.raises(ValueError, match="No flags file included in file list"):
        fhd_uv.read(testfiles[1:], use_future_array_shapes=True)

    # Missing params
    subfiles = [item for sublist in [testfiles[0:2], testfiles[3:]] for item in sublist]
    with pytest.raises(ValueError, match="No params file included in file list"):
        fhd_uv.read(subfiles, use_future_array_shapes=True)

    # No data files
    with pytest.raises(
        ValueError, match="No data files included in file list and read_data is True."
    ):
        fhd_uv.read(["foo.sav"], use_future_array_shapes=True)


def test_read_fhd_warnings():
    """Test warnings with various broken inputs."""
    # bad obs structure values
    broken_data_file = testdir + testfile_prefix + "broken_vis_XX.sav"
    bad_filelist = [
        testfiles[0],
        testfiles[2],
        broken_data_file,
        testfiles[6],
        testfiles[7],
    ]
    warn_messages = [
        "Ntimes does not match",
        "Telescope location derived from obs",
        "These visibilities may have been phased improperly",
        "Nbls does not match",
    ]
    fhd_uv = UVData()
    with uvtest.check_warnings(UserWarning, warn_messages):
        fhd_uv.read(bad_filelist, run_check=False, use_future_array_shapes=True)

    # bad flag file
    broken_flag_file = testdir + testfile_prefix + "broken_flags.sav"
    bad_filelist = testfiles[1:] + [broken_flag_file]
    fhd_uv = UVData()
    with pytest.raises(
        ValueError, match="No recognized key for visibility weights in flags_file."
    ):
        fhd_uv.read(bad_filelist, use_future_array_shapes=True)


@pytest.mark.parametrize(
    "new_file_end,file_copy,message",
    [
        (["extra_vis_XX.sav"], testfiles[1], "multiple xx datafiles in filelist"),
        (["extra_vis_YY.sav"], testfiles[3], "multiple yy datafiles in filelist"),
        (
            ["vis_XY.sav", "extra_vis_XY.sav"],
            testfiles[1],
            "multiple xy datafiles in filelist",
        ),
        (
            ["vis_YX.sav", "extra_vis_YX.sav"],
            testfiles[1],
            "multiple yx datafiles in filelist",
        ),
        (["extra_params.sav"], testfiles[2], "multiple params files in filelist"),
        (["extra_obs.sav"], testfiles[8], "multiple obs files in filelist"),
        (["extra_flags.sav"], testfiles[0], "multiple flags files in filelist"),
        (["extra_layout.sav"], testfiles[6], "multiple layout files in filelist"),
        (["extra_settings.txt"], testfiles[7], "multiple settings files in filelist"),
    ],
)
def test_read_fhd_extra_files(new_file_end, file_copy, message):
    # try cases with extra files of each type
    new_files = []
    for file_end in new_file_end:
        extra_file = testdir + testfile_prefix + file_end
        new_files.append(extra_file)
        copyfile(file_copy, extra_file)
    fhd_uv = UVData()
    with pytest.raises(ValueError, match=message):
        fhd_uv.read(testfiles + new_files, use_future_array_shapes=True)
    for extra_file in new_files:
        os.remove(extra_file)


@pytest.mark.filterwarnings("ignore:Telescope location derived from obs")
def test_read_fhd_model(tmp_path, fhd_model):
    """FHD to uvfits loopback test with model visibilities."""
    fhd_uv = fhd_model
    uvfits_uv = UVData()

    outfile = str(tmp_path / "outtest_FHD_1061316296_model.uvfits")
    fhd_uv.write_uvfits(outfile)
    uvfits_uv.read_uvfits(outfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(fhd_uv.filename) == {os.path.basename(fn) for fn in testfiles}
    assert uvfits_uv.filename == ["outtest_FHD_1061316296_model.uvfits"]
    fhd_uv.filename = uvfits_uv.filename
    fhd_uv._filename.form = (1,)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=fhd_uv.phase_center_catalog
    )
    assert fhd_uv == uvfits_uv


@pytest.mark.filterwarnings("ignore:Telescope location derived from obs")
def test_multi_files(fhd_model):
    """Read multiple files at once."""
    fhd_uv1 = UVData()
    fhd_uv2 = UVData()
    test1 = list(np.array(testfiles)[[0, 1, 2, 4, 6, 7]])
    test2 = list(np.array(testfiles)[[0, 2, 3, 5, 6, 7]])
    fhd_uv1.read(
        np.array([test1, test2]),
        use_model=True,
        file_type="fhd",
        allow_rephase=False,
        use_future_array_shapes=True,
    )

    fhd_uv2 = fhd_model

    assert uvutils._check_histories(
        fhd_uv2.history + " Combined data along polarization axis using pyuvdata.",
        fhd_uv1.history,
    )

    fhd_uv1.history = fhd_uv2.history

    assert set(fhd_uv1.filename + ["1061316296_obs.sav"]) == set(fhd_uv2.filename)
    fhd_uv1.filename = fhd_uv2.filename
    fhd_uv1._filename.form = len(fhd_uv1.filename)

    assert fhd_uv1 == fhd_uv2


@pytest.mark.filterwarnings("ignore:Telescope location derived from obs")
def test_multi_files_axis(fhd_model):
    """Read multiple files at once with axis keyword."""
    fhd_uv1 = UVData()
    fhd_uv2 = UVData()
    test1 = list(np.array(testfiles)[[0, 1, 2, 4, 6, 7]])
    test2 = list(np.array(testfiles)[[0, 2, 3, 5, 6, 7]])
    fhd_uv1.read(
        np.array([test1, test2]),
        use_model=True,
        axis="polarization",
        allow_rephase=False,
        use_future_array_shapes=True,
    )

    fhd_uv2 = fhd_model

    assert uvutils._check_histories(
        fhd_uv2.history + " Combined data along polarization axis using pyuvdata.",
        fhd_uv1.history,
    )

    fhd_uv1.history = fhd_uv2.history

    assert set(fhd_uv1.filename + ["1061316296_obs.sav"]) == set(fhd_uv2.filename)
    fhd_uv1.filename = fhd_uv2.filename
    fhd_uv1._filename.form = len(fhd_uv1.filename)

    assert fhd_uv1 == fhd_uv2


def test_single_time():
    """
    test reading in a file with a single time.
    """
    single_time_filelist = glob.glob(os.path.join(DATA_PATH, "refsim1.1_fhd/*"))
    fhd_uv = UVData()
    with uvtest.check_warnings(
        UserWarning,
        [
            "tile_names from obs structure does not match",
            "Telescope location derived from obs lat/lon/alt",
        ],
    ):
        fhd_uv.read(single_time_filelist, use_future_array_shapes=True)

    assert np.unique(fhd_uv.time_array).size == 1


def test_conjugation():
    """test uvfits vs fhd conjugation"""
    uvfits_file = os.path.join(DATA_PATH, "ref_1.1_uniform.uvfits")
    fhd_filelist = glob.glob(os.path.join(DATA_PATH, "refsim1.1_fhd/*"))
    uvfits_uv = UVData()
    uvfits_uv.read(uvfits_file, use_future_array_shapes=True)

    fhd_uv = UVData()
    with uvtest.check_warnings(
        UserWarning,
        [
            "tile_names from obs structure does not match",
            "Telescope location derived from obs lat/lon/alt",
        ],
    ):
        fhd_uv.read(fhd_filelist, use_future_array_shapes=True)

    uvfits_uv.select(polarizations=fhd_uv.polarization_array)

    assert uvfits_uv._uvw_array == fhd_uv._uvw_array
    assert uvfits_uv._data_array == fhd_uv._data_array


# pyuvdata/uvdata/tests/test_mir.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2020 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for Mir class.

Performs a series of test for the Mir class, which inherits from UVData. Note that
there is a separate test module for the MirParser class (mir_parser.py), which is
what is used to read the raw binary data into something that the Mir class can
manipulate into a UVData object.
"""
import os

import numpy as np
import pytest

from ... import UVData
from ... import tests as uvtest
from ...data import DATA_PATH
from ...uvdata.mir import Mir
from ...uvdata.mir_parser import MirParser
from ..uvdata import _future_array_shapes_warning


@pytest.fixture(scope="session")
def sma_mir_filt_main():
    uv_object = UVData()
    testfile = os.path.join(DATA_PATH, "sma_test.mir")
    uv_object.read(
        testfile, pseudo_cont=True, corrchunk=0, use_future_array_shapes=True
    )

    uv_object.flag_array[:, : uv_object.Nfreqs // 2, 0] = True
    uv_object.flag_array[:, uv_object.Nfreqs // 2 :, 1] = True
    uv_object.set_lsts_from_time_array()
    uv_object._set_app_coords_helper()

    yield uv_object


@pytest.fixture(scope="function")
def sma_mir_filt(sma_mir_filt_main):
    uv_object = sma_mir_filt_main.copy()

    yield uv_object


@pytest.mark.filterwarnings("ignore:LST values stored in this file are not ")
@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_read_mir_write_uvfits(sma_mir, tmp_path, future_shapes):
    """
    Mir to uvfits loopback test.

    Read in Mir files, write out as uvfits, read back in and check for
    object equality.
    """
    testfile = os.path.join(tmp_path, "outtest_mir.uvfits")
    uvfits_uv = UVData()

    if not future_shapes:
        sma_mir.use_current_array_shapes()
    sma_mir.write_uvfits(testfile)
    uvfits_uv.read_uvfits(testfile, use_future_array_shapes=future_shapes)
    for item in ["dut1", "earth_omega", "gst0", "rdate", "timesys"]:
        # Check to make sure that the UVFITS-specific paramters are set on the
        # UVFITS-based obj, and not on our original object. Then set it to None for the
        # UVFITS-based obj.
        assert getattr(sma_mir, item) is None
        assert getattr(uvfits_uv, item) is not None
        setattr(uvfits_uv, item, None)

    # UVFITS doesn't allow for numbering of spectral windows like MIR does, so
    # we need an extra bit of handling here
    assert len(np.unique(sma_mir.spw_array)) == len(np.unique(uvfits_uv.spw_array))

    spw_dict = dict(zip(uvfits_uv.spw_array, sma_mir.spw_array))

    assert np.all(
        [
            idx == spw_dict[jdx]
            for idx, jdx in zip(sma_mir.flex_spw_id_array, uvfits_uv.flex_spw_id_array)
        ]
    )

    # Now that we've checked, set this things as equivalent
    uvfits_uv.spw_array = sma_mir.spw_array
    uvfits_uv.flex_spw_id_array = sma_mir.flex_spw_id_array

    # Check the history first via find
    assert 0 == uvfits_uv.history.find(
        sma_mir.history + "  Read/written with pyuvdata version:"
    )
    sma_mir.history = uvfits_uv.history

    # We have to do a bit of special handling for the phase_center_catalog, because
    # _very_ small errors (like last bit in the mantissa) creep in when passing through
    # the util function transform_sidereal_coords (for mutli-phase-ctr datasets). Verify
    # the two match up in terms of their coordinates
    for cat_name in sma_mir.phase_center_catalog.keys():
        assert np.isclose(
            sma_mir.phase_center_catalog[cat_name]["cat_lat"],
            uvfits_uv.phase_center_catalog[cat_name]["cat_lat"],
        )
        assert np.isclose(
            sma_mir.phase_center_catalog[cat_name]["cat_lon"],
            uvfits_uv.phase_center_catalog[cat_name]["cat_lon"],
        )
    uvfits_uv.phase_center_catalog = sma_mir.phase_center_catalog

    # There's a minor difference between what SMA calculates online for app coords
    # and what pyuvdata calculates, to the tune of ~1 arcsec. Check those values here,
    # then set them equal to one another.
    assert np.all(
        np.abs(sma_mir.phase_center_app_ra - uvfits_uv.phase_center_app_ra) < 1e-5
    )

    assert np.all(
        np.abs(sma_mir.phase_center_app_dec - uvfits_uv.phase_center_app_dec) < 1e-5
    )

    sma_mir._set_app_coords_helper()
    uvfits_uv._set_app_coords_helper()

    # make sure filenames are what we expect
    assert sma_mir.filename == ["sma_test.mir"]
    assert uvfits_uv.filename == ["outtest_mir.uvfits"]
    sma_mir.filename = uvfits_uv.filename
    assert sma_mir == uvfits_uv

    assert sma_mir == uvfits_uv


@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:LST values stored in this file are not ")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_read_mir_write_ms(sma_mir, tmp_path, future_shapes):
    """
    Mir to uvfits loopback test.

    Read in Mir files, write out as ms, read back in and check for
    object equality.
    """
    pytest.importorskip("casacore")
    testfile = os.path.join(tmp_path, "outtest_mir.ms")
    ms_uv = UVData()

    warn_msg = ["The `make_multi_phase` option is deprecated"]
    if not future_shapes:
        sma_mir.use_current_array_shapes()
        warn_msg.append(_future_array_shapes_warning)

    sma_mir.write_ms(testfile, clobber=True)
    with uvtest.check_warnings(DeprecationWarning, match=warn_msg):
        ms_uv.read(
            testfile,
            make_multi_phase=True,
            allow_rephase=False,
            use_future_array_shapes=future_shapes,
        )

    # fix up the phase center info to match the mir dataset
    cat_id = list(sma_mir.phase_center_catalog.keys())[0]
    cat_name = sma_mir.phase_center_catalog[cat_id]["cat_name"]
    ms_uv._update_phase_center_id(list(ms_uv.phase_center_catalog.keys())[0], cat_id)
    ms_uv.phase_center_catalog[cat_id]["cat_name"] = cat_name
    ms_uv.phase_center_catalog[cat_id]["info_source"] = "file"

    # Single integration with 1 phase center = single scan number
    # output in the MS
    assert ms_uv.scan_number_array == np.array([1])

    # There are some minor differences between the values stored by MIR and that
    # calculated by UVData. Since MS format requires these to be calculated on the fly,
    # we calculate them here just to verify that everything is looking okay.
    sma_mir.set_lsts_from_time_array()
    sma_mir._set_app_coords_helper()

    # These reorderings just make sure that data from the two formats are lined up
    # correctly.
    sma_mir.reorder_freqs(spw_order="number")
    ms_uv.reorder_blts()

    # MS doesn't have the concept of an "instrument" name like FITS does, and instead
    # defaults to the telescope name. Make sure that checks out here.
    assert sma_mir.instrument == "SWARM"
    assert ms_uv.instrument == "SMA"
    sma_mir.instrument = ms_uv.instrument

    # Quick check for history here
    assert ms_uv.history != sma_mir.history
    ms_uv.history = sma_mir.history

    # Only MS has extra keywords, verify those look as expected.
    assert ms_uv.extra_keywords == {"DATA_COL": "DATA", "observer": "SMA"}
    assert sma_mir.extra_keywords == {}
    sma_mir.extra_keywords = ms_uv.extra_keywords

    # Make sure the filenames line up as expected.
    assert sma_mir.filename == ["sma_test.mir"]
    assert ms_uv.filename == ["outtest_mir.ms"]
    sma_mir.filename = ms_uv.filename = None

    # Finally, with all exceptions handled, check for equality.
    assert ms_uv.__eq__(sma_mir, allowed_failures=["filename"])


@pytest.mark.filterwarnings("ignore:LST values stored ")
def test_read_mir_write_uvh5(sma_mir, tmp_path):
    """
    Mir to uvfits loopback test.

    Read in Mir files, write out as uvh5, read back in and check for
    object equality.
    """
    testfile = os.path.join(tmp_path, "outtest_mir.uvh5")
    uvh5_uv = UVData()

    sma_mir.write_uvh5(testfile)
    uvh5_uv.read_uvh5(testfile, use_future_array_shapes=True)

    # Check the history first via find
    assert 0 == uvh5_uv.history.find(
        sma_mir.history + "  Read/written with pyuvdata version:"
    )

    # test fails because of updated history, so this is our workaround for now.
    sma_mir.history = uvh5_uv.history

    # make sure filenames are what we expect
    assert sma_mir.filename == ["sma_test.mir"]
    assert uvh5_uv.filename == ["outtest_mir.uvh5"]
    sma_mir.filename = uvh5_uv.filename

    assert sma_mir == uvh5_uv


def test_mir_partial_read(sma_mir):
    """Check that select is done after the read for select on read with mir files."""
    uv = sma_mir

    uv2 = uv.copy()
    freq_chans_to_keep = np.arange(uv.Nfreqs // 2)
    uv2.select(freq_chans=freq_chans_to_keep)

    testfile = os.path.join(DATA_PATH, "sma_test.mir")
    with uvtest.check_warnings(
        UserWarning,
        "Warning: a select on read keyword is set that is "
        "not supported by read_mir. This select will be done after reading the file.",
    ):
        uv3 = UVData.from_file(
            testfile, freq_chans=freq_chans_to_keep, use_future_array_shapes=True
        )

    assert uv3 == uv2


def test_write_mir(hera_uvh5, err_type=NotImplementedError):
    """
    Mir writer test

    Check and make sure that attempts to use the writer return a
    'not implemented' error.
    """

    # Check and see if the correct error is raised
    with pytest.raises(err_type):
        hera_uvh5.write_mir("dummy.mir")


def test_multi_nchan_spw_read(tmp_path):
    """
    Mir to uvfits error test for spws of different sizes.

    Read in Mir files, write out as uvfits, read back in and check for
    object equality.
    """
    testfile = os.path.join(DATA_PATH, "sma_test.mir")
    uv_in = UVData()
    uv_in.read_mir(testfile, corrchunk=[0, 1, 2, 3, 4])

    dummyfile = os.path.join(tmp_path, "dummy.mirtest.uvfits")
    with pytest.raises(IndexError):
        uv_in.write_uvfits(dummyfile)


def test_read_mir_write_ms_flex_pol(mir_data, tmp_path):
    """
    Mir to MS loopback test with flex-pol.

    Read in Mir files, write out as ms, read back in and check for
    object equality.
    """
    pytest.importorskip("casacore")
    testfile = os.path.join(tmp_path, "read_mir_write_ms_flex_pol.ms")

    # Read in the raw data so that we can manipulate it, and make it look like the
    # test data set was recorded with split-tuning
    mir_data.sp_data._data["gunnLO"][np.isin(mir_data.sp_data["blhid"], [1, 3])] += 30.0
    mir_data.sp_data._data["fsky"][np.isin(mir_data.sp_data["blhid"], [1, 3])] += 30.0

    # Spin up a Mir object, which can be converted into a UVData object,
    # with flex-pol enabled.
    mir_uv = UVData()
    mir_obj = Mir()
    mir_obj._init_from_mir_parser(mir_data)
    mir_uv._convert_from_filetype(mir_obj)
    mir_uv.use_future_array_shapes()

    # Write out our modified data set
    mir_uv.write_ms(testfile, clobber=True)
    ms_uv = UVData.from_file(
        testfile, allow_rephase=False, use_future_array_shapes=True
    )

    # fix up the phase center info to match the mir dataset
    cat_id = list(mir_uv.phase_center_catalog.keys())[0]
    cat_name = mir_uv.phase_center_catalog[cat_id]["cat_name"]
    ms_uv._update_phase_center_id(list(ms_uv.phase_center_catalog.keys())[0], cat_id)
    ms_uv.phase_center_catalog[cat_id]["cat_name"] = cat_name
    ms_uv.phase_center_catalog[cat_id]["info_source"] = "file"

    # There are some minor differences between the values stored by MIR and that
    # calculated by UVData. Since MS format requires these to be calculated on the
    # fly, we calculate them here just to verify that everything is looking okay.
    mir_uv.set_lsts_from_time_array()
    mir_uv._set_app_coords_helper()

    # These reorderings just make sure that data from the two formats
    # are lined up correctly.
    mir_uv.reorder_freqs(spw_order="number")
    ms_uv.reorder_blts()

    # MS doesn't have the concept of an "instrument" name like FITS does, and instead
    # defaults to the telescope name. Make sure that checks out here.
    assert mir_uv.instrument == "SWARM"
    assert ms_uv.instrument == "SMA"
    mir_uv.instrument = ms_uv.instrument

    # Quick check for history here
    assert ms_uv.history != mir_uv.history
    ms_uv.history = mir_uv.history

    # Only MS has extra keywords, verify those look as expected.
    assert ms_uv.extra_keywords == {"DATA_COL": "DATA", "observer": "SMA"}
    assert mir_uv.extra_keywords == {}
    mir_uv.extra_keywords = ms_uv.extra_keywords

    # Make sure the filenames line up as expected.
    assert mir_uv.filename == ["sma_test.mir"]
    assert ms_uv.filename == ["read_mir_write_ms_flex_pol.ms"]

    # Finally, with all exceptions handled, check for equality.
    assert ms_uv.__eq__(mir_uv, allowed_failures=["filename"])


def test_inconsistent_sp_records(mir_data, sma_mir):
    """
    Test that the MIR object does the right thing w/ inconsistent meta-data.
    """
    mir_data.select(where=("iband", "ne", 0))
    mir_data.sp_data._data["ipq"][1] = 0
    mir_data.load_data()

    with uvtest.check_warnings(UserWarning, "Per-spectral window metadata differ."):
        mir_uv = UVData()
        mir_obj = Mir()
        mir_obj._init_from_mir_parser(mir_data)
        mir_uv._convert_from_filetype(mir_obj)
        mir_uv.use_future_array_shapes()

    assert mir_uv == sma_mir


def test_inconsistent_bl_records(mir_data, sma_mir):
    """
    Test that the MIR object does the right thing w/ inconsistent meta-data.
    """
    mir_data.select(where=("iband", "ne", 0))
    mir_data.bl_data._data["u"][0] = 0.0
    mir_data.load_data()
    with uvtest.check_warnings(UserWarning, "Per-baseline metadata differ."):
        mir_uv = UVData()
        mir_obj = Mir()
        mir_obj._init_from_mir_parser(mir_data)
        mir_uv._convert_from_filetype(mir_obj)
        mir_uv.use_future_array_shapes()

    assert mir_uv == sma_mir


def test_multi_ipol(mir_data, sma_mir):
    """
    Test that the MIR object does the right thing when different polarization types
    are recorded in the pol code.
    """
    mir_data.select(where=("iband", "ne", 0))
    mir_data.bl_data._data["ipol"][:] = mir_data.bl_data["ant1rx"]
    mir_data.load_data()

    mir_uv = UVData()
    mir_obj = Mir()
    mir_obj._init_from_mir_parser(mir_data)
    mir_uv._convert_from_filetype(mir_obj)
    mir_uv.use_future_array_shapes()

    assert mir_uv == sma_mir


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("filetype", ["uvh5", "miriad", "ms", "uvfits"])
@pytest.mark.parametrize("future_shapes", [True, False])
def test_flex_pol_roundtrip(sma_mir_filt, filetype, future_shapes, tmp_path):
    """Test that we can round-trip flex-pol data sets"""
    testfile = os.path.join(tmp_path, "flex_pol_roundtrip." + filetype)
    if filetype == "ms":
        pytest.importorskip("casacore")
    elif filetype == "miriad":
        pytest.importorskip("pyuvdata._miriad")

    if not future_shapes:
        sma_mir_filt.use_current_array_shapes()

    uvd2 = sma_mir_filt.copy(metadata_only=True)

    sma_mir_filt._make_flex_pol(raise_error=True)
    with pytest.raises(
        ValueError,
        match="Cannot make a metadata_only UVData object flex-pol because flagging "
        "info is required.",
    ):
        uvd2._make_flex_pol(raise_error=True)
    with uvtest.check_warnings(
        UserWarning,
        match="Cannot make a metadata_only UVData object flex-pol because flagging "
        "info is required.",
    ):
        uvd2._make_flex_pol()
    uvd2._make_flex_pol(raise_warning=False)
    uvd3 = sma_mir_filt.copy(metadata_only=True)
    assert uvd2 != uvd3

    uvd3.remove_flex_pol(combine_spws=False)
    assert uvd2 != uvd3

    if filetype in ["uvfits", "miriad"]:
        warn_str = (
            "combine_spws is True but there are not matched spws for all "
            "polarizations, so spws will not be combined."
        )
        exp_warning = UserWarning
    else:
        exp_warning = None
        warn_str = ""

    with uvtest.check_warnings(exp_warning, match=warn_str):
        if filetype == "uvfits":
            sma_mir_filt.write_uvfits(testfile)
        else:
            getattr(sma_mir_filt, "write_" + filetype)(testfile)

    test_uv = UVData.from_file(
        testfile, remove_flex_pol=False, use_future_array_shapes=future_shapes
    )

    if filetype in ["uvfits", "miriad"]:
        test_uv._make_flex_pol(raise_error=True)

    assert np.all(
        sma_mir_filt.flex_spw_polarization_array == test_uv.flex_spw_polarization_array
    )
    assert np.all(sma_mir_filt.polarization_array == test_uv.polarization_array)
    assert np.all(sma_mir_filt.flag_array == test_uv.flag_array)
    assert np.all(sma_mir_filt.data_array == test_uv.data_array)
    assert np.all(sma_mir_filt.nsample_array == test_uv.nsample_array)


def test_flex_pol_select(sma_mir_filt):
    """Test select operations on flex-pol UVData objects"""
    sma_mir_filt._make_flex_pol(raise_error=True)
    with pytest.raises(
        ValueError, match="No data matching this polarization and frequency"
    ):
        sma_mir_filt.select(polarizations=["xx"], freq_chans=[0, 1, 2, 3])

    sma_mir_filt2 = sma_mir_filt.copy()
    sma_mir_filt.select(polarizations=["xx"])

    assert sma_mir_filt.flex_spw_polarization_array is None
    assert np.all(sma_mir_filt.polarization_array == -5)

    sma_mir_filt._make_flex_pol()

    assert np.all(sma_mir_filt.flex_spw_polarization_array == -5)
    assert np.all(sma_mir_filt.polarization_array == 0)

    # Need to add more polarizations to test uneven pol spacing
    sma_mir_filt3 = sma_mir_filt2.copy()
    sma_mir_filt3.flex_spw_polarization_array = np.asarray([-7, -8])
    sma_mir_filt3.flex_spw_id_array = np.asarray([0] * 4 + [1] * 4)
    sma_mir_filt3.spw_array = [0, 1]
    sma_mir_filt2 += sma_mir_filt3

    with uvtest.check_warnings(
        UserWarning, match="Selected polarization values are not evenly spaced"
    ):
        sma_mir_filt2.select(polarizations=["xx", "xy", "yx"])


def test_flex_pol_select_warning(sma_mir_filt):
    """
    Check that selecting flex-pol datasets with uneven pol spacing throws a warning.
    """
    sma_mir_filt._make_flex_pol(raise_error=True)
    sma_mir_filt.flex_spw_id_array = np.arange(8) // 2
    sma_mir_filt.spw_array = np.arange(4)
    sma_mir_filt.flex_spw_polarization_array = np.array([-8, -8, -6, -5])
    sma_mir_filt.Nspws = 4

    with uvtest.check_warnings(
        UserWarning, "Selected polarization values are not evenly spaced."
    ):
        sma_mir_filt.select(polarizations=[-8, -6, -5])


@pytest.mark.parametrize(
    "make_flex,err_msg",
    [
        [True, "Cannot add a flex-pol UVData objects where the same"],
        [False, "Cannot add a flex-pol and non-flex-pol UVData objects."],
    ],
)
def test_flex_pol_add_errs(sma_mir_filt, make_flex, err_msg):
    """Test that the add error throws appropriate errors"""
    sma_copy = sma_mir_filt.copy()
    sma_mir_filt._make_flex_pol()

    if make_flex:
        sma_copy.select(polarizations=["xx"])
        sma_copy.flag_array[:] = True
        sma_copy.data_array[:] = 0.0
        sma_copy._make_flex_pol()

    with pytest.raises(ValueError) as cm:
        sma_copy + sma_mir_filt
    assert str(cm.value).startswith(err_msg)


def test_flex_pol_add(sma_mir_filt):
    """Test that the add method works correctly with flex-pol data"""
    # Grab two copies of the data before we start to manipulate it
    sma_xx_copy = sma_mir_filt.copy()
    sma_yy_copy = sma_mir_filt.copy()
    sma_mir_filt._make_flex_pol()

    # In both copies, isolate out the relevant polarization data.
    sma_xx_copy.select(polarizations=["xx"], freq_chans=[4, 5, 6, 7])
    sma_xx_copy._make_flex_pol()
    sma_yy_copy.select(polarizations=["yy"], freq_chans=[0, 1, 2, 3])
    sma_yy_copy._make_flex_pol()

    # Add the two back together here, and make sure we can the same value out,
    # modulo the history.
    sma_check = sma_yy_copy + sma_xx_copy

    assert sma_check.history != sma_mir_filt.history
    sma_check.history = sma_mir_filt.history = None

    assert sma_check == sma_mir_filt


def test_flex_pol_spw_all_flag(sma_mir_filt):
    """
    Test that if one spw is totally flagged, the polarization gets filled correctly.
    """
    # Flag all of the data where we have y-pol data
    sma_mir_filt.flag_array[:, : sma_mir_filt.Nfreqs // 2, :] = True
    sma_mir_filt._make_flex_pol()

    # Since all of the y-pol data is flagged, the flex-pol data should only contain
    # xx visibilities (as recorded in flex_spw_polarization_array).t
    assert np.all(sma_mir_filt.flex_spw_polarization_array == -5)


def test_bad_sphid(mir_data):
    """
    Test what bad values for sphid in sp_data result in an error.
    """
    mir_obj = Mir()
    with uvtest.check_warnings(
        UserWarning, "Changing fields that tie to header keys can"
    ):
        mir_data.sp_data["sphid"] = -1

    with pytest.raises(KeyError) as err:
        mir_obj._init_from_mir_parser(mir_data)
    assert str(err.value).startswith("'Mismatch between keys in vis_data and sphid")


def test_bad_pol_code(mir_data):
    """
    Test that an extra (unused) pol code doesn't produce an error. Note that we want
    this check because the "Unknown" pol code is something present in some data sets.
    """
    mir_obj = Mir()
    mir_data.codes_data._data = np.resize(
        mir_data.codes_data._data, mir_data.codes_data._size + 1
    )
    mir_data.codes_data._data[-1] = ("pol", -999, "Unknown", 0)
    mir_data.codes_data._mask = np.ones(mir_data.codes_data._size, dtype=bool)

    mir_obj._init_from_mir_parser(mir_data)


def test_rechunk_on_read():
    """Test that rechunking on read works as expected."""
    testfile = os.path.join(DATA_PATH, "sma_test.mir")
    uv_data = UVData.from_file(testfile, rechunk=16384, use_future_array_shapes=True)

    # Do some basic checks to make sure that this loaded correctly.
    assert uv_data.freq_array.size == 8
    assert np.all(uv_data.channel_width == 2.288e09)


@pytest.mark.parametrize(
    "select_kwargs",
    [
        {"antenna_nums": [1, 4]},
        {"antenna_names": ["1", "4"]},
        {"bls": [(1, 4)]},
        {"time_range": [2459055, 2459056]},
        {"lst_range": [2, 2.5]},
        {"polarizations": ["hh", "vv"]},
        {"catalog_names": ["3c84"]},
        {"corrchunk": [1, 2, 3, 4]},
        {"receivers": ["230", "240"]},
        {"sidebands": ["l", "u"]},
    ],
)
def test_select_on_read(select_kwargs, sma_mir):
    testfile = os.path.join(DATA_PATH, "sma_test.mir")
    uv_data = UVData.from_file(testfile, use_future_array_shapes=True, **select_kwargs)
    uv_data.history = sma_mir.history
    assert sma_mir == uv_data


def test_non_icrs_coord_read(mir_data):
    # When fed a non-J2000 coordinate, we want to convert that so that it can easily
    mir_uv = UVData()
    mir_obj = Mir()

    # Plug in a dummy epoch value
    mir_data.in_data["epoch"] = 2020.0
    mir_obj._init_from_mir_parser(mir_data)
    mir_uv._convert_from_filetype(mir_obj)

    cat_entry = list(mir_uv.phase_center_catalog.values())[0]

    assert cat_entry["cat_frame"] == "icrs"
    assert cat_entry["cat_epoch"] is None
    assert np.isclose(cat_entry["cat_lon"], 0.8718033763283803, atol=4e-9, rtol=0)
    assert np.isclose(cat_entry["cat_lat"], 0.724518442710549, atol=4e-9, rtol=0)

    # Note that if the test dataset were written perfectly, the apparent coords would
    # perfectly translate back to the original values in the dataset, but there's a
    # defect of ~0.5 arcsec in how the positions were recorded, which we tolerate
    assert np.allclose(cat_entry["cat_lon"], mir_data.in_data["rar"], atol=4e-6, rtol=0)
    assert np.allclose(
        cat_entry["cat_lat"], mir_data.in_data["decr"], atol=4e-6, rtol=0
    )


def test_dedoppler_data(mir_data, sma_mir):
    mir_uv = UVData()
    mir_obj = Mir()

    # Need to unload data to redoppler
    mir_data.unload_data()

    # Deselect the pseudo-cont channel
    mir_data.select(where=("corrchunk", "ne", 0))

    # Now spoof a v4-style file
    mir_data.codes_data.set_value("code", "4", index=0)
    mir_data.sp_data["fDDS"] = 1.0

    # Complete the initialization of the UVData object
    mir_obj._init_from_mir_parser(mir_data, apply_dedoppler=True)
    mir_uv._convert_from_filetype(mir_obj)

    # Make sure things differ
    assert sma_mir != mir_uv

    # The only two things affected should be data_array and flag_array in the object,
    # everything else should be identical.
    mir_uv.data_array = sma_mir.data_array
    mir_uv.flag_array = sma_mir.flag_array
    assert sma_mir == mir_uv


def test_source_pos_change_warning(mir_data, tmp_path):
    # We need to spoof a new file to synthetically generate a two-integration dataset
    filepath = os.path.join(tmp_path, "source_pos_change_warning")
    with uvtest.check_warnings(UserWarning, "Writing out raw data with tsys applied."):
        mir_data.write(filepath)

    mir_copy = MirParser(filepath)

    # Now combine the data
    with uvtest.check_warnings(
        UserWarning,
        [
            "Duplicate metadata found for the following attributes",
            "These two objects contain data taken at the exact same time",
            "Both objects do not have auto-correlation data.",
        ],
    ):
        mir_copy.__iadd__(mir_data, force=True, merge=False)

    print(mir_copy.auto_data)

    # Muck the ra coord
    mir_copy.in_data["rar"] = [0, 1]
    mir_obj = Mir()

    with uvtest.check_warnings(
        UserWarning, "Position for 3c84 changes by more than an arcminute."
    ):
        mir_obj._init_from_mir_parser(mir_copy)


# pyuvdata/uvdata/tests/test_mir_meta_data.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2022 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for MirMetaData class and associated subclasses.

Performs a series of tests on the MirMetaData, and the associated subclasses, which are
the python-based handlers for Mir metadata products. Tests in this module are designed
to probe the functions of the individual class methods and attributes, and not
necessarily how they interact with each other (inside the `MirParser` class) or with
pyuvdata at large (via the `UVData` class).
"""
import os

import numpy as np
import pytest

from ... import tests as uvtest
from ..mir_meta_data import MirMetaData, MirMetaError


@pytest.fixture(scope="function")
def mir_in_data(mir_data_main):
    yield mir_data_main.in_data.copy()


@pytest.fixture(scope="function")
def mir_bl_data(mir_data_main):
    yield mir_data_main.bl_data.copy()


@pytest.fixture(scope="function")
def mir_sp_data(mir_data_main):
    yield mir_data_main.sp_data.copy()


@pytest.fixture(scope="function")
def mir_eng_data(mir_data_main):
    yield mir_data_main.eng_data.copy()


@pytest.fixture(scope="function")
def mir_codes_data(mir_data_main):
    yield mir_data_main.codes_data.copy()


@pytest.fixture(scope="function")
def mir_ac_data(mir_data_main):
    yield mir_data_main.ac_data.copy()


def test_mir_meta_init(mir_data):
    """
    Test that the initialization of MirMetaData objects behave as expected. This
    includes subtypes that are part of the MirParser class.
    """
    attr_list = list(mir_data._metadata_attrs.keys())
    # Drop "ac_data", since it's a synthetic table
    attr_list.remove("ac_data")

    for item in attr_list:
        # antpos_data is a little special, since it's a text file, so we can't use the
        # generic read function for MirMetaData here.
        attr = getattr(mir_data, item)
        if item != "antpos_data":
            # Read in the metadata w/ the generic read
            meta_attr = MirMetaData(
                attr._filetype,
                attr.dtype,
                attr._header_key,
                attr._binary_dtype,
                attr._pseudo_header_key,
                mir_data.filepath,
            )
            # Now make a placeholder subtype
            new_attr = type(attr)()

            # Plug in the __dict__, which _should_ transfer over all the writable attrs
            new_attr.__dict__ = meta_attr.__dict__
            assert attr == new_attr

        new_attr = type(attr)(mir_data.filepath)
        assert attr == new_attr


def test_mir_meta_iter(mir_data):
    """
    Test that MirMetaData objects iterate as expected, which is that they should yield
    the full group for each index position (similar to ndarray).
    """
    # Test the unflagged case
    in_data = mir_data.in_data
    for idx, item in enumerate(in_data):
        assert in_data._data[idx] == item

    # Now the flagged case
    in_data._mask[1::2] = False
    for idx, item in enumerate(in_data):
        assert in_data._data[2 * idx] == item


def test_mir_meta_copy(mir_in_data):
    """
    Verify that the copy operation of MirMetaData produces a duplicate dataset that is
    NOT a reference to the same data in memory.
    """
    other = mir_in_data.copy()
    for item in vars(other):
        this_attr = getattr(mir_in_data, item)
        other_attr = getattr(other, item)

        # Nones str, and tuple can be duplicates, since they're both immutable.
        if not (isinstance(this_attr, (str, tuple)) or this_attr is None):
            assert this_attr is not other_attr

        assert np.all(this_attr == other_attr)


@pytest.mark.parametrize(
    "comp_obj,err_msg", [[0, "Cannot compare MirInData with int."]]
)
def test_mir_meta_eq_errs(mir_in_data, comp_obj, err_msg):
    with pytest.raises(ValueError, match=err_msg):
        mir_in_data.__eq__(comp_obj)


@pytest.mark.parametrize("verbose", [True, False])
@pytest.mark.parametrize("result", [True, False])
def test_mir_meta_eq(mir_sp_data, result, verbose):
    copy_data = mir_sp_data.copy()
    comp_func = copy_data.__eq__ if result else copy_data.__ne__

    assert comp_func(mir_sp_data, verbose=verbose) == result

    # Now muck a single field
    copy_data._data["corrchunk"][::2] = -1
    assert comp_func(mir_sp_data, verbose=verbose) != result
    assert (
        comp_func(mir_sp_data, verbose=verbose, ignore_params=["corrchunk"]) == result
    )

    # Muck the param list to make sure things work okay
    assert (
        comp_func(mir_sp_data, verbose=verbose, ignore_params=["mjd", "corrchunk"])
        == result
    )

    # Make sure that mask diffs are also handled correctly.
    copy_data._mask[::2] = False
    assert comp_func(mir_sp_data, verbose=verbose, use_mask=True) != result

    # Now flag both datasets at the offending position
    mir_sp_data._mask[::2] = False
    assert comp_func(mir_sp_data, verbose=verbose, use_mask=True) == result

    # Check that diff data sizes are handled correctly
    copy_data._data = np.concatenate((copy_data._data, copy_data._data))
    assert comp_func(mir_sp_data, verbose=verbose) != result

    # Test the masks w/ a None
    copy_data._mask = None
    assert comp_func(mir_sp_data, verbose=verbose, use_mask=True) != result


@pytest.mark.parametrize(
    "field,op,err_type,err_msg",
    [
        ["blah", "eq", MirMetaError, "select_field blah not found"],
        ["inhid", "blah", ValueError, "select_comp must be one of:"],
    ],
)
def test_mir_meta_where_errs(mir_in_data, field, op, err_type, err_msg):
    with pytest.raises(err_type, match=err_msg):
        mir_in_data.where(field, op, [1])


@pytest.mark.parametrize("return_keys", [True, False])
@pytest.mark.parametrize(
    "tup,goodidx,headkeys",
    [
        [("corrchunk", "eq", 0), np.arange(0, 20, 5), np.arange(0, 20, 5) + 1],
        [("corrchunk", "==", 0), np.arange(0, 20, 5), np.arange(0, 20, 5) + 1],
        [("blhid", "ne", 4), np.arange(0, 15), np.arange(0, 15) + 1],
        [("blhid", "!=", 4), np.arange(0, 15), np.arange(0, 15) + 1],
        [("inhid", "between", [0, 2]), np.arange(20), np.arange(20) + 1],
        [("fres", "outside", [-1, 1]), np.arange(0, 20, 5), np.arange(0, 20, 5) + 1],
        [("ipq", "lt", [10]), np.arange(20), np.arange(20) + 1],
        [("ipq", "<", [10]), np.arange(20), np.arange(20) + 1],
        [("nch", "le", 4), np.arange(0, 20, 5), np.arange(0, 20, 5) + 1],
        [("nch", "<=", 4), np.arange(0, 20, 5), np.arange(0, 20, 5) + 1],
        [("dataoff", "gt", 0.0), np.arange(1, 20), np.arange(1, 20) + 1],
        [("dataoff", ">", 0.0), np.arange(1, 20), np.arange(1, 20) + 1],
        [("wt", "ge", 0.0), np.arange(20), np.arange(20) + 1],
        [("wt", ">=", 0.0), np.arange(20), np.arange(20) + 1],
    ],
)
def test_mir_meta_where(mir_sp_data, tup, goodidx, headkeys, return_keys):
    where_arr = mir_sp_data.where(*tup, return_header_keys=return_keys)
    assert np.all(where_arr == headkeys) if return_keys else np.all(where_arr[goodidx])


def test_mir_meta_where_mask(mir_sp_data):
    where_arr = mir_sp_data.where("inhid", "eq", 1)
    assert np.all(where_arr)

    mask = np.ones_like(mir_sp_data._mask)
    mask[::2] = False
    where_arr = mir_sp_data.where("inhid", "eq", 1, mask=mask)
    assert not np.any(where_arr[::2])
    assert np.all(where_arr[1::2])


def test_mir_meta_where_multidim(mir_data):
    we_mask = mir_data.we_data.where("flags", "eq", 0)
    assert len(we_mask) == len(mir_data.we_data)
    assert we_mask.ndim == 1
    assert np.all(we_mask)


def test_mir_meta_where_pseudo_key(mir_data):
    eng_keys = mir_data.eng_data.where("tsys", "gt", 0, return_header_keys=True)
    assert isinstance(eng_keys, list)
    for key in eng_keys:
        assert isinstance(key, tuple)
        assert key in mir_data.eng_data._header_key_index_dict


@pytest.mark.parametrize(
    "kwargs,err_type,err_msg",
    [
        [{"index": 0, "where": 0}, ValueError, "Only one of index, header_key"],
        [{"index": 0, "use_mask": True}, ValueError, "Cannot set use_mask=True"],
        [{"where": 0}, ValueError, "Argument for where must be either"],
        [{"where": [(0,)]}, ValueError, "Argument for where must be either"],
        [{"where": ("a", "eq", -1)}, MirMetaError, "Argument for where has no match"],
    ],
)
def test_mir_meta_index_query_errs(mir_in_data, kwargs, err_type, err_msg):
    with pytest.raises(err_type, match=err_msg):
        mir_in_data._index_query(**kwargs)


@pytest.mark.parametrize(
    "arg,output",
    [
        [{"index": [2, 4, 6]}, [2, 4, 6]],
        [{"header_key": [2, 4, 6]}, [1, 3, 5]],
        [{"where": ("inhid", "eq", -1)}, [False] * 20],
        [{"where": ("inhid", "eq", -1), "and_where_args": False}, [True] * 20],
        [{"where": [("inhid", "eq", 1), ("a", "eq", 1)]}, [True] * 20],
    ],
)
def test_mir_meta_index_query(mir_sp_data, arg, output):
    assert np.all(output == mir_sp_data._index_query(**arg))


@pytest.mark.parametrize(
    "field_name,err_type,err_msg",
    [[[0, 1], ValueError, "field_name must either be a str or list of str."]],
)
def test_mir_meta_get_value_errs(mir_in_data, field_name, err_type, err_msg):
    with pytest.raises(err_type, match=err_msg):
        mir_in_data.get_value(field_name)


@pytest.mark.parametrize(
    "field_name,arg,output",
    [
        ["inhid", {}, [1] * 20],
        ["corrchunk", {"index": [0, 5, 10, 15]}, [0] * 4],
        ["blhid", {"where": ("blhid", "eq", 2)}, [2] * 5],
        [
            ["blhid", "sphid"],
            {"index": [1, 4], "return_tuples": False},
            [[1, 1], [2, 5]],
        ],
        [["igq", "sphid"], {"index": [0, 3], "return_tuples": True}, [(0, 1), (0, 4)]],
    ],
)
def test_mir_meta_get_value(mir_sp_data, field_name, arg, output):
    assert np.all(
        np.array(output) == np.array(mir_sp_data.get_value(field_name, **arg))
    )


@pytest.mark.parametrize(
    "field_name,msg",
    [
        ["dataoff", 'Values in "dataoff" are typically only used'],
        ["inhid", "Changing fields that tie to header keys can result in"],
        ["blhid", "Changing fields that tie to header keys can result in"],
        ["sphid", "Changing fields that tie to header keys can result in"],
    ],
)
def test_mir_meta_set_value_warns(mir_sp_data, field_name, msg):
    with uvtest.check_warnings(UserWarning, msg):
        mir_sp_data.set_value(field_name, 1)


@pytest.mark.parametrize(
    "field_name,arg,set_value,output",
    [
        ["iant1", {}, 9, [9] * 4],
        ["iant1", {}, [1, 2, 3, 4], [1, 2, 3, 4]],
        ["iant2", {"index": 2}, 9, [4, 4, 9, 4]],
        ["iant2", {"header_key": 4}, 9, [4, 4, 4, 9]],
        ["iant2", {"header_key": 4}, 9, [4, 4, 4, 9]],
        ["u", {"where": ("u", "ne", 0)}, 0, [0, 0, 0, 0]],
    ],
)
def test_mir_meta_set_value(mir_bl_data, field_name, arg, set_value, output):
    mir_bl_copy = mir_bl_data.copy()
    mir_bl_data.set_value(field_name, set_value, **arg)
    assert np.all(mir_bl_data._data[field_name] == output)
    assert np.all(
        mir_bl_data._stored_values[field_name] == mir_bl_copy._data[field_name]
    )


@pytest.mark.parametrize(
    "arg,output",
    [
        [{}, True],
        [{"where": ("u", "ne", 0)}, True],
        [{"index": [1, 3]}, [False, True, False, True]],
        [{"header_key": [3, 4, 3]}, [False, False, True, True]],  # Guilty spark
    ],
)
def test_mir_meta_generate_mask(mir_bl_data, arg, output):
    assert np.all(mir_bl_data._generate_mask(**arg) == output)


@pytest.mark.parametrize(
    "arg,output",
    [
        [{"reset": True}, True],
        [{"where": (("u", "eq", 0), ("u", "ne", 0))}, False],
        [
            {"where": (("u", "eq", 0), ("u", "ne", 0)), "and_where_args": False},
            [True, False, False, True],
        ],
        [{"where": ("u", "ne", 0), "and_mask": False}, True],
        [{"index": [1, 3], "reset": True}, [False, True, False, True]],
        [{"header_key": [3, 4, 3]}, [False, False, False, True]],
        [
            {"mask": [True, False, True, False], "use_mask": False},
            [True, False, False, False],
        ],
    ],
)
def test_mir_meta_set_mask(mir_bl_data, arg, output):
    # Set the mask a priori to give us something to compare with.
    mir_bl_data._mask[:] = [True, False, False, True]

    check = mir_bl_data.set_mask(**arg)
    assert np.all(mir_bl_data._mask == output)
    assert ("reset" in arg) or (
        check == np.any(mir_bl_data._mask != [True, False, False, True])
    )


@pytest.mark.parametrize(
    "arg,output",
    [
        [{}, [True, False, False, True]],
        [{"where": ("u", "ne", 0)}, [True, False, False, True]],
        [{"index": [1, 3]}, [False, True]],
        [{"header_key": [3, 4, 3]}, [False, True, False]],
    ],
)
def test_mir_meta_get_mask(mir_bl_data, arg, output):
    mir_bl_data._mask[:] = [True, False, False, True]
    assert np.all(mir_bl_data.get_mask(**arg) == output)


@pytest.mark.parametrize(
    "arg,output",
    [
        [{}, [1, 2, 3, 4]],
        [{"where": ("u", "ne", 0)}, [1, 2, 3, 4]],
        [{"index": [1, 3]}, [2, 4]],
        [{"index": [1, 3], "force_list": True}, [[2, 4]]],
    ],
)
def test_mir_meta_get_header_keys(mir_bl_data, arg, output):
    assert np.all(np.array(mir_bl_data.get_header_keys(**arg)) == np.array(output))


def test_mir_meta_get_header_pseudo_keys(mir_eng_data):
    assert mir_eng_data.get_header_keys() == [(1, 1), (4, 1)]


def test_mir_meta_set_header_key_index_dict(mir_sp_data):
    mir_sp_data._set_header_key_index_dict()
    for key, value in mir_sp_data._header_key_index_dict.items():
        assert key == (value + 1)

    mir_sp_data._data["sphid"] = np.flip(mir_sp_data._data["sphid"])
    mir_sp_data._set_header_key_index_dict()
    for key, value in mir_sp_data._header_key_index_dict.items():
        assert (20 - key) == value


def test_mir_meta_generate_new_header_keys_err(mir_in_data):
    with pytest.raises(ValueError, match="Both objects must be of the same type."):
        mir_in_data._generate_new_header_keys(0)


def test_mir_meta_generate_new_header_keys_noop(mir_eng_data):
    assert mir_eng_data._generate_new_header_keys(mir_eng_data) == {}


def test_mir_meta_generate_new_header_keys(mir_bl_data):
    update_dict = mir_bl_data._generate_new_header_keys(mir_bl_data)
    assert update_dict == {"blhid": {1: 5, 2: 6, 3: 7, 4: 8}}


def test_mir_meta_sort_by_header_key(mir_bl_data):
    mir_bl_copy = mir_bl_data.copy()
    mir_bl_data._sort_by_header_key()
    assert mir_bl_copy == mir_bl_data

    mir_bl_data._data["blhid"] = np.flip(mir_bl_data._data["blhid"])
    mir_bl_data._sort_by_header_key()
    assert mir_bl_copy != mir_bl_data
    assert mir_bl_copy._header_key_index_dict == mir_bl_data._header_key_index_dict
    assert np.all(mir_bl_copy._data["blhid"] == mir_bl_copy._data["blhid"])


@pytest.mark.parametrize(
    "fields,args,mask_data,comp_dict",
    [
        ["inhid", {}, True, {}],
        ["inhid", {}, None, {1: list(range(1, 11))}],
        ["inhid", {"use_mask": False}, True, {1: list(range(1, 21))}],
        ["inhid", {}, False, {1: list(range(1, 21))}],
        ["inhid", {"return_index": True}, False, {1: list(range(20))}],
        [
            ["inhid", "corrchunk"],
            {"return_index": True},
            False,
            {
                (1, 0): [0, 5, 10, 15],
                (1, 1): [1, 6, 11, 16],
                (1, 2): [2, 7, 12, 17],
                (1, 3): [3, 8, 13, 18],
                (1, 4): [4, 9, 14, 19],
            },
        ],
    ],
)
def test_mir_meta_group_by(mir_sp_data, fields, args, mask_data, comp_dict):
    if mask_data:
        mir_sp_data._mask[:] = False
    elif mask_data is None:
        mir_sp_data._mask[10:] = False

    group_dict = mir_sp_data.group_by(fields, **args)

    assert group_dict.keys() == comp_dict.keys()

    for key in group_dict:
        assert np.all(group_dict[key] == comp_dict[key])


def test_mir_meta_reset_values_errs(mir_in_data):
    with pytest.raises(ValueError, match="No stored values for field foo."):
        mir_in_data.reset_values("foo")


@pytest.mark.parametrize("list_args", [True, False])
def test_mir_meta_reset_values(mir_sp_data, list_args):
    mir_sp_copy = mir_sp_data.copy()
    mir_sp_data["fsky"] = 0
    mir_sp_data["vres"] = 0

    assert mir_sp_data != mir_sp_copy
    assert np.all(mir_sp_data["fsky"] != mir_sp_copy["fsky"])
    assert np.all(mir_sp_data["vres"] != mir_sp_copy["vres"])

    if list_args:
        for item in ["fsky", "vres"]:
            mir_sp_data.reset_values(item)
    else:
        mir_sp_data.reset_values()

    assert mir_sp_data == mir_sp_copy
    assert mir_sp_data._stored_values == {}


def test_mir_meta_reset(mir_sp_data):
    mir_sp_copy = mir_sp_data.copy()
    mir_sp_data["fsky"] = 0
    mir_sp_data["wt"] = 0
    mir_sp_data._mask[:] = False

    assert mir_sp_data != mir_sp_copy

    mir_sp_data.reset()
    assert mir_sp_data == mir_sp_copy


@pytest.mark.parametrize(
    "update_dict,err_type,err_msg",
    [
        [{1: {1: 1}}, ValueError, "update_dict must have keys that are type str"],
        [{"foo": {1: 2}}, ValueError, "Field group foo not found in this object."],
    ],
)
def test_mir_meta_update_fields_errs(mir_in_data, update_dict, err_type, err_msg):
    with pytest.raises(err_type, match=err_msg):
        mir_in_data._update_fields(update_dict, raise_err=True)


def test_mir_meta_update_fields(mir_bl_data):
    mir_bl_copy = mir_bl_data.copy()
    update_dict = {
        "foo": {1: 1},
        "inhid": {1: 10},
        "blhid": {1: 0},
        ("iant1", "iant2"): {(1, 4): (4, 1)},
    }

    mir_bl_data._update_fields(update_dict)
    assert mir_bl_data != mir_bl_copy
    assert np.all(mir_bl_data["inhid"] == [10, 10, 10, 10])
    assert np.all(mir_bl_data["blhid"] == [0, 2, 3, 4])
    assert np.all(mir_bl_data["iant1"] == [4, 4, 4, 4])
    assert np.all(mir_bl_data["iant2"] == [1, 1, 1, 1])


def test_mir_meta_write_errs(mir_sp_data, tmp_path):
    filepath = os.path.join(tmp_path, "meta_write_errs")
    mir_sp_data.write(filepath)
    mir_sp_data["gunnLO"] = 0.0

    with pytest.raises(FileExistsError, match="File already exists, must set over"):
        mir_sp_data.write(filepath)

    with pytest.raises(ValueError, match="Conflicting header keys detected"):
        mir_sp_data.write(filepath, append_data=True, check_index=True)


def test_mir_meta_write_append(mir_sp_data, tmp_path):
    filepath = os.path.join(tmp_path, "meta_write_append")
    new_obj = type(mir_sp_data)()

    mir_sp_data.write(filepath)
    # Test the no-op
    mir_sp_data.write(filepath, append_data=True, check_index=True)
    new_obj.read(filepath)
    assert mir_sp_data == new_obj

    # Now try writing two separate halves of the data one at a time.
    mir_sp_data._mask[::2] = False
    mir_sp_data.write(filepath, overwrite=True)
    mir_sp_data._mask[::2] = True
    mir_sp_data._mask[1::2] = False
    mir_sp_data.write(filepath, append_data=True, check_index=True)
    new_obj.read(filepath)
    assert mir_sp_data == new_obj


@pytest.mark.parametrize(
    "cmd,args,err_type,err_msg",
    [
        ["int_other", {}, ValueError, "Both objects must be of the same type."],
        ["", {"merge": True, "discard_flagged": True}, ValueError, "Cannot both merge"],
        ["muck_key", {"merge": True}, ValueError, "Cannot merge if header keys"],
        ["muck_data", {}, MirMetaError, "Cannot combine objects, as both contain"],
        ["", {"merge": False}, MirMetaError, "Cannot add objects together if merge="],
    ],
)
def test_mir_meta_add_check_errs(mir_sp_data, cmd, args, err_type, err_msg):
    if cmd == "int_other":
        other = 0
    else:
        other = mir_sp_data.copy()
        if cmd == "muck_key":
            other._header_key_index_dict[-1] = -1
        elif cmd == "muck_data":
            other["gunnLO"] = -1.0

    with pytest.raises(err_type, match=err_msg):
        mir_sp_data._add_check(other, **args)


@pytest.mark.parametrize(
    "cmd,args,comp_results",
    [
        [["noop"], {"merge": True}, [[], [0, 1, 2, 3], [], [True] * 4]],
        [["comb_mod"], {}, [[0], [1, 2, 3], [True], [True] * 3]],
        [["mask_mod"], {}, [[0, 1], [2, 3], [True] * 2, [True] * 2]],
        [["mask_mod"], {"overwrite": True}, [[], [0, 1, 2, 3], [], [True] * 4]],
        [["flip_mod"], {"overwrite": True}, [[], [0, 1, 2, 3], [], [True] * 4]],
    ],
)
def test_mir_meta_add_check_merge(mir_bl_data, args, cmd, comp_results):
    mir_bl_copy = mir_bl_data.copy()
    # Use this as a way to mark the copy as altered
    if "comb_mod" in cmd:
        mir_bl_copy._data["u"][0] = 0.0
        mir_bl_copy._mask[0] = False
        mir_bl_data._data["u"][2] = 0.0
        mir_bl_data._mask[2] = False
    elif "flip_mod" in cmd:
        mir_bl_data["u"] = 0.0
    elif "noop" not in cmd:
        mir_bl_copy["u"] = 0.0

    if "mask_mod" in cmd:
        mir_bl_copy._mask[0:2] = False
        mir_bl_data._mask[2:4] = False

    result_tuple = mir_bl_data._add_check(mir_bl_copy, **args)
    for item, jtem in zip(result_tuple, comp_results):
        assert np.array_equal(item, jtem)


@pytest.mark.parametrize(
    "cmd,comp_results",
    [
        [["partial"], [[0, 1, 2, 3], [2, 3], [True] * 4, [False] * 2]],
        [["partial", "flip"], [[2, 3], [0, 1, 2, 3], [False] * 2, [True] * 4]],
        [["full"], [[0, 1, 2, 3], [0, 1, 2, 3], [True] * 4, [False] * 4]],
        [["full", "flip"], [[0, 1, 2, 3], [0, 1, 2, 3], [False] * 4, [True] * 4]],
    ],
)
def test_mir_meta_add_check_concat(mir_bl_data, cmd, comp_results):
    mir_bl_copy = mir_bl_data.copy()
    # Use this as a way to mark the copy as altered
    if "partial" in cmd:
        mir_bl_copy._data["blhid"][2:4] = [7, 8]
    elif "full" in cmd:
        mir_bl_copy._data["blhid"][:] = [5, 6, 7, 8]

    mir_bl_copy._set_header_key_index_dict()
    mir_bl_copy._mask[:] = False

    if "flip" in cmd:
        result_tuple = mir_bl_copy._add_check(mir_bl_data)
    else:
        result_tuple = mir_bl_data._add_check(mir_bl_copy)

    for item, jtem in zip(result_tuple, comp_results):
        assert np.array_equal(item, jtem)


def test_mir_meta_add_errs(mir_in_data):
    with pytest.raises(ValueError, match="Both objects must be of the same type."):
        mir_in_data += 0


@pytest.mark.parametrize("this_none", [True, False])
@pytest.mark.parametrize("other_none", [True, False])
@pytest.mark.parametrize("method", ["__add__", "__iadd__"])
def test_mir_meta_add_none(mir_in_data, this_none, other_none, method):
    this = mir_in_data.copy(skip_data=this_none)
    other = mir_in_data.copy(skip_data=other_none)

    result = getattr(this, method)(other)

    if this_none and other_none:
        assert (this == result) and (other == result)
    else:
        assert mir_in_data == result


@pytest.mark.parametrize("method", ["__add__", "__iadd__"])
def test_mir_meta_add_concat(mir_bl_data, method):
    mir_bl_copy = mir_bl_data.copy()
    mir_bl_copy._data["blhid"] = [1, 3, 5, 7]
    mir_bl_copy._data["u"] = [15, 16, 17, 18]
    mir_bl_copy._mask[:] = [True] * 4
    mir_bl_copy._set_header_key_index_dict()

    mir_bl_data._data["blhid"] = [2, 4, 6, 8]
    mir_bl_data._data["u"] = [11, 12, 13, 14]
    mir_bl_data._mask[:] = [False] * 4
    mir_bl_data._set_header_key_index_dict()

    result = getattr(mir_bl_data, method)(mir_bl_copy)
    if method == "__iadd__":
        assert mir_bl_data is result

    assert np.all(result._data["u"] == np.arange(11, 19))
    assert np.all(~result._mask[:4])
    assert np.all(result._mask[4:])


def test_mir_sp_recalc_dataoff(mir_sp_data):
    dataoff_arr = mir_sp_data["dataoff"].copy()

    # Now update one mask position, a pseudo-cont record
    pseudo_rec_size = 18
    mir_sp_data._mask[0] = False
    mir_sp_data._recalc_dataoff()
    assert np.all(mir_sp_data["dataoff"] == (dataoff_arr[1:] - pseudo_rec_size))

    # Now flag all the pseudo-cont records
    mir_sp_data._mask[[0, 5, 10, 15]] = False
    mir_sp_data._recalc_dataoff()
    assert np.all(
        mir_sp_data["dataoff"]
        == np.concatenate(
            (
                dataoff_arr[1:5] - (1 * pseudo_rec_size),
                dataoff_arr[6:10] - (2 * pseudo_rec_size),
                dataoff_arr[11:15] - (3 * pseudo_rec_size),
                dataoff_arr[16:20] - (4 * pseudo_rec_size),
            )
        )
    )

    # Finally, ignore the mask when doing dataoff and make sure it returns correctly
    mir_sp_data._recalc_dataoff(use_mask=False)
    assert np.all(mir_sp_data._data["dataoff"] == dataoff_arr)


def test_mir_meta_get_record_size_info_errs():
    with pytest.raises(TypeError, match="Cannot use this method on objects other than"):
        MirMetaData(None, None, None)._get_record_size_info()


@pytest.mark.parametrize(
    "attr,val_size,rec_size_arr",
    [["sp_data", 2, ([18] + ([65538] * 4)) * 4], ["ac_data", 4, [65536] * 16]],
)
def test_mir_meta_get_record_size_info(mir_data, attr, val_size, rec_size_arr):
    comp_rec_size, comp_val = getattr(mir_data, attr)._get_record_size_info()
    assert val_size == comp_val
    assert np.all(comp_rec_size == rec_size_arr)


@pytest.mark.parametrize("reindex", [True, False])
@pytest.mark.parametrize("use_mask", [True, False])
@pytest.mark.parametrize("mod_mask", [True, False])
def test_mir_meta_generate_recpos_dict(mir_sp_data, reindex, use_mask, mod_mask):
    if mod_mask:
        mir_sp_data._mask[::2] = False

    dataoff_arr = mir_sp_data.get_value("dataoff", use_mask=use_mask).astype(int) // 2
    rec_size_arr = (mir_sp_data.get_value("nch", use_mask=use_mask).astype(int) * 2) + 1

    if use_mask and mod_mask and reindex:
        dataoff_arr = np.cumsum(rec_size_arr) - rec_size_arr

    int_dict, sp_dict = mir_sp_data._generate_recpos_dict(
        use_mask=use_mask, reindex=reindex
    )
    assert int_dict == {
        1: {
            "inhid": 1,
            "record_size": 1048680 // (1 + (use_mask and mod_mask and reindex)),
            "record_start": 0,
        }
    }
    for key in int_dict:
        sp_dict = sp_dict[key]
        for value, dataoff, recsize in zip(sp_dict.values(), dataoff_arr, rec_size_arr):
            assert value["start_idx"] == dataoff
            assert value["end_idx"] == dataoff + recsize
            assert value["chan_avg"] == 1

    assert list(mir_sp_data.get_value("sphid", use_mask=use_mask)) == list(sp_dict)


def test_mir_codes_get_code_names(mir_codes_data):
    assert sorted(mir_codes_data.get_code_names()) == sorted(
        [
            "aq",
            "band",
            "blcd",
            "bq",
            "cocd",
            "cq",
            "dec",
            "filever",
            "gq",
            "ifc",
            "offtype",
            "oq",
            "pol",
            "pos",
            "pq",
            "project",
            "pstate",
            "ra",
            "rec",
            "ref_time",
            "sb",
            "source",
            "stype",
            "svtype",
            "taper",
            "tel1",
            "tel2",
            "tq",
            "trans",
            "ut",
            "vctype",
            "vrad",
        ]
    )


@pytest.mark.parametrize(
    "args,kwargs,err_type,err_msg",
    [
        [(0, 0, 0), {}, MirMetaError, "select_field must either be one of the native"],
        [("source", "gt", 0), {}, ValueError, 'select_comp must be "eq", "==", "ne"'],
    ],
)
def test_mir_codes_where_errs(mir_codes_data, args, kwargs, err_type, err_msg):
    with pytest.raises(err_type, match=err_msg):
        mir_codes_data.where(*args, **kwargs)


@pytest.mark.parametrize(
    "args,kwargs,output",
    [
        [("source", "eq", "3c84"), {}, [1]],
        [("pol", "ne", ["hh", "vv"]), {}, [2, 3]],
        [("tel1", "eq", [1, 5, 8]), {}, [1, 5, 8]],
        [
            ("vrad", "eq", "         0.0"),
            {"return_header_keys": False},
            (92 * [False]) + [True] + (6 * [False]),
        ],
    ],
)
def test_mir_codes_where(mir_codes_data, args, kwargs, output):
    assert np.all(output == mir_codes_data.where(*args, **kwargs))


def test_mir_codes_get_item_err(mir_codes_data):
    with pytest.raises(MirMetaError, match="foo does not match any code or field"):
        mir_codes_data["foo"]


@pytest.mark.parametrize(
    "vname,output",
    [
        ["filever", ["3"]],
        ["source", {1: "3c84", "3c84": 1}],
        ["sb", {"l": 0, "u": 1, 0: "l", 1: "u"}],
        ["ut", {"Jul 24 2020  4:34:39.00PM": 1, 1: "Jul 24 2020  4:34:39.00PM"}],
    ],
)
def test_mir_codes_get_item(mir_codes_data, vname, output):
    assert output == mir_codes_data[vname]


@pytest.mark.parametrize("name", ["v_name", "icode", "code", "ncode"])
def test_mir_codes_get_item_dtype(mir_codes_data, name):
    assert np.all(mir_codes_data[name] == mir_codes_data._data[name])


def test_mir_codes_generate_new_header_keys_errs_and_warns(mir_codes_data):
    # This _could_ be parameterized, although each test is so customized that
    # it's easier to code this as a single passthrough.
    with pytest.raises(ValueError, match="Both objects must be of the same type."):
        mir_codes_data._generate_new_header_keys(0)

    mir_codes_copy = mir_codes_data.copy()
    mir_codes_copy.set_value("code", "1", where=("v_name", "eq", "filever"))
    with pytest.raises(ValueError, match="The codes for filever in codes_read"):
        mir_codes_data._generate_new_header_keys(mir_codes_copy)

    mir_codes_copy.set_value("code", "3", where=("v_name", "eq", "filever"))

    mir_codes_copy.set_value("code", ["3", "4", "5"], where=("v_name", "eq", "aq"))
    with uvtest.check_warnings(UserWarning, "Codes for aq not in the recognized list"):
        check_dict = mir_codes_data._generate_new_header_keys(mir_codes_copy)

    assert list(check_dict) == [("v_name", "icode")]
    check_dict = check_dict[("v_name", "icode")]
    assert check_dict == {
        ("aq", 0): ("aq", 3),
        ("aq", 1): ("aq", 4),
        ("aq", 2): ("aq", 5),
    }


@pytest.mark.parametrize(
    "code_row,update_dict",
    [
        [("source", 1, "3c84", 0), {}],
        [
            ("source", 1, "3c279", 0),
            {
                "isource": {1: 2},
                ("v_name", "icode"): {
                    ("source", 1): ("source", 2),
                    ("stype", 1): ("stype", 2),
                    ("svtype", 1): ("svtype", 2),
                },
            },
        ],
        [
            ("stype", 1, "ephem", 0),
            {
                "isource": {1: 2},
                ("v_name", "icode"): {
                    ("source", 1): ("source", 2),
                    ("stype", 1): ("stype", 2),
                    ("svtype", 1): ("svtype", 2),
                },
            },
        ],
        [
            ("project", 2, "retune", 0),
            {"iproject": {2: 1}, ("v_name", "icode"): {("project", 2): ("project", 1)}},
        ],
        [("project", 2, "do_a_thing", 0), {}],
    ],
)
def test_mir_codes_generate_new_header_keys(mir_codes_data, code_row, update_dict):
    mir_codes_copy = mir_codes_data.copy()
    mir_codes_copy._data[mir_codes_data.where("v_name", "eq", code_row[0])] = code_row

    assert update_dict == mir_codes_copy._generate_new_header_keys(mir_codes_data)


def test_mir_acdata_read_errs(mir_data):
    with pytest.raises(AssertionError) as err:
        mir_data.ac_data.read(mir_data.filepath, nchunks=1)
    str(err.value).startswith("Could not determine auto-correlation record size.")


def test_mir_make_key_mask_cipher(mir_ac_data, mir_eng_data):
    assert np.array_equal(
        mir_eng_data._make_key_mask(mir_ac_data),
        mir_eng_data._make_key_mask(mir_ac_data, use_cipher=False),
    )


def test_mir_make_key_mask(mir_in_data, mir_bl_data, mir_sp_data):
    assert not mir_in_data._make_key_mask(mir_bl_data)
    assert not mir_in_data._make_key_mask(mir_sp_data)
    assert not mir_bl_data._make_key_mask(mir_sp_data)
    assert not mir_in_data._make_key_mask(mir_in_data)
    assert not mir_bl_data._make_key_mask(mir_bl_data)
    assert not mir_sp_data._make_key_mask(mir_sp_data)

    # Modify the mask for baselines
    mir_bl_data._mask[::2] = False

    # in_data mask should not change
    assert not mir_in_data._make_key_mask(mir_bl_data)

    # But sp_data mask should!
    assert mir_sp_data._make_key_mask(mir_bl_data, reverse=True)

    assert np.array_equal(mir_sp_data["sphid"], [6, 7, 8, 9, 10, 16, 17, 18, 19, 20])

    # Finally, set the single ingtegration as bad, and make sure it cascades down
    mir_in_data._mask[:] = False
    mir_bl_data._make_key_mask(mir_in_data, reverse=True)
    mir_sp_data._make_key_mask(mir_bl_data, reverse=True)

    assert not any(mir_in_data._mask)
    assert not any(mir_bl_data._mask)
    assert not any(mir_sp_data._mask)


# pyuvdata/uvdata/tests/test_mir_parser.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2020 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for MirParser class.

Performs a series of tests on the MirParser, which is the python-based reader for MIR
data in pyuvdata. Tests in this module are specific to the way that MIR is read into
python, not necessarily how pyuvdata (by way of the UVData class) interacts with that
data.
"""
import os

import h5py
import numpy as np
import pytest

from ... import tests as uvtest
from ..mir_parser import MirMetaError, MirParser


@pytest.fixture(scope="module")
def compass_soln_file(tmp_path_factory):
    tmp_path = tmp_path_factory.mktemp("mir_parser", numbered=True)
    filename = os.path.join(tmp_path, "compass_soln.mat")
    with h5py.File(filename, "w") as file:
        # Set up some basic indexing for our one-baseline test file
        file["antArr"] = np.array([[1, 4]])
        file["ant1Arr"] = np.array([[1]])
        file["ant2Arr"] = np.array([[4]])
        file["rx1Arr"] = np.repeat([0, 0, 1, 1], 4).reshape(1, -1)
        file["rx2Arr"] = np.repeat([0, 0, 1, 1], 4).reshape(1, -1)
        file["sbArr"] = np.repeat([0, 1, 0, 1], 4).reshape(1, -1)
        file["winArr"] = np.tile([[1, 2, 3, 4]], 4)

        # Make a set of bp solns that are easy to recreate in the test (to verify
        # that we actually have the solutions that we expect).
        bp_soln = np.arange(16 * 16384) + (np.flip(np.arange(16 * 16384)) * 1j)

        file["bandpassArr"] = np.reshape(
            np.concatenate((bp_soln, np.conj(np.reciprocal(bp_soln)))), (2, 16, 16384)
        ).astype(np.complex64)

        # This number is pulled from the test mir_data object, in in_data["mjd"].
        file["mjdArr"] = np.array([[59054.69153811]])

        # Set up a picket fence of flags for the "normal" flagging. Note we use
        # uint8 here because of the compression scheme COMPASS uses.
        file["flagArr"] = np.full((1, 1, 16, 2048), 170, dtype=np.uint8)

        # Set up the wide flags so that the first half of the spectrum is flagged.
        file["wideFlagArr"] = np.tile(
            ((np.arange(2048) < 1024) * 255).reshape(1, 1, -1).astype(np.uint8),
            (1, 16, 1),
        )

    yield filename


def test_mir_parser_index_uniqueness(mir_data):
    """
    Mir index uniqueness check

    Make sure that there are no duplicate indices for things that are primary keys
    for the various table-like structures that are used in MIR
    """
    inhid_list = mir_data.in_data["inhid"]
    assert np.all(np.unique(inhid_list) == sorted(inhid_list))

    blhid_list = mir_data.bl_data["blhid"]
    assert np.all(np.unique(blhid_list) == sorted(blhid_list))

    sphid_list = mir_data.sp_data["sphid"]
    assert np.all(np.unique(sphid_list) == sorted(sphid_list))


def test_mir_parser_index_valid(mir_data):
    """
    Mir index validity check

    Make sure that all indexes are non-negative
    """
    assert np.all(mir_data.in_data["inhid"] >= 0)

    assert np.all(mir_data.bl_data["blhid"] >= 0)

    assert np.all(mir_data.sp_data["sphid"] >= 0)


def test_mir_parser_index_linked(mir_data):
    """
    Mir index link check

    Make sure that all referenced indices have matching pairs in their parent tables
    """
    inhid_set = set(np.unique(mir_data.in_data["inhid"]))

    # Should not exist is has_auto=False
    # See `mir_data_object` above.
    if mir_data.ac_data is not None:
        assert set(np.unique(mir_data.ac_data["inhid"])).issubset(inhid_set)
    else:
        # This should only occur when has_auto=False
        assert not mir_data._has_auto

    assert set(np.unique(mir_data.bl_data["inhid"])).issubset(inhid_set)

    assert set(np.unique(mir_data.eng_data["inhid"])).issubset(inhid_set)

    assert set(np.unique(mir_data.eng_data["inhid"])).issubset(inhid_set)

    assert set(np.unique(mir_data.sp_data["inhid"])).issubset(inhid_set)

    blhid_set = set(np.unique(mir_data.bl_data["blhid"]))

    assert set(np.unique(mir_data.sp_data["blhid"])).issubset(blhid_set)


def test_mir_parser_unload_data(mir_data):
    """
    Check that the unload_data function works as expected
    """
    # Spoof for just this test
    mir_data.raw_data = {1: {"data": [1] * 16384, "scale_fac": [1]}}

    attr_list = ["vis_data", "raw_data", "auto_data"]

    for attr in attr_list:
        assert getattr(mir_data, attr) is not None

    mir_data.unload_data()

    for attr in attr_list:
        assert getattr(mir_data, attr) is None


@pytest.mark.parametrize(
    "attr",
    [
        "antpos_data",
        "bl_data",
        "codes_data",
        "eng_data",
        "in_data",
        "sp_data",
        "we_data",
        "ac_data",
    ],
)
def test_mir_write_item(mir_data, attr, tmp_path):
    """
    Mir write tester.

    Test writing out individual components of the metadata of a MIR dataset.
    """
    filepath = os.path.join(tmp_path, "test_write%s" % attr)
    orig_attr = getattr(mir_data, attr)
    orig_attr.write(filepath)
    check_attr = orig_attr.copy(skip_data=True)
    check_attr.read(filepath)
    assert orig_attr == check_attr


def test_mir_write_vis_data_err(mir_data, tmp_path):
    mir_data.unload_data()
    with pytest.raises(ValueError, match="Cannot write data if not already loaded."):
        mir_data._write_cross_data(tmp_path)


def test_mir_raw_data(mir_data, tmp_path):
    """
    Test reading and writing of raw data.
    """
    filepath = os.path.join(tmp_path, "test_write_raw")
    mir_data.load_data(load_raw=True)

    mir_data._write_cross_data(filepath)
    # Sub out the file we need to read from
    mir_data._file_dict = {filepath: item for item in mir_data._file_dict.values()}
    raw_data = mir_data._read_data("cross", return_vis=False)

    assert raw_data.keys() == mir_data.raw_data.keys()

    for key in raw_data.keys():
        for subkey in ["data", "scale_fac"]:
            assert np.array_equal(raw_data[key][subkey], mir_data.raw_data[key][subkey])


def test_mir_auto_data_errs(mir_data):
    mir_data.unload_data()
    with pytest.raises(ValueError, match="Cannot write data if not already loaded."):
        mir_data._write_auto_data(None)


def test_mir_auto_data(mir_data, tmp_path):
    """
    Test reading and writing of auto data.
    """
    filepath = os.path.join(tmp_path, "test_write_auto")

    mir_data._write_auto_data(filepath)
    # Sub out the file we need to read from, and fix a couple of attributes that changed
    # since we are no longer spoofing values (after reading in data from old-style file)
    mir_data._file_dict = {filepath: item for item in mir_data._file_dict.values()}
    mir_data._file_dict[filepath]["auto"]["filetype"] = "ach_read"
    int_dict, mir_data._ac_dict = mir_data.ac_data._generate_recpos_dict(reindex=True)
    mir_data._file_dict[filepath]["auto"]["int_dict"] = int_dict
    auto_data = mir_data._read_data("auto")

    assert auto_data.keys() == mir_data.auto_data.keys()

    for key in auto_data.keys():
        for subkey in ["data", "flags"]:
            assert np.array_equal(
                auto_data[key][subkey], mir_data.auto_data[key][subkey]
            )


@pytest.mark.filterwarnings("ignore", message=["No cross data", "No auto data"])
@pytest.mark.parametrize("data_type", ["none", "raw", "vis", "load", "no_auto"])
def test_mir_write_full(mir_data, tmp_path, data_type):
    """
    Mir write dataset tester.

    Make sure we can round-trip a MIR dataset correctly.
    """
    # We want to clear our the auto data here, since we can't _yet_ write that out
    mir_data.unload_data()
    if data_type == "no_auto":
        mir_data._clear_auto()

    if data_type in ["vis", "raw", "no_auto"]:
        mir_data.load_data(load_raw=(data_type == "raw"), apply_tsys=False)

    # Write out our test dataset
    filepath = os.path.join(tmp_path, "test_write_full_%s.mir" % data_type)

    mir_data.write(filepath, load_data=(data_type == "load"))
    with uvtest.check_warnings(
        None if (data_type != "none") else UserWarning,
        None if (data_type != "none") else ["No cross data", "No auto data"],
    ):
        mir_data.write(filepath, load_data=(data_type == "load"))

    # Read in test dataset.
    mir_copy = MirParser(filepath, has_auto=mir_data._has_auto)
    if data_type != "none":
        mir_copy.load_data(load_raw=(data_type in ["raw", "load"]), apply_tsys=False)

    # The objects won't be equal off the bat - a couple of things to handle first.
    assert mir_data != mir_copy

    # _file_dict has the filepath as a key, so we handle this in a special way.
    assert mir_data._file_dict.values() != mir_copy._file_dict.values()
    mir_data._file_dict = mir_copy._file_dict = None

    # Filename obviously _should_ be different...
    assert mir_data.filepath != mir_copy.filepath
    mir_data.filepath = mir_copy.filepath = None

    # Take care of some auto-specific stuff, which because we spoofed the original
    # ac_data attribute, won't be _exactly_ the same.
    if mir_copy._has_auto:
        assert mir_data._ac_dict.values() != mir_copy._ac_dict.values()
        mir_data._ac_dict = mir_copy._ac_dict = None
        assert np.any(mir_data.ac_data["dataoff"] != mir_copy.ac_data._data["dataoff"])
        mir_data.ac_data._data["dataoff"] = mir_copy.ac_data._data["dataoff"] = 0

    # Check for final equality with the above exceptions handled.
    assert mir_data == mir_copy


def test_compass_flag_sphid_apply(mir_data, compass_soln_file):
    """
    Test COMPASS per-sphid flagging.

    Test that applying COMPASS flags on a per-sphid basis works as expected.
    """
    # Unflag previously flagged data
    for entry in mir_data.vis_data.values():
        entry["flags"][:] = False

    compass_solns = mir_data._read_compass_solns(compass_soln_file)
    mir_data._apply_compass_solns(compass_solns, apply_bp=False, apply_flags=True)
    for key, entry in mir_data.vis_data.items():
        if mir_data.sp_data.get_value("corrchunk", header_key=key) != 0:
            assert np.all(entry["flags"][1::2])
            assert not np.any(entry["flags"][::2])


def test_compass_flag_wide_apply(mir_data, compass_soln_file):
    """
    Test COMPASS wide flagging.

    Test that applying COMPASS flags on a per-baseline (all time) basis works correctly.
    """
    # Make sure that a priori flags are preserved
    for entry in mir_data.vis_data.values():
        entry["flags"][:] = False
        entry["flags"][-1] = True

    mir_data.in_data["mjd"] += 1
    with uvtest.check_warnings(
        UserWarning, "No metadata from COMPASS matches that in this data set."
    ):
        compass_solns = mir_data._read_compass_solns(compass_soln_file)

    mir_data._apply_compass_solns(compass_solns, apply_bp=False, apply_flags=True)

    for key, entry in mir_data.vis_data.items():
        if mir_data.sp_data.get_value("corrchunk", header_key=key) != 0:
            assert np.all(entry["flags"][:8192])
            assert not np.any(entry["flags"][8192:-1])
            assert np.all(entry["flags"][-1])


@pytest.mark.parametrize("muck_solns", ["none", "some", "all"])
def test_compass_bp_apply(mir_data, compass_soln_file, muck_solns):
    """
    Test COMPASS bandpass calibration.

    Test that applying COMPASS bandpass solutions works correctly.
    """
    tempval = np.complex64(1 + 1j)
    for entry in mir_data.vis_data.values():
        entry["data"][:] = tempval
        entry["flags"][:] = False

    if muck_solns != "none":
        mir_data.bl_data["iant1"] += 1
        if muck_solns == "all":
            mir_data.bl_data["iant2"] += 1

    with uvtest.check_warnings(
        None if (muck_solns == "none") else UserWarning,
        None if (muck_solns == "none") else "No metadata from COMPASS matches",
    ):
        compass_solns = mir_data._read_compass_solns(compass_soln_file)

    mir_data._apply_compass_solns(compass_solns, apply_bp=True, apply_flags=False)

    for key, entry in mir_data.vis_data.items():
        if mir_data.sp_data.get_value("corrchunk", header_key=key) != 0:
            # If muck_solns is not some, then all the values should agree with our
            # temp value above, otherwise none should
            assert (muck_solns != "some") == np.allclose(entry["data"], tempval)
            assert (muck_solns != "none") == np.all(entry["flags"])


def test_compass_error(mir_data, compass_soln_file):
    """
    Test COMPASS-related errors.

    Verify that known error conditions trigger expected errors.
    """
    mir_data.unload_data()

    compass_solns = mir_data._read_compass_solns(compass_soln_file)

    with pytest.raises(ValueError, match="Visibility data must be loaded"):
        mir_data._apply_compass_solns(compass_solns)


@pytest.mark.parametrize(
    "field,comp,value,vis_keys",
    [
        ["mjd", "between", [60000.0, 50000.0], np.arange(1, 21)],
        ["source", "ne", "nosourcehere", np.arange(1, 21)],
        ["ant", "eq", 4, np.arange(1, 21)],
        ["ant1", "!=", 8, np.arange(1, 21)],
        ["ant1rx", "==", 0, [1, 2, 3, 4, 5, 11, 12, 13, 14, 15]],
        ["corrchunk", "ne", [1, 2, 3, 4], np.arange(1, 21, 5)],
        ["N", "lt", 0.0, []],
    ],
)
def test_select(mir_data, field, comp, value, vis_keys):
    """Verify that select throws warnings as expected."""
    mir_data.select(where=(field, comp, value))

    # Confirm that we have all the indexes we should internally
    assert mir_data._check_data_index()

    # Cross-reference with the list we provide to be sure we have everything.
    assert np.all(np.isin(list(mir_data.vis_data), vis_keys))


def test_select_reset(mir_data):
    """Verify that running reset with select returns all entries as expected."""
    mir_copy = mir_data.copy()

    # Select based on something that should not exist.
    mir_data.select(where=("mjd", "eq", 0.0))
    assert len(mir_data.vis_data) == 0

    # Now run reset
    mir_data.select(reset=True, update_data=True)
    assert mir_data == mir_copy


def test_eq_errs(mir_data):
    """Verify that the __eq__ method throws appropriate errors."""
    with pytest.raises(ValueError, match="Cannot compare MirParser with int."):
        mir_data.__eq__(0)


@pytest.mark.parametrize(
    "metadata_only,mod_attr,mod_val,exp_state",
    [
        [False, "auto_data", {}, False],
        [False, "auto_data", {1: np.zeros(4), 2: np.zeros(4)}, False],
        [True, "auto_data", {1: np.zeros(4), 2: np.zeros(4)}, True],
        [
            False,
            "vis_data",
            {
                idx: {
                    "data": np.ones(2, dtype=np.complex64),
                    "flags": np.ones(4, dtype=bool),
                }
                for idx in range(1, 21)
            },
            False,
        ],
        [False, "in_data", np.array([1, 2, 3, 4]), False],
        [True, "in_data", np.array([1, 2, 3, 4]), False],
        [True, "abc", "def", False],
        [False, "abc", "def", False],
        [False, "_has_auto", True, True],
        [False, "_has_auto", False, False],
        [False, "_has_auto", None, False],
        [True, "zero_data", None, True],
        [False, "zero_data", None, False],
        [False, "unload_data", None, True],
        [False, "meta_attr", None, False],
    ],
)
@pytest.mark.parametrize("flip", [False, True])
def test_eq(mir_data, metadata_only, mod_attr, mod_val, exp_state, flip):
    """Verify that __eq__ works as expected"""
    mir_copy = mir_data.copy()

    target_obj = mir_copy if flip else mir_data
    if "zero_data" == mod_attr:
        for attr in ["vis_data", "auto_data"]:
            for key in getattr(target_obj, attr).keys():
                if isinstance(getattr(target_obj, attr)[key], dict):
                    for subkey in getattr(target_obj, attr)[key].keys():
                        if subkey == "scale_fac":
                            getattr(target_obj, attr)[key][subkey] = 0
                        else:
                            getattr(target_obj, attr)[key][subkey][:] = 0
                else:
                    getattr(target_obj, attr)[key][:] = 0
    elif "unload_data" == mod_attr:
        mir_data.unload_data()
        mir_copy.unload_data()
    elif "meta_attr" == mod_attr:
        del mir_copy._metadata_attrs["ac_data"]
    else:
        setattr(target_obj, mod_attr, mod_val)

    assert mir_data.__eq__(mir_copy, metadata_only=metadata_only) == exp_state

    assert mir_data.__ne__(mir_copy, metadata_only=metadata_only) != exp_state


def test_scan_int_start_errs(mir_data):
    """Verify _scan_int_start throws errors when expected."""
    with pytest.raises(ValueError, match="Index value inhid in sch_read does not "):
        mir_data._scan_int_start(
            os.path.join(mir_data.filepath, "sch_read"), allowed_inhid=[-1]
        )


def test_scan_int_start(mir_data):
    """Verify that we can correctly scan integration starting periods."""
    true_dict = {1: {"inhid": 1, "record_size": 1048680, "record_start": 0}}
    assert true_dict == mir_data._scan_int_start(
        os.path.join(mir_data.filepath, "sch_read"), allowed_inhid=[1]
    )


def test_fix_int_dict(mir_data):
    """Verify that we can fix a "bad" integration start record."""
    bad_entry = {2: {"inhid": 1, "record_size": 120, "record_start": 120}}

    good_dict = {
        mir_data.filepath: {
            "cross": {
                "int_dict": {
                    2: {"inhid": 1, "record_size": 1048680, "record_start": 0}
                },
                "filetype": "sch_read",
                "ignore_header": False,
            }
        }
    }

    # First, check that doing the autos throws a warning since it's a not a
    # "true" int_dict but instead is synthetically generated
    with uvtest.check_warnings(UserWarning, "Cannot fix auto file headers for "):
        mir_data._fix_int_dict("auto")

    # Muck with the records so that the inhid does not match that on disk.
    mir_data.sp_data._data["inhid"][:] = 2
    mir_data.bl_data._data["inhid"][:] = 2
    mir_data.in_data._data["inhid"][:] = 2
    mir_data.sp_data._data["nch"][:] = 1
    mir_data._sp_dict[2] = mir_data._sp_dict.pop(1)

    # Plug in the bad entry
    mir_data._file_dict = good_dict
    mir_data._file_dict[mir_data.filepath]["cross"]["int_dict"] = bad_entry.copy()
    # This should _hopefully_ generate the good dict
    mir_data._fix_int_dict("cross")
    assert good_dict == mir_data._file_dict

    # Plug in the bad entry again
    mir_data._file_dict[mir_data.filepath]["cross"]["int_dict"] = bad_entry.copy()
    with uvtest.check_warnings(UserWarning, "Values in int_dict do not match"):
        mir_data._read_data("cross", return_vis=False)

    assert good_dict == mir_data._file_dict

    # Attempt to load the data
    _ = mir_data._read_data("cross", return_vis=False)


def test_read_packdata_err(mir_data):
    with pytest.raises(ValueError, match="inhid_arr contains keys not found in file_"):
        mir_data._read_packdata(mir_data._file_dict, [1, 2])


def test_read_packdata_mmap(mir_data):
    """Test that reading in vis data with mmap works just as well as np.read"""
    mmap_data = mir_data._read_packdata(
        mir_data._file_dict, mir_data.in_data["inhid"], use_mmap=True
    )

    reg_data = mir_data._read_packdata(
        mir_data._file_dict, mir_data.in_data["inhid"], use_mmap=False
    )

    assert mmap_data.keys() == reg_data.keys()
    for key in mmap_data.keys():
        assert np.array_equal(mmap_data[key], reg_data[key])


@pytest.mark.parametrize("attr", ["_make_packdata", "_read_data"])
def test_data_errs(mir_data, attr):
    with pytest.raises(ValueError, match="Argument for data_type not recognized"):
        getattr(mir_data, attr)(None, None, None, None)


def test_read_packdata__make_packdata(mir_data):
    """Verify that making packdata produces the same result as reading packdata"""
    mir_data.load_data(load_raw=True)

    _read_data = mir_data._read_packdata(
        mir_data._file_dict, mir_data.in_data["inhid"], "cross"
    )

    make_data = mir_data._make_packdata(
        mir_data._file_dict[mir_data.filepath]["cross"]["int_dict"],
        mir_data._sp_dict,
        mir_data.raw_data,
        "cross",
    )

    assert _read_data.keys() == make_data.keys()
    for key in _read_data.keys():
        assert np.array_equal(_read_data[key], make_data[key])


def test_apply_tsys_errs(mir_data):
    """
    Test that apply_tsys throws errors as expected.

    Note that we test these errors in sequence since it's a lot more efficient to do
    these operations on the same object one after another.
    """
    with pytest.raises(ValueError, match="Cannot apply tsys again "):
        mir_data.apply_tsys()

    mir_data.apply_tsys(invert=True)
    with pytest.raises(ValueError, match="Cannot undo tsys application if it was nev"):
        mir_data.apply_tsys(invert=True)

    mir_data.unload_data()
    with pytest.raises(ValueError, match="Must call load_data first before applying"):
        mir_data.apply_tsys(invert=True)


def test_apply_tsys_warn(mir_data):
    """Verify that apply_tsys throws warnings when tsys values aren't found."""
    with uvtest.check_warnings(UserWarning, "Changing fields that tie to header keys"):
        mir_data.eng_data["antenna"] = -1

    mir_data._tsys_applied = False

    with uvtest.check_warnings(
        UserWarning,
        [
            ("No tsys for blhid %i found (1-4 baseline, inhid 1)." % idx)
            for idx in range(1, 5)
        ],
    ):
        mir_data.apply_tsys()

    assert np.all(
        [np.all(data_dict["flags"]) for data_dict in mir_data.vis_data.values()]
    )


def test_apply_tsys(mir_data):
    """Test that apply_tsys works on vis_data as expected."""
    mir_copy = mir_data.copy()
    # Calculate the scaling factors directly. The factor of 2 comes from DSB -> SSB
    rxa_norm = mir_data.jypk * 2 * (np.prod(mir_data.eng_data["tsys"]) ** 0.5)
    rxb_norm = mir_data.jypk * 2 * (np.prod(mir_data.eng_data["tsys_rx2"]) ** 0.5)
    # The first 5 records should be rxa, and 5 rxb, then 5 rxa, then 5 rxb
    norm_list = np.concatenate(
        (
            np.ones(5) * rxa_norm,
            np.ones(5) * rxb_norm,
            np.ones(5) * rxa_norm,
            np.ones(5) * rxb_norm,
        )
    )

    mir_data.unload_data()
    mir_data.load_data(load_cross=True, apply_tsys=False)
    mir_copy.unload_data()
    mir_copy.load_data(load_cross=True, apply_tsys=True)
    for key, norm_fac in zip(mir_data.vis_data.keys(), norm_list):
        assert np.allclose(
            norm_fac * mir_data.vis_data[key]["data"], mir_copy.vis_data[key]["data"]
        )
        assert np.allclose(
            mir_data.vis_data[key]["flags"], mir_copy.vis_data[key]["flags"]
        )

    mir_copy.apply_tsys(invert=True)
    for key, _ in zip(mir_data.vis_data.keys(), norm_list):
        assert np.allclose(
            mir_data.vis_data[key]["data"], mir_copy.vis_data[key]["data"]
        )
        assert np.allclose(
            mir_data.vis_data[key]["flags"], mir_copy.vis_data[key]["flags"]
        )


def test_apply_flags_err(mir_data):
    mir_data.unload_data()
    with pytest.raises(ValueError, match="Cannot apply flags if vis_data are not load"):
        mir_data.apply_flags()


@pytest.mark.parametrize("sphid_arr", [[1], list(range(1, 21)), [10, 15]])
def test_apply_flags(mir_data, sphid_arr):
    mir_data.sp_data.set_value("flags", 1, header_key=sphid_arr)
    mir_data.apply_flags()
    for key, value in mir_data.vis_data.items():
        assert np.all(value["flags"]) == (key in sphid_arr)


def test_check_data_index(mir_data):
    """Verify that check_data_index returns True/False as expected."""
    # Spoof this for the sake of this test
    mir_data.raw_data = mir_data.vis_data

    assert mir_data._check_data_index()

    # Now muck with the records so that this becomes False
    for item in ["sp_data", "ac_data"]:
        getattr(mir_data, item)._data[0] = -1
        assert not mir_data._check_data_index()
        getattr(mir_data, item)._data[0] = 1
        assert mir_data._check_data_index()

    for item in ["vis_data", "raw_data", "auto_data"]:
        getattr(mir_data, item).update({-1: None})
        assert not mir_data._check_data_index()
        del getattr(mir_data, item)[-1]
        assert mir_data._check_data_index()


@pytest.mark.parametrize("select_auto", [True, False])
@pytest.mark.parametrize("select_vis", [True, False])
@pytest.mark.parametrize("select_raw", [True, False])
def test_downselect_data(mir_data, select_vis, select_raw, select_auto):
    if select_raw:
        # Create the raw data in case we need it.
        mir_data.raw_data = mir_data._convert_vis_to_raw(mir_data.vis_data)
        if not select_vis:
            # Unload this if we don't need it
            mir_data.vis_data = None

    mir_copy = mir_data.copy()

    # Manually downselect the data that we need.
    if select_vis or select_raw:
        mir_data.sp_data._mask[1::2] = False
    if select_auto:
        mir_data.ac_data._mask[1::2] = False

    mir_data._downselect_data(
        select_vis=select_vis, select_raw=select_raw, select_auto=select_auto
    )

    if select_vis or select_auto or select_raw:
        assert mir_data != mir_copy
    else:
        assert mir_data == mir_copy

    assert mir_data._check_data_index()

    # If we down-selected, make sure we plug back in the original data.
    if select_vis or select_raw:
        mir_data.sp_data._mask[:] = True
    if select_auto:
        mir_data.ac_data._mask[:] = True

    # Make sure that the metadata all look good.
    assert mir_data.__eq__(mir_copy, metadata_only=True)

    if select_vis or select_auto or select_raw:
        with pytest.raises(MirMetaError, match="Missing spectral records in data attr"):
            mir_data._downselect_data(
                select_vis=select_vis, select_raw=select_raw, select_auto=select_auto
            )

        # Any data attributes we wiped out, manually downselect the records in the
        # copy to make sure that everything agrees as we expect.
        if select_raw:
            mir_copy.raw_data = {
                key: value
                for idx, (key, value) in enumerate(mir_copy.raw_data.items())
                if (np.mod(idx, 2) == 0)
            }
        if select_auto:
            mir_copy.auto_data = {
                key: value
                for idx, (key, value) in enumerate(mir_copy.auto_data.items())
                if (np.mod(idx, 2) == 0)
            }
        if select_vis:
            mir_copy.vis_data = {
                key: value
                for idx, (key, value) in enumerate(mir_copy.vis_data.items())
                if (np.mod(idx, 2) == 0)
            }

    assert mir_data == mir_copy


@pytest.mark.parametrize("unload_auto", [True, False])
@pytest.mark.parametrize("unload_vis", [True, False])
@pytest.mark.parametrize("unload_raw", [True, False])
def test_unload_data(mir_data, unload_vis, unload_raw, unload_auto):
    """Verify that unload_data unloads data as expected."""
    # Spoof raw_data for just this test.
    mir_data.raw_data = {1: {"data": [1] * 16384, "scale_fac": [1]}}

    mir_data.unload_data(
        unload_vis=unload_vis, unload_raw=unload_raw, unload_auto=unload_auto
    )

    assert mir_data.vis_data is None if unload_vis else mir_data.vis_data is not None
    assert mir_data._tsys_applied != unload_vis

    assert mir_data.raw_data is None if unload_raw else mir_data.raw_data is not None

    assert mir_data.auto_data is None if unload_auto else mir_data.auto_data is not None


@pytest.mark.parametrize(
    "kwargs,err_type,err_msg",
    [[{"load_auto": True}, ValueError, "This object has no auto-correlation data"]],
)
def test_load_data_err(mir_data, kwargs, err_type, err_msg):
    mir_data._clear_auto()

    with pytest.raises(err_type, match=err_msg):
        mir_data.load_data(**kwargs)


@pytest.mark.parametrize(
    "optype,kwargs,warn_msg",
    [
        ["load_raw", {"load_cross": True}, "Converting previously loaded data since"],
        ["muck_vis", {"allow_downselect": True}, "Cannot downselect cross-correlation"],
        ["muck_auto", {"allow_downselect": True}, "Cannot downselect auto-correlation"],
    ],
)
def test_load_data_warn(mir_data, optype, kwargs, warn_msg):
    if optype == "load_raw":
        mir_data.load_data(load_raw=True, load_cross=True, load_auto=False)
    elif optype == "muck_vis":
        mir_data.vis_data = {}
    elif optype == "muck_auto":
        mir_data.auto_data = {}

    with uvtest.check_warnings(UserWarning, warn_msg):
        mir_data.load_data(**kwargs)


@pytest.mark.parametrize("load_vis", [None, False])
def test_load_data_defaults(mir_data, load_vis):
    """Check that the default behavior of load_vis acts as expected."""
    # Blow away the old data first before we attempt.
    mir_data.unload_data()
    mir_data.load_data(load_cross=load_vis)

    assert (mir_data.vis_data is not None) == (load_vis is None)
    assert mir_data._tsys_applied == (load_vis is None)
    assert mir_data.raw_data is None

    assert mir_data.auto_data is not None


def test_load_data_conv(mir_data):
    """Test that the conversion operation of load_data operates as expected."""
    mir_copy = mir_data.copy()

    mir_data.unload_data()
    assert mir_data.vis_data is None

    mir_data.load_data(load_cross=True, allow_conversion=False)
    mir_copy.load_data(load_cross=True, allow_conversion=True)

    assert mir_copy.vis_data is not None
    assert mir_copy == mir_data


def test_update_filter_update_data(mir_data):
    """
    Test that _update_filter behaves as expected with update_data.
    """
    mir_copy = mir_data.copy()

    # Manually unload the data, see if update_data will fix it.
    mir_data.vis_data = {}
    mir_data.auto_data = {}

    mir_data._update_filter(update_data=True)
    assert mir_data == mir_copy

    # Now see what happens if we don't explicitly allow for data to be updated.
    mir_data.vis_data = {}
    mir_data.auto_data = {}
    with uvtest.check_warnings(UserWarning, "Unable to update data attributes,"):
        mir_data._update_filter()

    assert mir_data.vis_data is None
    assert mir_data.auto_data is None


def test_reset(mir_data):
    mir_copy = mir_data.copy()
    mir_copy.rechunk(8)

    assert mir_data != mir_copy

    mir_copy.reset()
    mir_data.unload_data()
    assert mir_data == mir_copy

    for item in mir_data._metadata_attrs.values():
        assert item._stored_values == {}


@pytest.mark.parametrize(
    "unload_data,warn_msg",
    [
        [False, "Writing out raw data with tsys applied."],
        [True, ["No cross data loaded,", "No auto data loaded,"]],
    ],
)
def test_write_warn(mir_data, tmp_path, unload_data, warn_msg):
    """Test that write throws errors as expected."""
    testfile = os.path.join(
        tmp_path, "test_write_warn_%s.mir" % ("meta" if unload_data else "tsysapp")
    )
    if unload_data:
        mir_data.unload_data()

    with uvtest.check_warnings(UserWarning, warn_msg):
        mir_data.write(testfile)

    # Drop the data and autos here to make the comparison a bit easier.
    mir_data.unload_data()
    mir_data._clear_auto()

    mir_copy = MirParser(testfile)
    assert (
        mir_copy._file_dict[mir_copy.filepath] == mir_data._file_dict[mir_data.filepath]
    )
    assert mir_copy.filepath != mir_data.filepath
    assert mir_copy.filepath == testfile
    mir_copy.filepath = mir_data.filepath
    mir_copy._file_dict = mir_data._file_dict
    assert mir_copy == mir_data


@pytest.mark.parametrize("inplace", [True, False])
def test_rechunk_raw(inplace):
    """Test that rechunk_vis properly averages data"""
    raw_data = {
        5: {"data": np.arange(-16384, 16384, dtype=np.int16), "scale_fac": np.int16(1)}
    }

    # First up, test what should be a no-op
    raw_copy = MirParser._rechunk_raw(raw_data, [1], inplace=inplace)

    assert (raw_copy is raw_data) == inplace

    assert raw_data.keys() == raw_copy.keys()
    assert raw_data[5]["scale_fac"] == 1
    assert np.all(raw_data[5]["data"] == np.arange(-16384, 16384))

    # Now let's actually do some averaging and make sure it works as expected.
    raw_copy = MirParser._rechunk_raw(raw_data, [2], inplace=inplace)
    assert (raw_copy is raw_data) == inplace
    # Scale factor drops on account of having gotten rid of one sig binary digit
    # through the averaging process
    assert raw_copy[5]["scale_fac"] == 0
    # This is what raw_data _should_ look like after averaging. Note two arange arrays
    # are used here because the spacing for real _or_ imag is the same, but not real
    # and imag together.
    assert np.all(
        raw_copy[5]["data"]
        == np.vstack(
            (np.arange(-32766, 32768, 8), np.arange(-32764, 32768, 8))
        ).T.flatten()
    )
    raw_data = raw_copy

    # Finally, test that flagging works as expected
    raw_data[5]["data"][2:] = -32768  # Marks channel as flagged
    raw_copy = MirParser._rechunk_raw(raw_data, [4096], inplace=inplace)
    assert (raw_copy is raw_data) == inplace
    # Scale factor should not change
    assert raw_copy[5]["scale_fac"] == 0
    # First channel should just contain channel 1 data, second channel should be flagged
    assert np.all(raw_copy[5]["data"] == [-32766, -32764, -32768, -32768])


@pytest.mark.parametrize("inplace", [True, False])
def test_rechunk_cross(inplace):
    """Test that rechunk_raw properly averages data"""
    # Chicago FTW!
    vis_data = {
        25624: {
            "data": (np.arange(1024) + np.flip(np.arange(1024) * 1j)),
            "flags": np.zeros(1024, dtype=bool),
        }
    }
    check_vals = np.arange(1024) + np.flip(np.arange(1024) * 1j)

    # First up, test no averaging
    vis_copy = MirParser._rechunk_data(vis_data, [1], inplace=inplace)

    assert (vis_copy is vis_data) == inplace

    assert vis_data.keys() == vis_copy.keys()
    assert np.all(vis_data[25624]["flags"] == np.zeros(1024, dtype=bool))
    assert np.all(vis_data[25624]["data"] == check_vals)

    # Next, test averaging w/o flags
    vis_copy = MirParser._rechunk_data(vis_data, [4], inplace=inplace)
    check_vals = np.mean(check_vals.reshape(256, 4), axis=1)

    assert (vis_copy is vis_data) == inplace
    assert vis_data.keys() == vis_copy.keys()
    assert np.all(vis_copy[25624]["flags"] == np.zeros(256, dtype=bool))
    assert np.all(vis_copy[25624]["data"] == check_vals)
    vis_data = vis_copy

    # Finally, check what happens if we flag data
    vis_data[25624]["flags"][1:] = True
    vis_copy = MirParser._rechunk_data(vis_data, [128], inplace=inplace)
    assert (vis_copy is vis_data) == inplace
    assert vis_data.keys() == vis_copy.keys()
    assert np.all(vis_copy[25624]["flags"] == [False, True])
    assert np.all(vis_copy[25624]["data"] == [check_vals[0], 0.0])


@pytest.mark.parametrize("inplace", [True, False])
def test_rechunk_auto(inplace):
    auto_data = {
        8675309: {
            "data": np.arange(-1024, 1024, dtype=np.float32),
            "flags": np.zeros(2048, dtype=bool),
        }
    }

    # First up, test no averaging
    auto_copy = MirParser._rechunk_data(auto_data, [1], inplace=inplace)
    assert (auto_copy is auto_data) == inplace
    assert auto_data.keys() == auto_copy.keys()
    assert np.all(auto_copy[8675309]["data"] == np.arange(-1024, 1024))

    # First up, test no averaging
    auto_copy = MirParser._rechunk_data(auto_data, [512], inplace=inplace)
    assert (auto_copy is auto_data) == inplace
    assert auto_data.keys() == auto_copy.keys()
    assert np.all(auto_copy[8675309]["data"] == [-768.5, -256.5, 255.5, 767.5])


@pytest.mark.parametrize(
    "chan_avg,drop_data,err_type,err_msg",
    [
        [0.5, False, ValueError, "chan_avg must be of type int."],
        [-1, False, ValueError, "chan_avg cannot be a number less than"],
        [3, False, ValueError, "chan_avg does not go evenly into "],
        [2, True, ValueError, "Index values do not match data keys."],
    ],
)
def test_rechunk_errs(mir_data, chan_avg, drop_data, err_type, err_msg):
    """Verify that rechunk throws errors as expected."""
    if drop_data:
        mir_data.vis_data = {}

    # Rather than parametrize this, because the underlying object isn't changed,
    # check for the different load states here, since the error should get thrown
    # no matter which thing you are loading.
    with pytest.raises(err_type, match=err_msg):
        mir_data.rechunk(chan_avg)


def test_rechunk_nop(mir_data):
    """Test that setting chan_avg to 1 doesn't change the object."""
    mir_copy = mir_data.copy()

    mir_data.rechunk(1)
    assert mir_data == mir_copy


def test_rechunk_on_the_fly(mir_data):
    mir_data.rechunk(8)
    mir_copy = mir_data.copy()

    mir_copy.unload_data()
    mir_copy.load_data(load_cross=True, load_auto=True)

    assert mir_data == mir_copy


def test_rechunk_raw_vs_vis(mir_data):
    mir_copy = mir_data.copy()
    mir_copy.load_data(load_raw=True)

    # This will just rechunk the raw data
    mir_copy.rechunk(8)

    # This will rechunk the vis data
    mir_data.rechunk(8)

    # This will convert raw to vis in the copy
    with uvtest.check_warnings(UserWarning, "Converting previously loaded data"):
        mir_copy.load_data(allow_conversion=True, load_cross=True)

    assert mir_copy == mir_data


@pytest.mark.parametrize(
    "muck_data,kwargs,err_type,err_msg",
    [
        [["in_data"], {}, ValueError, "Cannot merge objects due to conflicts"],
        [["file"], {}, ValueError, "Duplicate metadata found for the following"],
        [["auto"], {}, ValueError, "Cannot combine two MirParser objects if one "],
        [["jypk"], {}, ValueError, "Cannot combine objects where the jypk value"],
        [["all"], {}, TypeError, "Cannot add a MirParser object an object of "],
        [[], {"merge": False}, ValueError, "Must set merge=True in order to"],
        [["add_file"], {"merge": True}, ValueError, "These two objects were"],
        [["file"], {"merge": True}, ValueError, "Cannot merge objects that"],
        [["file", "antpos"], {"merge": False}, ValueError, "Antenna positions differ"],
    ],
)
def test_add_errs(mir_data, muck_data, kwargs, err_type, err_msg):
    """Verify that __add__ throws errors as expected"""
    mir_copy = mir_data.copy()

    if "in_data" in muck_data:
        mir_data.in_data["mjd"] = 0.0
    if "auto" in muck_data:
        mir_data._clear_auto()
    if "jypk" in muck_data:
        mir_data.jypk = 1.0
    if "file" in muck_data:
        mir_data._file_dict = {}
    if "add_file" in muck_data:
        mir_data._file_dict["foo.mir"] = {}
    if "antpos" in muck_data:
        mir_data.antpos_data["xyz_pos"] = 0.0
    if "all" in muck_data:
        mir_data = np.arange(100)

    with pytest.raises(err_type) as err:
        mir_data.__add__(mir_copy, **kwargs)
    if not ("all" in muck_data):
        assert str(err.value).startswith(err_msg)

    with pytest.raises(err_type, match=err_msg):
        mir_copy.__add__(mir_data, **kwargs)


def test_add_merge(mir_data):
    """
    Verify that the __add__ method behaves as expected under 'simple' scenarios, i.e.,
    where overwrite or force are not necessary.
    """
    mir_copy = mir_data.copy()
    mir_orig = mir_data.copy()

    # So this is a _very_ simple check, but make sure that combining two
    # objects that have all data loaded returns an equivalent object.
    assert mir_data == (mir_data + mir_data)

    # Now try in-place adding
    mir_data += mir_data
    assert mir_data == mir_copy

    # Alright, now try running a select and split the data into two.
    mir_data.select(where=("corrchunk", "eq", [0, 1, 2]))
    mir_copy.select(where=("corrchunk", "ne", [0, 1, 2]))

    # Verify that we have changed some things
    assert mir_data != mir_orig
    assert mir_data != mir_copy
    assert mir_orig != mir_copy

    # Now combine the two, and see what comes out.
    mir_data += mir_copy
    assert mir_data == mir_orig

    # Hey, that was fun, let's try selecting on bl next!
    mir_data.select(reset=True, update_data=True)
    mir_copy.select(reset=True, update_data=True)

    mir_data.select(where=("sb", "eq", "l"))
    mir_copy.select(where=("sb", "eq", "u"))

    # The reset unloads the data, so fix that now
    mir_data.load_data(load_cross=True, load_auto=True, apply_tsys=True)
    mir_copy.load_data(load_cross=True, load_auto=True, apply_tsys=True)

    # Verify that we have changed some things
    assert mir_data != mir_orig
    assert mir_data != mir_copy
    assert mir_orig != mir_copy

    # Now combine the two, and see what comes out.
    mir_data += mir_copy
    assert mir_data == mir_orig

    # Finally, let's try something a little different. Drop autos on one object, and
    # do a filter where the union of the two objects does NOT give you back the sum
    # total of the other object
    mir_data.select(reset=True, update_data=True)
    mir_copy.select(reset=True, update_data=True)

    mir_copy._clear_auto()

    mir_data.select(where=("corrchunk", "eq", [1, 2]))
    mir_copy.select(where=("corrchunk", "eq", [3, 4]))
    mir_data.load_data(load_cross=True, apply_tsys=True)
    mir_copy.load_data(load_cross=True, apply_tsys=True)

    with uvtest.check_warnings(
        UserWarning, "Both objects do not have auto-correlation data."
    ):
        mir_data.__iadd__(mir_copy, force=True)

    # Make sure we got all the data entries
    assert mir_data._check_data_index()

    # Make sure auto properties propagated correctly.
    assert (not mir_data._has_auto) and (mir_data.auto_data is None)
    mir_orig._clear_auto()

    # Finally, make sure the object isn't the same, but after a reset and reload,
    # we get the same object back (modulo the auto-correlation data).
    assert mir_data != mir_orig
    mir_data.select(reset=True, update_data=True)
    mir_data.load_data(load_cross=True, apply_tsys=True)
    assert mir_data == mir_orig


@pytest.mark.parametrize("drop_auto", [True, False])
@pytest.mark.parametrize("drop_raw", [True, False])
@pytest.mark.parametrize("drop_vis", [True, False, "jypk", "tsys"])
def test_add_drop_data(mir_data, drop_auto, drop_raw, drop_vis):
    mir_data.raw_data = mir_data._convert_vis_to_raw(mir_data.vis_data)
    mir_copy = mir_data.copy()

    if drop_auto:
        mir_copy.auto_data = None
    if drop_raw:
        mir_copy.raw_data = None
    if drop_vis:
        if drop_vis == "jypk":
            mir_copy.jypk = 0.0
        elif drop_vis == "tsys":
            mir_copy._tsys_applied = False
        else:
            mir_copy.vis_data = None

    result = mir_data.__add__(mir_copy, overwrite=(drop_vis == "jypk"))

    assert (result.auto_data is None) == bool(drop_auto)
    assert (result.raw_data is None) == bool(drop_raw)
    assert (result.vis_data is None) == bool(drop_vis)


@pytest.mark.parametrize(
    "muck_attr",
    [
        "ac_data",
        "antpos_data",
        "bl_data",
        "eng_data",
        "in_data",
        "sp_data",
        "we_data",
        "all",
        "codes",
    ],
)
def test_add_overwrite(mir_data, muck_attr):
    """Verify that the overwrite option on __add__ works as expected."""
    mir_copy = mir_data.copy()

    prot_fields = [
        "inhid",
        "blhid",
        "sphid",
        "ints",
        "antenna",
        "antennaNumber",
        "achid",
        "v_name",
        "icode",
        "ncode",
        "dataoff",
    ]
    if muck_attr == "all":
        for item in [
            "ac_data",
            "antpos_data",
            "bl_data",
            "eng_data",
            "in_data",
            "sp_data",
            "we_data",
        ]:
            for field in getattr(mir_data, item).dtype.names:
                if field not in prot_fields:
                    getattr(mir_data, item)[field] = 255
    elif muck_attr == "codes":
        mir_data.codes_data.set_value("code", "1", where=("v_name", "eq", "filever"))
    else:
        for field in getattr(mir_data, muck_attr).dtype.names:
            if field not in prot_fields:
                getattr(mir_data, muck_attr)[field] = 255

    # After mucking, verify that at least something looks different
    assert mir_data != mir_copy

    # mir_copy contains the good data, so adding it second will overwrite the bad data.
    assert mir_data.__add__(mir_copy, overwrite=True) == mir_copy

    # On the other hand, if we add mir_data second, the bad values should get propagated
    assert mir_copy.__add__(mir_data, overwrite=True) == mir_data


def test_add_concat_warn(mir_data, tmp_path):
    filepath = os.path.join(tmp_path, "add_concat_warn")

    with uvtest.check_warnings(UserWarning, "Writing out raw data with tsys applied."):
        mir_data.write(filepath)

    mir_copy = MirParser(filepath)
    with uvtest.check_warnings(
        UserWarning,
        [
            "Duplicate metadata found for the following attributes",
            "These two objects contain data taken at the exact same time",
            "Both objects do not have auto-correlation data.",
        ],
    ):
        mir_copy.__iadd__(mir_data, force=True, merge=False)

    assert mir_copy != mir_data
    for item in mir_copy._metadata_attrs:
        if item == "codes_data":
            assert mir_data.codes_data == mir_copy.codes_data
        else:
            assert len(getattr(mir_copy, item)) == (2 * len(getattr(mir_data, item)))


def test_add_concat(mir_data, tmp_path):
    filepath = os.path.join(tmp_path, "add_concat")

    mir_copy = mir_data.copy()

    # Preserve particular fields that we want to propagate into the next file.
    prot_fields = [
        "inhid",
        "blhid",
        "sphid",
        "ints",
        "antenna",
        "xyz_pos",
        "antennaNumber",
        "achid",
        "v_name",
        "icode",
        "ncode",
        "dataoff",
        "nch",
        "iant1",
        "iant2",
        "tsys",
        "tsys_rx2",
        "antrx",
        "ant1rx",
        "ant2rx",
        "isb",
    ]

    for item in ["bl_data", "eng_data", "in_data", "sp_data", "we_data", "ac_data"]:
        for field in getattr(mir_copy, item).dtype.names:
            if field not in prot_fields:
                getattr(mir_copy, item)[field] = 156

    mir_copy.codes_data.set_value("code", "3c279", where=("v_name", "eq", "source"))

    with uvtest.check_warnings(UserWarning, "Writing out raw data with tsys applied."):
        mir_copy.write(filepath, overwrite=True)
    # Read the file in so that we have a dict here to work with.
    mir_copy.read(filepath, load_cross=True, has_auto=True)

    new_obj = mir_data + mir_copy

    # Make sure that the
    for item, this_attr in new_obj._metadata_attrs.items():
        other_attr = mir_data._metadata_attrs[item]
        if item == "antpos_data":
            assert this_attr == other_attr
        else:
            assert this_attr != other_attr
            if item == "codes_data":
                # We add 3 here since the 1 extra source creates 3 new codes entries
                assert len(this_attr) == (3 + len(other_attr))

                # Make sure that the source actually got updated as expected
                assert this_attr["source"]["3c279"] == 2
            else:
                # Otherwise, the number of entries should double for all attributes
                assert len(this_attr) == (2 * len(other_attr))
                for field in this_attr.dtype.names:
                    if field not in prot_fields:
                        assert np.all(this_attr[field][len(other_attr) :] == 156)


@pytest.mark.parametrize(
    "kern_type,tol,err_type,err_msg",
    [
        ["cubic", -1, ValueError, "tol must be between 0 and 0.5."],
        ["abc", 0.5, ValueError, 'Kernel type of "abc" not recognized,'],
    ],
)
def test_generate_chanshift_kernel_errs(kern_type, tol, err_type, err_msg):
    """ "Verify that _generate_chanshift_kernel throws errors as expected."""
    with pytest.raises(err_type, match=err_msg):
        MirParser._generate_chanshift_kernel(1.5, kern_type, tol=tol)


@pytest.mark.parametrize(
    "kern_type,chan_shift,alpha,tol,exp_coarse,exp_kern",
    [
        ["nearest", 0, -0.5, 1e-3, 0, []],
        ["linear", 0, -0.5, 1e-3, 0, []],
        ["cubic", 0, -0.5, 1e-3, 0, []],
        ["nearest", -1, -0.5, 1e-3, -1, []],
        ["linear", 2, -0.5, 1e-3, 2, []],
        ["cubic", -3, -0.5, 1e-3, -3, []],
        ["nearest", 1.5, -0.5, 1e-3, 2, []],
        ["linear", 2.0005, -0.5, 1e-3, 2, []],
        ["cubic", -3.1, -0.5, 0.2, -3, []],
        ["linear", 1.3, -0.5, 1e-3, 1, [0.7, 0.3]],
        ["linear", -1.3, -0.5, 1e-3, -2, [0.3, 0.7]],
        ["cubic", -3.5, 0, 1e-3, -4, [0, 0.5, 0.5, 0]],
        ["cubic", 1.4, 0.0, 1e-3, 1, [0.0, 0.352, 0.648, 0.0]],
        ["cubic", 1.4, -0.5, 1e-4, 1, [-0.048, 0.424, 0.696, -0.072]],
        ["cubic", 1.4, -1.0, 0, 1, [-0.096, 0.496, 0.744, -0.144]],
    ],
)
def test_generate_chanshift_kernel(
    mir_data, kern_type, chan_shift, alpha, tol, exp_coarse, exp_kern
):
    """Test that _generate_chanshift_kernel produces kernels as expected."""
    (coarse_shift, kern_size, kern) = MirParser._generate_chanshift_kernel(
        chan_shift, kern_type, alpha_fac=alpha, tol=tol
    )

    assert coarse_shift == exp_coarse
    assert len(exp_kern) == kern_size
    if kern is None:
        assert exp_kern == []
    else:
        assert np.allclose(exp_kern, kern)


@pytest.mark.parametrize("check_flags", [True, False])
@pytest.mark.parametrize("fwd_dir", [True, False])
@pytest.mark.parametrize(
    "inplace,return_vis", [[True, False], [False, True], [False, False]]
)
def test_chanshift_raw_vals(inplace, return_vis, fwd_dir, check_flags):
    """Test that _chanshift_raw modifies spectra as expected."""
    # Create a dataset to set against using a dummy impulse in the DC channel
    raw_vals = []
    raw_vals.extend([32767 if check_flags else 0] * 8)
    raw_vals.extend([-32768 if check_flags else 32767] * 2)
    raw_vals.extend([32767 if check_flags else 0] * 6)

    raw_dict = {
        123: {"data": np.array(raw_vals, dtype=np.int16), "scale_fac": np.int16(0)}
    }

    # Test no-op
    new_dict = MirParser._chanshift_raw(
        raw_dict, [(0, 0, None)], inplace=inplace, return_vis=return_vis
    )
    if inplace:
        assert new_dict is raw_dict
    if return_vis:
        new_dict = MirParser._convert_vis_to_raw(new_dict)

    assert np.all(raw_vals == new_dict[123]["data"])
    assert new_dict[123]["scale_fac"] == 0

    # Now try a simple one-channel shift
    new_dict = MirParser._chanshift_raw(
        raw_dict,
        [(1 if fwd_dir else -1, 0, None)],
        inplace=inplace,
        return_vis=return_vis,
    )
    if inplace:
        assert new_dict is raw_dict
    if return_vis:
        new_dict = MirParser._convert_vis_to_raw(new_dict)

    good_slice = slice(None if fwd_dir else 2, -2 if fwd_dir else None)
    flag_slice = slice(None if fwd_dir else -2, 2 if fwd_dir else None)
    # Note that the shift of 2 is required since each channel has a real and imag
    # component. The first two entries are dropped because they _should_ be flagged.
    assert np.all(
        raw_vals[good_slice]
        == np.roll(new_dict[123]["data"], -2 if fwd_dir else 2)[good_slice]
    )
    assert np.all(new_dict[123]["data"][flag_slice] == -32768)
    assert new_dict[123]["scale_fac"] == 0

    # Refresh the values, in case we are doing this in-place
    if inplace:
        raw_dict = {
            123: {"data": np.array(raw_vals, dtype=np.int16), "scale_fac": np.int16(0)}
        }

    # Last check, try a linear interpolation step
    new_dict = MirParser._chanshift_raw(
        raw_dict,
        [(1 if fwd_dir else -2, 2, np.array([0.5, 0.5], dtype=np.float32))],
        inplace=inplace,
        return_vis=return_vis,
    )
    if inplace:
        assert new_dict is raw_dict
    if return_vis:
        new_dict = MirParser._convert_vis_to_raw(new_dict)

    if fwd_dir:
        assert np.all(new_dict[123]["data"][14:16] == (32767 if check_flags else 0))
        assert np.all(
            new_dict[123]["data"][10:14] == (-32768 if check_flags else 32767)
        )
        assert np.all(new_dict[123]["data"][4:10] == (32767 if check_flags else 0))
        assert np.all(new_dict[123]["data"][0:4] == -32768)
    else:
        assert np.all(new_dict[123]["data"][0:4] == (32767 if check_flags else 0))
        assert np.all(new_dict[123]["data"][4:8] == (-32768 if check_flags else 32767))
        assert np.all(new_dict[123]["data"][8:12] == (32767 if check_flags else 0))
        assert np.all(new_dict[123]["data"][12:16] == -32768)
    assert new_dict[123]["scale_fac"] == (0 if check_flags else -1)


@pytest.mark.parametrize(
    "check_flags,flag_adj", [[False, True], [True, False], [True, True]]
)
@pytest.mark.parametrize("fwd_dir", [True, False])
@pytest.mark.parametrize("inplace", [True, False])
def test_chanshift_vis(check_flags, flag_adj, fwd_dir, inplace):
    """Test that _chanshift_vis modifies spectra as expected."""
    check_val = -(1 + 2j) if check_flags else (1 + 2j)
    vis_vals = [check_val if check_flags else 0] * 4
    vis_vals.append((3 + 4j) if check_flags else check_val)
    vis_vals.extend([check_val if check_flags else 0] * 3)
    flag_vals = [False] * 4
    flag_vals.append(check_flags)
    flag_vals.extend([False] * 3)

    vis_dict = {
        456: {
            "data": np.array(vis_vals, dtype=np.complex64),
            "flags": np.array(flag_vals, dtype=bool),
        }
    }

    # Test no-op
    new_dict = MirParser._chanshift_vis(
        vis_dict, [(0, 0, None)], flag_adj=flag_adj, inplace=inplace
    )

    if inplace:
        assert new_dict is vis_dict

    assert np.all(vis_vals == new_dict[456]["data"])
    assert np.all(new_dict[456]["flags"] == flag_vals)

    # Now try a simple one-channel shift
    new_dict = MirParser._chanshift_vis(
        vis_dict, [(1 if fwd_dir else -1, 0, None)], flag_adj=flag_adj, inplace=inplace
    )

    if inplace:
        assert new_dict is vis_dict

    good_slice = slice(None if fwd_dir else 1, -1 if fwd_dir else None)
    flag_slice = slice(None if fwd_dir else -1, 1 if fwd_dir else None)

    assert np.all(
        vis_vals[good_slice]
        == np.roll(new_dict[456]["data"], -1 if fwd_dir else 1)[good_slice]
    )
    assert np.all(
        flag_vals[good_slice]
        == np.roll(new_dict[456]["flags"], -1 if fwd_dir else 1)[good_slice]
    )

    assert np.all(new_dict[456]["data"][flag_slice] == 0.0)
    assert np.all(new_dict[456]["flags"][flag_slice])

    # Refresh the values, in case we are doing this in-place
    if inplace:
        vis_dict = {
            456: {
                "data": np.array(vis_vals, dtype=np.complex64),
                "flags": np.array(flag_vals, dtype=bool),
            }
        }

    # Last check, try a linear interpolation step
    new_dict = MirParser._chanshift_vis(
        vis_dict,
        [(1 if fwd_dir else -2, 2, np.array([0.75, 0.25], dtype=np.float32))],
        flag_adj=flag_adj,
        inplace=inplace,
    )
    if inplace:
        assert new_dict is vis_dict

    exp_vals = np.roll(vis_vals, 2 if fwd_dir else -2)
    exp_flags = np.roll(flag_vals, 2 if fwd_dir else -2)
    exp_vals[None if fwd_dir else -2 : 2 if fwd_dir else None] = 0.0
    exp_flags[None if fwd_dir else -2 : 2 if fwd_dir else None] = True
    mod_slice = slice(4 - (-1 if fwd_dir else 2), 6 - (-1 if fwd_dir else 2))
    if flag_adj:
        exp_flags[mod_slice] = check_flags
        exp_vals[mod_slice] = 0 if check_flags else [check_val * 0.75, check_val * 0.25]
    else:
        exp_vals[mod_slice] = [check_val * 0.25, check_val * 0.75]
        exp_flags[mod_slice] = False

    assert np.all(new_dict[456]["data"] == exp_vals)
    assert np.all(new_dict[456]["flags"] == exp_flags)


@pytest.mark.parametrize(
    "filever,irec,err_type,err_msg",
    [
        ["2", 3, ValueError, "MIR file format < v4.0 detected,"],
        ["4", 3, ValueError, "Receiver code 3 not recognized."],
    ],
)
def test_redoppler_data_errs(mir_data, filever, irec, err_type, err_msg):
    """Verify that redoppler_data throws errors as expected."""
    mir_data.codes_data.set_value("code", filever, where=("v_name", "eq", "filever"))
    mir_data.bl_data["irec"] = irec

    with pytest.raises(err_type, match=err_msg):
        mir_data.redoppler_data()


@pytest.mark.parametrize("plug_vals", [True, False])
@pytest.mark.parametrize("diff_rx", [True, False])
@pytest.mark.parametrize("use_raw", [True, False])
def test_redoppler_data(mir_data, plug_vals, diff_rx, use_raw):
    """Verify that redoppler_data behaves as expected."""
    # We have to spoof the filever because the test file is technically v3
    mir_data.codes_data.set_value("code", "4", where=("v_name", "eq", "filever"))

    if use_raw:
        mir_data.raw_data = mir_data._convert_vis_to_raw(mir_data.vis_data)
        mir_data.vis_data = None

    mir_copy = mir_data.copy()
    # This first attempt should basically just be a no-op
    mir_copy.redoppler_data()

    assert mir_data == mir_copy

    # Alright, let's tweak the data now to give us something to compare
    for sphid, nch in zip(mir_data.sp_data["sphid"], mir_data.sp_data["nch"]):
        if use_raw:
            mir_data.raw_data[sphid]["data"][:] = np.arange(nch * 2)
            mir_data.raw_data[sphid]["scale_fac"] = np.int16(0)
        else:
            mir_data.vis_data[sphid]["data"][:] = np.arange(nch)
            mir_data.vis_data[sphid]["flags"][:] = False

    rxb_blhids = mir_data.bl_data["blhid"][mir_data.bl_data["ant1rx"] == 1]

    freq_shift = (
        (139.6484375e-6)
        * (1 + (diff_rx & np.isin(mir_data.sp_data["blhid"], rxb_blhids)))
        * (mir_data.sp_data["corrchunk"] != 0)
    )

    if plug_vals:
        # Note we need the factor of two here now to simulate the an error
        # that is currently present
        # TODO: Remove this once the underlying issue is fixed.
        mir_data.sp_data["fDDS"] = -(freq_shift / 2)
        freq_shift = None

    mir_data.redoppler_data(freq_shift=freq_shift)

    # Alright, let's tweak the data now to give us something to compare
    for sp_rec in mir_data.sp_data:
        sphid = sp_rec["sphid"]
        nch = sp_rec["nch"]
        chan_shift = int(-np.sign(sp_rec["fres"]) * (sp_rec["corrchunk"] != 0))
        chan_shift *= 2 if ((sp_rec["blhid"] in rxb_blhids) and diff_rx) else 1
        if chan_shift == 0:
            if use_raw:
                assert np.all(mir_data.raw_data[sphid]["data"] == np.arange(nch * 2))
            else:
                assert np.all(mir_data.vis_data[sphid]["data"] == np.arange(nch))
        elif chan_shift < 0:
            if use_raw:
                assert np.all(
                    mir_data.raw_data[sphid]["data"][: chan_shift * 2]
                    == np.arange(-(2 * chan_shift), nch * 2)
                )
            else:
                assert np.all(
                    mir_data.vis_data[sphid]["data"][:chan_shift]
                    == np.arange(-chan_shift, nch)
                )
        else:
            if use_raw:
                assert np.all(
                    mir_data.raw_data[sphid]["data"][chan_shift * 2 :]
                    == np.arange((nch - chan_shift) * 2)
                )
            else:
                assert np.all(
                    mir_data.vis_data[sphid]["data"][chan_shift:]
                    == np.arange((nch - chan_shift))
                )


def test_fix_acdata(mir_data):
    # So we have to do a bit of metadata manipulation here in order to make this work
    # for total test coverage. Spoof a dataset where there's only 1 sideband but two
    # integrations. First up - just double the number of int headers.
    mir_data.in_data._data = np.tile(mir_data.in_data._data, 2)
    mir_data.in_data._data["inhid"] = [1, 2]
    mir_data.in_data._mask = np.tile(mir_data.in_data._mask, 2)
    mir_data.in_data._set_header_key_index_dict()

    # Now, anything that's LSB, make that part of integration #2 and USB
    sel_mask = mir_data.bl_data._data["isb"] == 0
    mir_data.bl_data._data["isb"][sel_mask] = 1
    mir_data.bl_data._data["inhid"][sel_mask] = 2

    # Finally, duplicate ac_data to be double te size, mapping the new entries to
    # integration #2.
    mir_data.ac_data._data = np.tile(mir_data.ac_data._data, 2)
    mir_data.ac_data._data["inhid"][16:] = 2
    mir_data.ac_data._mask = np.tile(mir_data.ac_data._mask, 2)
    mir_data.ac_data._set_header_key_index_dict()

    # Finally, call fix_acdata, which should (among other things), appropriately fill
    # in the frequency information.
    mir_data._fix_acdata()

    # Now check that fsky is correctly set
    assert np.array_equal(
        mir_data.ac_data["fsky"][:16],
        np.tile(np.repeat(mir_data.sp_data["fsky"][11:15], 2), 2),
    )
    assert np.array_equal(
        mir_data.ac_data["fsky"][16:],
        np.tile(np.repeat(mir_data.sp_data["fsky"][1:5], 2), 2),
    )

    # Make sure these values are actually different
    assert np.all(
        ~np.isin(mir_data.ac_data["fsky"][:16], mir_data.sp_data["fsky"][1:5])
    )


# Below are a series of checks that are designed to check to make sure that the
# MirParser class is able to produce consistent values from an engineering data
# set (originally stored in /data/engineering/mir_data/200724_16:35:14), to make
# sure that we haven't broken the ability of the reader to handle the data.


def test_mir_remember_me_record_lengths(mir_data):
    """
    Mir record length checker

    Make sure the test file contains the right number of records
    """
    # Check to make sure we've got the right number of records everywhere

    # ac_data only exists if has_auto=True
    if mir_data.ac_data._data is not None:
        assert len(mir_data.ac_data) == 16
    else:
        # This should only occur when has_auto=False
        assert not mir_data._has_auto

    assert len(mir_data.bl_data) == 4

    assert len(mir_data.codes_data) == 99

    assert len(mir_data.eng_data) == 2

    assert len(mir_data.in_data) == 1

    assert len(mir_data.sp_data) == 20

    assert len(mir_data.vis_data) == 20

    assert len(mir_data.we_data) == 1


def test_mir_remember_me_codes_data(mir_data):
    """
    Mir codes_read checker.

    Make sure that certain values in the codes_read file of the test data set match
    what we know to be 'true' at the time of observations.
    """
    assert mir_data.codes_data["filever"][0] == "3"

    assert mir_data.codes_data["ref_time"][0] == "Jul 24, 2020"

    assert mir_data.codes_data["ut"][1] == "Jul 24 2020  4:34:39.00PM"

    assert mir_data.codes_data["source"][1] == "3c84"

    assert mir_data.codes_data["ra"][1] == "03:19:48.15"

    assert mir_data.codes_data["dec"][1] == "+41:30:42.1"


def test_mir_remember_me_in_data(mir_data):
    """
    Mir in_read checker.

    Make sure that certain values in the in_read file of the test data set match what
    we know to be 'true' at the time of observations, including that spare values are
    stored as zero.
    """
    # Check to make sure that things seem right in in_read
    assert np.all(mir_data.in_data["traid"] == 484)

    assert np.all(mir_data.in_data["proid"] == 484)

    assert np.all(mir_data.in_data["inhid"] == 1)

    assert np.all(mir_data.in_data["ints"] == 1)

    assert np.all(mir_data.in_data["souid"] == 1)

    assert np.all(mir_data.in_data["isource"] == 1)

    assert np.all(mir_data.in_data["ivrad"] == 1)

    assert np.all(mir_data.in_data["ira"] == 1)

    assert np.all(mir_data.in_data["idec"] == 1)

    assert np.all(mir_data.in_data["epoch"] == 2000.0)

    assert np.all(mir_data.in_data["tile"] == 0)

    assert np.all(mir_data.in_data["obsflag"] == 0)

    assert np.all(mir_data.in_data["obsmode"] == 0)

    assert np.all(np.round(mir_data.in_data["mjd"]) == 59055)

    assert np.all(mir_data.in_data["spareshort"] == 0)

    assert np.all(mir_data.in_data["spareint6"] == 0)


def test_mir_remember_me_bl_data(mir_data):
    """
    Mir bl_read checker.

    Make sure that certain values in the bl_read file of the test data set match what
    we know to be 'true' at the time of observations, including that spare values are
    stored as zero.
    """
    # Now check bl_read
    assert np.all(mir_data.bl_data["blhid"] == np.arange(1, 5))

    assert np.all(mir_data.bl_data["isb"] == [0, 0, 1, 1])

    assert np.all(mir_data.bl_data["ipol"] == [0, 0, 0, 0])

    assert np.all(mir_data.bl_data["ant1rx"] == [0, 1, 0, 1])

    assert np.all(mir_data.bl_data["ant2rx"] == [0, 1, 0, 1])

    assert np.all(mir_data.bl_data["pointing"] == 0)

    assert np.all(mir_data.bl_data["irec"] == [0, 3, 0, 3])

    assert np.all(mir_data.bl_data["iant1"] == 1)

    assert np.all(mir_data.bl_data["iant2"] == 4)

    assert np.all(mir_data.bl_data["iblcd"] == 2)

    assert np.all(mir_data.bl_data["spareint1"] == 0)

    assert np.all(mir_data.bl_data["spareint2"] == 0)

    assert np.all(mir_data.bl_data["spareint3"] == 0)

    assert np.all(mir_data.bl_data["spareint4"] == 0)

    assert np.all(mir_data.bl_data["spareint5"] == 0)

    assert np.all(mir_data.bl_data["spareint6"] == 0)

    assert np.all(mir_data.bl_data["wtave"] == 0.0)

    assert np.all(mir_data.bl_data["sparedbl4"] == 0.0)

    assert np.all(mir_data.bl_data["sparedbl5"] == 0.0)

    assert np.all(mir_data.bl_data["sparedbl6"] == 0.0)


def test_mir_remember_me_eng_data(mir_data):
    """
    Mir eng_read checker.

    Make sure that certain values in the eng_read file of the test data set match what
    we know to be 'true' at the time of observations.
    """
    # Now check eng_read
    assert np.all(mir_data.eng_data["antenna"] == [1, 4])

    assert np.all(mir_data.eng_data["padNumber"] == [5, 8])

    assert np.all(mir_data.eng_data["trackStatus"] == 1)

    assert np.all(mir_data.eng_data["commStatus"] == 1)

    assert np.all(mir_data.eng_data["inhid"] == 1)


def test_mir_remember_me_we_data(mir_data):
    """
    Mir we_read checker.

    Make sure that certain values in the we_read file of the test data set match what
    we know to be 'true' at the time of observations.
    """
    assert np.all(mir_data.we_data["ints"] == 1)

    assert np.all(mir_data.we_data["flags"] == 0)


def test_mir_remember_me_ac_data(mir_data):
    """
    Mir ac_read checker.

    Make sure that certain values in the autoCorrelations file of the test data set
    match what we know to be 'true' at the time of observations.
    """
    # Now check ac_read

    # ac_read only exists if has_auto=True
    if mir_data.ac_data is not None:
        assert np.all(mir_data.ac_data["inhid"] == 1)

        assert np.all(mir_data.ac_data["achid"] == np.arange(1, 17))

        assert np.all(mir_data.ac_data["antenna"] == ([1] * 8) + ([4] * 8))

    else:
        # This should only occur when has_auto=False
        assert not mir_data._has_auto


def test_mir_remember_me_sp_data(mir_data):
    """
    Mir sp_read checker.

    Make sure that certain values in the sp_read file of the test data set match what
    we know to be 'true' at the time of observations, including that spare values are
    stored as zero.
    """
    # Now check sp_read
    assert np.all(mir_data.sp_data["sphid"] == np.arange(1, 21))

    assert np.all(mir_data.sp_data["sphid"] == np.arange(1, 21))

    assert np.all(mir_data.sp_data["igq"] == 0)

    assert np.all(mir_data.sp_data["ipq"] == 1)

    assert np.all(mir_data.sp_data["igq"] == 0)

    assert np.all(mir_data.sp_data["iband"] == [0, 1, 2, 3, 4] * 4)

    assert np.all(mir_data.sp_data["ipstate"] == 0)

    assert np.all(mir_data.sp_data["tau0"] == 0.0)

    assert np.all(mir_data.sp_data["cabinLO"] == 0.0)

    assert np.all(mir_data.sp_data["corrLO1"] == 0.0)

    assert np.all(mir_data.sp_data["vradcat"] == 0.0)

    assert np.all(mir_data.sp_data["nch"] == [4, 16384, 16384, 16384, 16384] * 4)

    assert np.all(mir_data.sp_data["corrblock"] == [0, 1, 1, 1, 1] * 4)

    assert np.all(mir_data.sp_data["corrchunk"] == [0, 1, 2, 3, 4] * 4)

    assert np.all(mir_data.sp_data["correlator"] == 1)

    assert np.all(mir_data.sp_data["iddsmode"] == 0)

    assert np.all(mir_data.sp_data["spareshort"] == 0)

    assert np.all(mir_data.sp_data["spareint3"] == 0)

    assert np.all(mir_data.sp_data["spareint4"] == 0)

    assert np.all(mir_data.sp_data["spareint5"] == 0)

    assert np.all(mir_data.sp_data["spareint6"] == 0)

    assert np.all(mir_data.sp_data["tssb"] == 0.0)

    assert np.all(mir_data.sp_data["fDDS"] == 0.0)

    assert np.all(mir_data.sp_data["sparedbl3"] == 0.0)

    assert np.all(mir_data.sp_data["sparedbl4"] == 0.0)

    assert np.all(mir_data.sp_data["sparedbl5"] == 0.0)

    assert np.all(mir_data.sp_data["sparedbl6"] == 0.0)


def test_mir_remember_me_vis_data(mir_data):
    """
    Mir sch_read checker.

    Make sure that certain values in the sch_read file of the test data set match what
    we know to be 'true' at the time of observations.
    """
    mir_data.load_data(load_raw=True)
    # Now check sch_read related values. Thanks to a glitch in the data recorder,
    # all of the pseudo-cont values are the same for the test file.
    assert np.all(
        sp_raw["scale_fac"] == -26 if (np.mod(idx, 5) == 0) else True
        for idx, sp_raw in enumerate(mir_data.raw_data.values())
    )

    check_arr = np.array([-4302, -20291, -5261, -21128, -4192, -19634, -4999, -16346])

    assert np.all(
        np.all(sp_raw["data"] == check_arr) if (np.mod(idx, 5) == 0) else True
        for idx, sp_raw in enumerate(mir_data.raw_data.values())
    )


# pyuvdata/uvdata/tests/test_miriad.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for Miriad object.

Note that because of the way that file handling is done in the C++ API, a miriad
file is not closed until the destructor function (tp_dealloc) is called.

The following lines were made before a major rewrite of the miriad interface.
As of April 2020, they should no longer necessarily be true,
but are preserved here in case segfaults arise again.

Due to implementation details of CPython, it is sometimes not enough to `del` the
object--a manual garbage collection may be required. When adding new tests,
proper cleanup consists of: (1) deleting any Miriad objects or UVData objects,
(2) performing garbage collection (with `gc.collect()`), and (3) removing the
directory corresponding to the miriad file. Each test should do this, to avoid
open file handles interfering with other tests. The exception to this is if a
test uses the `uv_in_paper` or `uv_in_uvfits` fixture, as these handle cleanup
on their own.
"""
import os
import shutil

import numpy as np
import pytest
from astropy import constants as const
from astropy.coordinates import Angle
from astropy.time import Time, TimeDelta

import pyuvdata.tests as uvtest
import pyuvdata.utils as uvutils
from pyuvdata import UVData
from pyuvdata.data import DATA_PATH

from ..miriad import Miriad
from ..uvdata import _future_array_shapes_warning

aipy_extracts = pytest.importorskip("pyuvdata.uvdata.aipy_extracts")

# always ignore the Altitude not present warning
pytestmark = pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad")

paper_miriad_file = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")


@pytest.fixture(scope="function")
def uv_in_paper(paper_miriad, tmp_path):
    uv_in = paper_miriad
    write_file = os.path.join(tmp_path, "outtest_miriad.uv")
    uv_in.write_miriad(write_file, clobber=True)
    uv_out = UVData()

    yield uv_in, uv_out, write_file

    # cleanup
    del uv_in, uv_out


@pytest.fixture(scope="function")
def uv_in_uvfits(paper_miriad, tmp_path):
    uv_in = paper_miriad
    write_file = os.path.join(tmp_path, "outtest_miriad.uvfits")

    uv_out = UVData()

    yield uv_in, uv_out, write_file

    # cleanup
    del uv_in, uv_out


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:Telescope ATCA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_read_write_read_atca(tmp_path, future_shapes):
    uv_in = UVData()
    uv_out = UVData()
    atca_file = os.path.join(DATA_PATH, "atca_miriad/")
    testfile = os.path.join(tmp_path, "outtest_atca_miriad.uv")
    with uvtest.check_warnings(
        [UserWarning] * 6 + [DeprecationWarning],
        [
            "Altitude is not present in Miriad file, and "
            "telescope ATCA is not in known_telescopes. ",
            "Altitude is not present",
            "Telescope location is set at sealevel at the file lat/lon "
            "coordinates. Antenna positions are present, but the mean antenna "
            "position does not give a telescope_location on the surface of the "
            "earth. Antenna positions do not appear to be on the surface of the "
            "earth and will be treated as relative.",
            "Telescope ATCA is not in known_telescopes.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "It is not clear from the file if the data are projected or not.",
            _future_array_shapes_warning,
        ],
    ):
        uv_in.read(atca_file)

    if future_shapes:
        uv_in.use_future_array_shapes()

    uv_in.write_miriad(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=future_shapes)

    # make sure filename is what we expect
    assert uv_in.filename[0] == "atca_miriad"
    assert uv_out.filename[0] == "outtest_atca_miriad.uv"
    assert len(uv_in.filename) == len(uv_out.filename)
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_nrao_write_miriad_read_miriad(casa_uvfits, tmp_path):
    """Test reading in a CASA tutorial uvfits file, writing and reading as miriad"""
    uvfits_uv = casa_uvfits
    miriad_uv = UVData()
    writefile = os.path.join(tmp_path, "outtest_miriad.uv")
    uvfits_uv.write_miriad(writefile, clobber=True)
    miriad_uv.read(writefile, use_future_array_shapes=True)

    # check that setting projected also works
    miriad_uv2 = UVData.from_file(
        writefile, projected=True, use_future_array_shapes=True
    )
    assert miriad_uv2 == miriad_uv

    # check that setting projected also works
    with uvtest.check_warnings(
        [DeprecationWarning, UserWarning, UserWarning],
        match=[
            "The phase_type parameter is deprecated, use the projected parameter "
            "instead.",
            "The uvw_array does not match the expected values given the antenna",
            "Telescope EVLA is not",
        ],
    ):
        miriad_uv2 = UVData.from_file(
            writefile, phase_type="phased", use_future_array_shapes=True
        )
    assert miriad_uv2 == miriad_uv

    # make sure filename is what we expect
    assert uvfits_uv.filename[0] == "day2_TDEM0003_10s_norx_1src_1spw.uvfits"
    assert miriad_uv.filename[0] == "outtest_miriad.uv"
    assert len(uvfits_uv.filename) == len(miriad_uv.filename)
    uvfits_uv.filename = miriad_uv.filename

    miriad_uv._consolidate_phase_center_catalogs(
        reference_catalog=uvfits_uv.phase_center_catalog
    )
    assert uvfits_uv == miriad_uv


@pytest.mark.filterwarnings("ignore:LST values stored in this file are not ")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_write_read_carma(tmp_path):
    uv_in = UVData()
    uv_out = UVData()
    carma_file = os.path.join(DATA_PATH, "carma_miriad")
    testfile = os.path.join(tmp_path, "outtest_carma_miriad.uv")

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, "
            "using known location values for SZA.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "pamatten in extra_keywords is a list, array or dict",
            "psys in extra_keywords is a list, array or dict",
            "psysattn in extra_keywords is a list, array or dict",
            "ambpsys in extra_keywords is a list, array or dict",
            "bfmask in extra_keywords is a list, array or dict",
        ],
    ):
        uv_in.read(carma_file, use_future_array_shapes=True)

    # Extra keywords cannnot handle lists, dicts, or arrays, so drop them from the
    # dataset, so that the writer doesn't run into issues.
    # TODO: Capture these extra keywords
    for item in list(uv_in.extra_keywords.keys()):
        if isinstance(uv_in.extra_keywords[item], dict):
            uv_in.extra_keywords.pop(item)
        elif isinstance(uv_in.extra_keywords[item], list):
            uv_in.extra_keywords.pop(item)
        elif isinstance(uv_in.extra_keywords[item], np.ndarray):
            uv_in.extra_keywords.pop(item)

    uv_in.write_miriad(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filename is what we expect
    assert uv_in.filename == ["carma_miriad"]
    assert uv_out.filename == ["outtest_carma_miriad.uv"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # We should get the same result if we feed in these parameters, since the original
    # file had the LST calculated on read, and its def a phased dataset
    uv_out.read(testfile, calc_lst=False, use_future_array_shapes=True)

    assert uv_in == uv_out

    uv_in.write_miriad(testfile, clobber=True, calc_lst=True)
    uv_out.read(testfile, calc_lst=False, use_future_array_shapes=True)

    # Finally, make sure that if we calc LSTs on write, but not read, that we still
    # get the same answer.
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:pamatten in extra_keywords is a list, array")
@pytest.mark.filterwarnings("ignore:psys in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:psysattn in extra_keywords is a list, array or")
@pytest.mark.filterwarnings("ignore:ambpsys in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:bfmask in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:LST values stored in this file are not ")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
def test_read_carma_miriad_write_ms(tmp_path):
    """
    Check a roundtrip between CARMA-MIRIAD and MS formats.

    Note that this check does a few different operations -- these are consolidated into
    one test because several of them rely upon manipulating the same file object.
    """
    pytest.importorskip("casacore")
    from casacore import tables

    uv_in = UVData()
    uv_out = UVData()
    carma_file = os.path.join(DATA_PATH, "carma_miriad")
    testfile = os.path.join(tmp_path, "outtest_carma_miriad.ms")

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, "
            "using known location values for SZA.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "pamatten in extra_keywords is a list, array or dict",
            "psys in extra_keywords is a list, array or dict",
            "psysattn in extra_keywords is a list, array or dict",
            "ambpsys in extra_keywords is a list, array or dict",
            "bfmask in extra_keywords is a list, array or dict",
        ],
    ):
        uv_in.read(carma_file, use_future_array_shapes=True)

    # MIRIAD is missing these in the file, so we'll fill it in here.
    uv_in.antenna_diameters = np.zeros(uv_in.Nants_telescope)
    uv_in.antenna_diameters[:6] = 10.0
    uv_in.antenna_diameters[15:] = 3.5

    # We need to recalculate app coords here for one source ("NOISE"), which was
    # not actually correctly calculated in the online CARMA system (long story). Since
    # the MS format requires recalculating apparent coords after read in, we'll
    # calculate them here just to verify that everything matches.
    uv_in._set_app_coords_helper()
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna ",
            "pamatten in extra_keywords is a list, array or dict",
            "psys in extra_keywords is a list, array or dict",
            "psysattn in extra_keywords is a list, array or dict",
            "ambpsys in extra_keywords is a list, array or dict",
            "bfmask in extra_keywords is a list, array or dict",
            "Writing in the MS file that the units of the data are",
        ],
    ):
        uv_in.write_ms(testfile, clobber=True)

    uv_out.read(testfile, use_future_array_shapes=True)

    # Make sure the MS extra keywords are as expected
    assert uv_out.extra_keywords["DATA_COL"] == "DATA"
    assert uv_out.extra_keywords["observer"] == "SZA"
    uv_in.extra_keywords["DATA_COL"] = "DATA"
    uv_in.extra_keywords["observer"] = "SZA"

    # make sure filename is what we expect
    assert uv_in.filename == ["carma_miriad"]
    assert uv_out.filename == ["outtest_carma_miriad.ms"]
    uv_in.filename = uv_out.filename

    # Do a quick check on the history to verify they're different.
    assert uv_in.history != uv_out.history
    uv_in.history = uv_out.history

    # Final equality check
    assert uv_in.__eq__(uv_out, allowed_failures=["filename"])

    # Manipulate the table so that all fields have the same name (as is permitted
    # for MS files), and wipe out the source_ID information
    tb_field = tables.table(os.path.join(testfile, "FIELD"), ack=False, readonly=False)
    tb_field.putcol("NAME", ["TEST"] * 3)
    tb_field.removecols("SOURCE_ID")
    tb_field.close()

    # Check and see that the naming convention lines up as expected -- only the internal
    # catalog entries (specifically the names/keys and catalog IDs) should have changed.
    uv_out.read(testfile, use_future_array_shapes=True)
    for idx in range(3):
        assert uv_out.phase_center_catalog[idx]["cat_name"] == "TEST-%03i" % idx

    uv_out.phase_center_catalog = uv_in.phase_center_catalog
    uv_out._set_app_coords_helper()

    # Final equality check
    assert uv_in.__eq__(uv_out, allowed_failures=["filename"])

    # Check to make sure we raise an error if overwritting a file w/o clobber enabled
    with pytest.raises(OSError, match="File exists; skipping"):
        uv_in.write_ms(testfile, clobber=False)


@pytest.mark.filterwarnings("ignore:LST values stored in this file are not ")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_miriad_write_uvfits(uv_in_uvfits):
    """
    Miriad to uvfits loopback test.

    Read in Miriad files, write out as uvfits, read back in and check for
    object equality.
    """
    miriad_uv, uvfits_uv, testfile = uv_in_uvfits

    miriad_uv.write_uvfits(testfile, force_phase=True)
    uvfits_uv.read_uvfits(testfile, use_future_array_shapes=True)

    # make sure filename is what we expect
    assert miriad_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uvfits_uv.filename == ["outtest_miriad.uvfits"]
    miriad_uv.filename = uvfits_uv.filename

    for item in ["dut1", "earth_omega", "gst0", "rdate", "timesys"]:
        # Check to make sure that the UVFITS-specific paramters are set on the
        # UVFITS-based obj, and not on our original object. Then set it to None for the
        # UVFITS-based obj.
        assert getattr(miriad_uv, item) is None
        assert getattr(uvfits_uv, item) is not None
        setattr(uvfits_uv, item, None)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=miriad_uv.phase_center_catalog, ignore_name=True
    )
    assert miriad_uv == uvfits_uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_miriad_read_warning_lat_lon_corrected():
    miriad_uv = UVData()
    # check warning when correct_lat_lon is set to False
    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, using known location "
            "altitude value for PAPER and lat/lon from file.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        miriad_uv.read(
            paper_miriad_file, correct_lat_lon=False, use_future_array_shapes=True
        )


def test_read_miriad_phasing_errors():
    miriad_uv = UVData()
    # check that setting the phase_type to something wrong errors
    with pytest.raises(
        ValueError, match="The phase_type was not one of the recognized options."
    ):
        with uvtest.check_warnings(
            DeprecationWarning, match="The phase_type parameter is deprecated"
        ):
            miriad_uv.read(
                paper_miriad_file, phase_type="foo", use_future_array_shapes=True
            )


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_wronglatlon():
    """
    Check for appropriate warnings with incorrect lat/lon values or missing telescope

    To test this, we needed files without altitudes and with wrong lat, lon or
    telescope values.
    These test files were made commenting out the line in miriad.py that adds altitude
    to the file and running the following code:
    import os
    import numpy as np
    from pyuvdata import UVData
    from pyuvdata.data import DATA_PATH
    uv_in = UVData()
    uv_out = UVData()
    miriad_file = os.path.join(DATA_PATH, 'zen.2456865.60537.xy.uvcRREAA')
    latfile = os.path.join(DATA_PATH, 'zen.2456865.60537_wronglat.xy.uvcRREAA')
    lonfile = os.path.join(DATA_PATH, 'zen.2456865.60537_wronglon.xy.uvcRREAA')
    latlonfile = os.path.join(DATA_PATH, 'zen.2456865.60537_wronglatlon.xy.uvcRREAA')
    telescopefile = os.path.join(DATA_PATH,
    'zen.2456865.60537_wrongtelecope.xy.uvcRREAA')
    uv_in.read(miriad_file)
    uv_in.select(times=uv_in.time_array[0])
    uv_in.select(freq_chans=[0])

    lat, lon, alt = uv_in.telescope_location_lat_lon_alt
    lat_wrong = lat + 10 * np.pi / 180.
    uv_in.telescope_location_lat_lon_alt = (lat_wrong, lon, alt)
    uv_in.write_miriad(latfile, clobber=True)
    uv_out.read(latfile)

    lon_wrong = lon + 10 * np.pi / 180.
    uv_in.telescope_location_lat_lon_alt = (lat, lon_wrong, alt)
    uv_in.write_miriad(lonfile, clobber=True)
    uv_out.read(lonfile)

    uv_in.telescope_location_lat_lon_alt = (lat_wrong, lon_wrong, alt)
    uv_in.write_miriad(latlonfile)
    uv_out.read(latlonfile)

    uv_in.telescope_location_lat_lon_alt = (lat, lon, alt)
    uv_in.telescope_name = 'foo'
    uv_in.write_miriad(telescopefile, clobber=True)
    uv_out.read(telescopefile, run_check=False)

    """
    uv_in = UVData()
    latfile = os.path.join(DATA_PATH, "zen.2456865.60537_wronglat.xy.uvcRREAA")
    lonfile = os.path.join(DATA_PATH, "zen.2456865.60537_wronglon.xy.uvcRREAA")
    latlonfile = os.path.join(DATA_PATH, "zen.2456865.60537_wronglatlon.xy.uvcRREAA")
    telescopefile = os.path.join(
        DATA_PATH, "zen.2456865.60537_wrongtelecope.xy.uvcRREAA"
    )
    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in file and latitude value does not match",
            "projected is False, but RA, Dec is off from lst, latitude by more than "
            "1.0 deg",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "It is not clear from the file if the data are projected or not.",
        ],
    ):
        uv_in.read(latfile, use_future_array_shapes=True)

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in file and longitude value does not match",
            "projected is False, but RA, Dec is off from lst, latitude by more than "
            "1.0 deg",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "It is not clear from the file if the data are projected or not.",
        ],
    ):
        uv_in.read(lonfile, use_future_array_shapes=True)

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in file and latitude and longitude "
            "values do not match",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "It is not clear from the file if the data are projected or not.",
        ],
    ):
        uv_in.read(latlonfile, correct_lat_lon=False, use_future_array_shapes=True)

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, and telescope",
            "Altitude is not present in Miriad file, and telescope",
            "Telescope location is not set, but antenna positions are "
            "present. Mean antenna latitude and longitude values match file "
            "values, so telescope_position will be set using the mean of the "
            "antenna altitudes",
            "Telescope foo is not in known_telescopes.",
            "It is not clear from the file if the data are projected or not.",
        ],
    ):
        uv_in.read(telescopefile, run_check=False, use_future_array_shapes=True)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_miriad_location_handling(paper_miriad_main, tmp_path):
    uv_in = paper_miriad_main
    uv_out = UVData()

    testfile = os.path.join(tmp_path, "outtest_miriad.uv")
    aipy_uv = aipy_extracts.UV(paper_miriad_file)

    if os.path.exists(testfile):
        shutil.rmtree(testfile)

    # Test for using antenna positions to get telescope position
    # extract antenna positions and rotate them for miriad
    nants = aipy_uv["nants"]
    rel_ecef_antpos = np.zeros((nants, 3), dtype=uv_in.antenna_positions.dtype)
    for ai, num in enumerate(uv_in.antenna_numbers):
        rel_ecef_antpos[num, :] = uv_in.antenna_positions[ai, :]

    # find zeros so antpos can be zeroed there too
    antpos_length = np.sqrt(np.sum(np.abs(rel_ecef_antpos) ** 2, axis=1))

    ecef_antpos = rel_ecef_antpos + uv_in.telescope_location
    longitude = uv_in.telescope_location_lat_lon_alt[1]
    antpos = uvutils.rotECEF_from_ECEF(ecef_antpos, longitude)

    # zero out bad locations (these are checked on read)
    antpos[np.where(antpos_length == 0), :] = [0, 0, 0]
    antpos = antpos.T.flatten() / const.c.to("m/ns").value

    # make new file
    aipy_uv2 = aipy_extracts.UV(testfile, status="new")
    # initialize headers from old file
    # change telescope name (so the position isn't set from known_telescopes)
    # and use absolute antenna positions
    aipy_uv2.init_from_uv(aipy_uv, override={"telescop": "foo", "antpos": antpos})
    # copy data from old file
    aipy_uv2.pipe(aipy_uv)
    aipy_uv2.close()

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, and "
            "telescope foo is not in known_telescopes. "
            "Telescope location will be set using antenna positions.",
            "Altitude is not present ",
            "Telescope location is not set, but antenna "
            "positions are present. Mean antenna latitude "
            "and longitude values match file values, so "
            "telescope_position will be set using the mean "
            "of the antenna altitudes",
            "Telescope foo is not in known_telescopes.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_out.read(testfile, use_future_array_shapes=True)

    # Test for handling when antenna positions have a different mean latitude
    # than the file latitude
    # make new file
    if os.path.exists(testfile):
        shutil.rmtree(testfile)
    aipy_uv = aipy_extracts.UV(paper_miriad_file)
    aipy_uv2 = aipy_extracts.UV(testfile, status="new")
    # initialize headers from old file
    # change telescope name (so the position isn't set from known_telescopes)
    # and use absolute antenna positions, change file latitude
    new_lat = aipy_uv["latitud"] * 1.5
    aipy_uv2.init_from_uv(
        aipy_uv, override={"telescop": "foo", "antpos": antpos, "latitud": new_lat}
    )
    # copy data from old file
    aipy_uv2.pipe(aipy_uv)
    aipy_uv2.close()

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, and "
            "telescope foo is not in known_telescopes. "
            "Telescope location will be set using antenna positions.",
            "Altitude is not present in Miriad file, and "
            "telescope foo is not in known_telescopes. "
            "Telescope location will be set using antenna positions.",
            "Telescope location is set at sealevel at the "
            "file lat/lon coordinates. Antenna positions "
            "are present, but the mean antenna latitude "
            "value does not match",
            "projected is False, but RA, Dec is off from lst, latitude by more than "
            "1.0 deg",
            "Telescope foo is not in known_telescopes.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_out.read(testfile, use_future_array_shapes=True)

    # Test for handling when antenna positions have a different mean longitude
    # than the file longitude
    # this is harder because of the rotation that's done on the antenna positions
    # make new file
    if os.path.exists(testfile):
        shutil.rmtree(testfile)
    aipy_uv = aipy_extracts.UV(paper_miriad_file)
    aipy_uv2 = aipy_extracts.UV(testfile, status="new")
    # initialize headers from old file
    # change telescope name (so the position isn't set from known_telescopes)
    # and use absolute antenna positions, change file longitude
    new_lon = aipy_uv["longitu"] + np.pi
    aipy_uv2.init_from_uv(
        aipy_uv, override={"telescop": "foo", "antpos": antpos, "longitu": new_lon}
    )
    # copy data from old file
    aipy_uv2.pipe(aipy_uv)
    aipy_uv2.close()

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, and "
            "telescope foo is not in known_telescopes. "
            "Telescope location will be set using antenna positions.",
            "Altitude is not present in Miriad file, and "
            "telescope foo is not in known_telescopes. "
            "Telescope location will be set using antenna positions.",
            "Telescope location is set at sealevel at the "
            "file lat/lon coordinates. Antenna positions "
            "are present, but the mean antenna longitude "
            "value does not match",
            "projected is False, but RA, Dec is off from lst, latitude by more than "
            "1.0 deg",
            "Telescope foo is not in known_telescopes.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_out.read(testfile, use_future_array_shapes=True)

    # Test for handling when antenna positions have a different mean longitude &
    # latitude than the file longitude
    # make new file
    if os.path.exists(testfile):
        shutil.rmtree(testfile)
    aipy_uv = aipy_extracts.UV(paper_miriad_file)
    aipy_uv2 = aipy_extracts.UV(testfile, status="new")
    # initialize headers from old file
    # change telescope name (so the position isn't set from known_telescopes)
    # and use absolute antenna positions, change file latitude and longitude
    aipy_uv2.init_from_uv(
        aipy_uv,
        override={
            "telescop": "foo",
            "antpos": antpos,
            "latitud": new_lat,
            "longitu": new_lon,
        },
    )
    # copy data from old file
    aipy_uv2.pipe(aipy_uv)
    aipy_uv2.close()

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, and "
            "telescope foo is not in known_telescopes. "
            "Telescope location will be set using antenna positions.",
            "Altitude is not present in Miriad file, and "
            "telescope foo is not in known_telescopes. "
            "Telescope location will be set using antenna positions.",
            "Telescope location is set at sealevel at the "
            "file lat/lon coordinates. Antenna positions "
            "are present, but the mean antenna latitude and "
            "longitude values do not match",
            "projected is False, but RA, Dec is off from lst, latitude by more than "
            "1.0 deg",
            "Telescope foo is not in known_telescopes.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_out.read(testfile, use_future_array_shapes=True)

    # Test for handling when antenna positions are far enough apart to make the
    # mean position inside the earth

    good_antpos = np.where(antpos_length > 0)[0]
    rot_ants = good_antpos[: len(good_antpos) // 2]
    rot_antpos = uvutils.rotECEF_from_ECEF(ecef_antpos[rot_ants, :], longitude + np.pi)
    modified_antpos = uvutils.rotECEF_from_ECEF(ecef_antpos, longitude)
    modified_antpos[rot_ants, :] = rot_antpos
    # zero out bad locations (these are checked on read)
    modified_antpos[np.where(antpos_length == 0), :] = [0, 0, 0]
    modified_antpos = modified_antpos.T.flatten() / const.c.to("m/ns").value

    # make new file
    if os.path.exists(testfile):
        shutil.rmtree(testfile)
    aipy_uv = aipy_extracts.UV(paper_miriad_file)
    aipy_uv2 = aipy_extracts.UV(testfile, status="new")
    # initialize headers from old file
    # change telescope name (so the position isn't set from known_telescopes)
    # and use modified absolute antenna positions
    aipy_uv2.init_from_uv(
        aipy_uv, override={"telescop": "foo", "antpos": modified_antpos}
    )
    # copy data from old file
    aipy_uv2.pipe(aipy_uv)
    aipy_uv2.close()

    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, and "
            "telescope foo is not in known_telescopes. "
            "Telescope location will be set using antenna positions.",
            "Altitude is not present ",
            "Telescope location is set at sealevel at the "
            "file lat/lon coordinates. Antenna positions "
            "are present, but the mean antenna position "
            "does not give a telescope_location on the "
            "surface of the earth.",
            "Telescope foo is not in known_telescopes.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_out.read(testfile, use_future_array_shapes=True)

    # cleanup
    aipy_uv.close()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_singletimeselect_unprojected(uv_in_paper):
    """
    Check behavior with writing & reading after selecting a single time from
    an unprojected file.
    """
    uv_in, uv_out, testfile = uv_in_paper

    uv_in_copy = uv_in.copy()
    uv_in.select(times=uv_in.time_array[0])
    uv_in.write_miriad(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filename is what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out

    # check that setting projected works
    uv_out.read(testfile, projected=False, use_future_array_shapes=True)
    assert uv_in == uv_out

    # also check that setting phase_type works
    with uvtest.check_warnings(
        [DeprecationWarning, UserWarning],
        match=[
            "The phase_type parameter is deprecated, use the projected parameter "
            "instead.",
            "The uvw_array does not match the expected values given the antenna",
        ],
    ):
        uv_out.read(testfile, phase_type="drift", use_future_array_shapes=True)
        assert uv_in == uv_out

    # check again with more than one time but only 1 unflagged time
    time_gt0_array = np.where(uv_in_copy.time_array > uv_in_copy.time_array[0])[0]
    uv_in_copy.flag_array[time_gt0_array, :, :] = True

    # get unflagged blts
    blt_good = np.where(~np.all(uv_in_copy.flag_array, axis=(1, 2)))
    assert np.isclose(np.mean(np.diff(uv_in_copy.time_array[blt_good])), 0.0)

    uv_in_copy.write_miriad(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filename is what we expect
    assert uv_in_copy.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in_copy.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in_copy)
    assert uv_in_copy == uv_out


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:The provided name zenith is already used")
@pytest.mark.parametrize("frame", ["fk5", "fk4"])
def test_loop_multi_phase(tmp_path, paper_miriad, frame):
    uv_in = paper_miriad
    testfile = os.path.join(tmp_path, "outtest_miriad.uv")
    testfile2 = os.path.join(tmp_path, "outtest_miriad2.uv")

    mask = np.full(uv_in.Nblts, False)
    mask[: uv_in.Nblts // 2] = True
    if frame == "fk5":
        epoch = 2000
    elif frame == "fk4":
        epoch = 1950
    uv_in.phase(
        ra=0, dec=0, phase_frame=frame, select_mask=mask, cat_name="foo", epoch=epoch
    )

    uv_in.write_miriad(testfile, clobber=True)
    uv2 = UVData.from_file(testfile, use_future_array_shapes=True)

    uv2._consolidate_phase_center_catalogs(other=uv_in)
    assert uv2 == uv_in

    # remove phsframe to test frame setting based on epoch
    aipy_uv = aipy_extracts.UV(testfile)
    # make new file
    aipy_uv2 = aipy_extracts.UV(testfile2, status="new")
    # initialize headers from old file
    aipy_uv2.init_from_uv(aipy_uv, exclude=["phsframe"])
    # copy data from old file
    aipy_uv2.pipe(aipy_uv)
    aipy_uv2.close()

    uv3 = UVData.from_file(testfile2, use_future_array_shapes=True)

    # without the "phsframe" variable, the unprojected phase center gets interpreted as
    # an ephem type phase center.
    zen_id, _ = uv3._look_in_catalog(cat_name="zenith")
    new_id = uv3._add_phase_center(cat_name="zenith", cat_type="unprojected")
    uv3.phase_center_id_array[np.nonzero(uv3.phase_center_id_array == zen_id)] = new_id
    uv3._clear_unused_phase_centers()
    uv3._consolidate_phase_center_catalogs(other=uv_in)

    assert uv3 == uv_in


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_miriad_multi_phase_error(tmp_path, paper_miriad):
    uv_in = paper_miriad
    testfile = os.path.join(tmp_path, "outtest_miriad.uv")

    mask = np.full(uv_in.Nblts, False)
    mask[: uv_in.Nblts // 2] = True
    uv_in.phase(
        ra=0, dec=0, phase_frame="fk5", select_mask=mask, cat_name="foo", epoch=200
    )

    uv_in.write_miriad(testfile, clobber=True)
    with pytest.raises(
        ValueError, match="projected is False but there are multiple sources."
    ):
        UVData.from_file(testfile, projected=False, use_future_array_shapes=True)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.parametrize("cut_ephem_pts", [True, False])
@pytest.mark.parametrize("extrapolate", [True, False])
def test_miriad_ephem(tmp_path, casa_uvfits, cut_ephem_pts, extrapolate):
    pytest.importorskip("astroquery")

    from ssl import SSLError

    from requests import RequestException

    uv_in = casa_uvfits
    # Need to spread out the times to get ra/dec changes that are different at
    # miriad's precision
    unique_times = np.unique(uv_in.time_array)
    for t_ind, ut in enumerate(unique_times):
        time_mask = np.nonzero(uv_in.time_array == ut)[0]
        uv_in.time_array[time_mask] += t_ind * 0.01
    uv_in.set_lsts_from_time_array()

    testfile = os.path.join(tmp_path, "outtest_miriad.uv")

    # Handle this part with care, since we don't want the test to fail if we are unable
    # to reach the JPL-Horizons service.
    try:
        uv_in.phase(ra=0, dec=0, epoch="J2000", lookup_name="Mars", cat_name="Mars")
    except (SSLError, RequestException) as err:
        pytest.skip("SSL/Connection error w/ JPL Horizons: " + str(err))

    if extrapolate:
        # change cat_times to force extrapolation
        uv_in.phase_center_catalog[1]["cat_times"] += 0.5

    if cut_ephem_pts:
        uv_in.phase_center_catalog[1]["cat_times"] = uv_in.phase_center_catalog[1][
            "cat_times"
        ][0]
        uv_in.phase_center_catalog[1]["cat_lon"] = uv_in.phase_center_catalog[1][
            "cat_lon"
        ][0]
        uv_in.phase_center_catalog[1]["cat_lat"] = uv_in.phase_center_catalog[1][
            "cat_lat"
        ][0]
        uv_in.phase_center_catalog[1]["cat_vrad"] = uv_in.phase_center_catalog[1][
            "cat_vrad"
        ][0]
        uv_in.phase_center_catalog[1]["cat_dist"] = uv_in.phase_center_catalog[1][
            "cat_dist"
        ][0]

    with uvtest.check_warnings(
        UserWarning,
        match="Some visibility times did not match ephem times so the ra and dec "
        "values for those visibilities were interpolated or set to the closest time "
        "if they would have required extrapolation.",
    ):
        uv_in.write_miriad(testfile, clobber=True)
    uv2 = UVData.from_file(testfile, use_future_array_shapes=True)

    uv2._update_phase_center_id(0, 1)
    uv2.phase_center_catalog[1]["info_source"] = uv_in.phase_center_catalog[1][
        "info_source"
    ]

    uv_in.print_phase_center_info()
    print("")
    uv2.print_phase_center_info()

    if cut_ephem_pts:
        # Only one ephem points results in only one ra/dec, so it is interpretted as
        # a sidereal rather than ephem phase center
        assert uv2.phase_center_catalog[1]["cat_type"] == "sidereal"
        assert (
            uv2.phase_center_catalog[1]["cat_lon"]
            == uv_in.phase_center_catalog[1]["cat_lon"]
        )
        assert (
            uv2.phase_center_catalog[1]["cat_lat"]
            == uv_in.phase_center_catalog[1]["cat_lat"]
        )

        # just replace the phase_center_catalog before testing for equality
        uv2.phase_center_catalog = uv_in.phase_center_catalog
    else:
        # the number of catalog times is changed
        # adjust phase center catalogs to make them equal
        assert uv2._phase_center_catalog != uv_in._phase_center_catalog
        uv2.phase_center_catalog[1]["cat_times"] = uv_in.phase_center_catalog[1][
            "cat_times"
        ]
        uv2.phase_center_catalog[1]["cat_lon"] = uv_in.phase_center_catalog[1][
            "cat_lon"
        ]
        uv2.phase_center_catalog[1]["cat_lat"] = uv_in.phase_center_catalog[1][
            "cat_lat"
        ]
        uv2.phase_center_catalog[1]["cat_vrad"] = uv_in.phase_center_catalog[1][
            "cat_vrad"
        ]
        uv2.phase_center_catalog[1]["cat_dist"] = uv_in.phase_center_catalog[1][
            "cat_dist"
        ]
        assert uv2._phase_center_catalog == uv_in._phase_center_catalog

    assert uv2 == uv_in


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_driftscan(tmp_path, paper_miriad):
    uv_in = paper_miriad
    testfile = os.path.join(tmp_path, "outtest_miriad.uv")

    uv2 = uv_in.copy()
    uv2.phase(
        lat=Angle("80d").rad,
        lon=0,
        phase_frame="altaz",
        cat_type="driftscan",
        cat_name="drift_alt80",
    )
    with uvtest.check_warnings(
        UserWarning,
        match="This object has a driftscan phase center. Miriad does not really ",
    ):
        uv2.write_miriad(testfile, clobber=True)

    uv3 = UVData.from_file(testfile, use_future_array_shapes=True)
    assert np.all(uv3._check_for_cat_type("ephem"))

    # put it back to a driftscan
    uv3._update_phase_center_id(0, new_id=1)
    uv3.phase_center_catalog = uv2.phase_center_catalog

    assert uv2 == uv3


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_poltoind(uv_in_paper):
    miriad_uv, _, _ = uv_in_paper

    pol_arr = miriad_uv.polarization_array

    miriad = miriad_uv._convert_to_filetype("miriad")
    miriad.polarization_array = None
    with pytest.raises(ValueError) as cm:
        miriad._pol_to_ind(pol_arr[0])
    assert str(cm.value).startswith(
        "Can't index polarization -7 because polarization_array is not set"
    )

    miriad.polarization_array = [pol_arr[0], pol_arr[0]]
    with pytest.raises(ValueError) as cm:
        miriad._pol_to_ind(pol_arr[0])
    assert str(cm.value).startswith("multiple matches for pol=-7 in polarization_array")


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "kwd_name,kwd_value,warnstr,errstr",
    (
        [
            "testdict",
            {"testkey": 23},
            "testdict in extra_keywords is a list, array or dict",
            "Extra keyword testdict is of <class 'dict'>",
        ],
        [
            "testlist",
            [12, 14, 90],
            "testlist in extra_keywords is a list, array or dict",
            "Extra keyword testlist is of <class 'list'>",
        ],
        [
            "testarr",
            np.array([12, 14, 90]),
            "testarr in extra_keywords is a list, array or dict",
            "Extra keyword testarr is of <class 'numpy.ndarray'>",
        ],
        [
            "test_long_key",
            True,
            "key test_long_key in extra_keywords is longer than 8 characters",
            None,
        ],
        [
            "complex1",
            np.complex64(5.3 + 1.2j),
            None,
            "Extra keyword complex1 is of <class 'numpy.complex64'>",
        ],
        [
            "complex2",
            6.9 + 4.6j,
            None,
            "Extra keyword complex2 is of <class 'complex'>",
        ],
    ),
)
def test_miriad_extra_keywords_errors(
    uv_in_paper, kwd_name, kwd_value, warnstr, errstr
):
    uv_in, _, testfile = uv_in_paper

    uvw_warn_str = "The uvw_array does not match the expected values"
    # check for warnings & errors with extra_keywords that are dicts, lists or arrays
    uv_in.extra_keywords[kwd_name] = kwd_value
    if warnstr is None:
        warnstr_list = [uvw_warn_str]
    else:
        warnstr_list = [warnstr, uvw_warn_str]

    with uvtest.check_warnings(UserWarning, warnstr_list):
        uv_in.check()

    if errstr is not None:
        with pytest.raises(TypeError, match=errstr):
            uv_in.write_miriad(testfile, clobber=True, run_check=False)
    else:
        with uvtest.check_warnings(UserWarning, warnstr):
            uv_in.write_miriad(testfile, clobber=True, run_check=False)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "kwd_names,kwd_values",
    (
        [["bool", "bool2"], [True, False]],
        [["int1", "int2"], [np.int64(5), 7]],
        [["float1", "float2"], [np.float64(5.3), 6.9]],
        [["str", "longstr"], ["hello", "this is a very long string " * 1000]],
    ),
)
def test_miriad_extra_keywords(uv_in_paper, tmp_path, kwd_names, kwd_values):
    uv_in, uv_out, testfile = uv_in_paper

    for name, value in zip(kwd_names, kwd_values):
        uv_in.extra_keywords[name] = value
    uv_in.write_miriad(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filename is what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_roundtrip_optional_params(uv_in_paper, tmp_path):
    uv_in, uv_out, testfile = uv_in_paper

    uv_in.x_orientation = "east"
    uv_in.reorder_blts()

    uv_in.write_miriad(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filename is what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out

    # test with bda as well (single entry in tuple)
    uv_in.reorder_blts(order="bda")

    uv_in.write_miriad(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_breakread_miriad(uv_in_paper, tmp_path):
    """Test Miriad file checking."""
    uv_in, uv_out, testfile = uv_in_paper

    with pytest.raises(IOError) as cm:
        uv_in.read("foo", file_type="miriad")
    assert str(cm.value).startswith("foo not found")

    uv_in_copy = uv_in.copy()
    uv_in_copy.Nblts += 10
    uv_in_copy.write_miriad(testfile, clobber=True, run_check=False)
    with uvtest.check_warnings(
        UserWarning, "Nblts does not match the number of unique blts in the data"
    ):
        uv_out.read(testfile, run_check=False, use_future_array_shapes=True)

    uv_in_copy = uv_in.copy()
    uv_in_copy.Nbls += 10
    uv_in_copy.write_miriad(testfile, clobber=True, run_check=False)
    with uvtest.check_warnings(
        UserWarning, "Nbls does not match the number of unique baselines in the data"
    ):
        uv_out.read(testfile, run_check=False, use_future_array_shapes=True)

    uv_in_copy = uv_in.copy()
    uv_in_copy.Ntimes += 10
    uv_in_copy.write_miriad(testfile, clobber=True, run_check=False)
    with uvtest.check_warnings(
        UserWarning, "Ntimes does not match the number of unique times in the data"
    ):
        uv_out.read(testfile, run_check=False, use_future_array_shapes=True)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_write_read_miriad(uv_in_paper):
    """
    PAPER file Miriad loopback test.

    Read in Miriad PAPER file, write out as new Miriad file, read back in and
    check for object equality.
    """
    uv_in, uv_out, write_file = uv_in_paper
    uv_out.read(write_file, use_future_array_shapes=True)

    # make sure filename is what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out

    # check that we can read & write phased data
    uv_in2 = uv_in.copy()
    uv_in2.phase_to_time(Time(np.mean(uv_in2.time_array), format="jd"))
    uv_in2.write_miriad(write_file, clobber=True)
    uv_out.read(write_file, use_future_array_shapes=True)

    uv_out._consolidate_phase_center_catalogs(other=uv_in2)
    assert uv_in2 == uv_out
    del uv_in2

    # check that trying to overwrite without clobber raises an error
    with pytest.raises(OSError) as cm:
        uv_in.write_miriad(write_file, clobber=False)
    assert str(cm.value).startswith("File exists: skipping")

    # check that if x_orientation is set, it's read back out properly
    uv_in.x_orientation = "east"
    uv_in.write_miriad(write_file, clobber=True)
    uv_out.read(write_file, use_future_array_shapes=True)
    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_miriad_antenna_diameters(uv_in_paper):
    # check that if antenna_diameters is set, it's read back out properly
    uv_in, uv_out, write_file = uv_in_paper
    uv_in.antenna_diameters = (
        np.zeros((uv_in.Nants_telescope,), dtype=np.float32) + 14.0
    )
    uv_in.write_miriad(write_file, clobber=True)
    uv_out.read(write_file, use_future_array_shapes=True)

    # make sure that filenames make sense
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out

    # check that antenna diameters get written if not exactly float
    uv_in.antenna_diameters = (
        np.zeros((uv_in.Nants_telescope,), dtype=np.float32) + 14.0
    )
    uv_in.write_miriad(write_file, clobber=True)
    uv_out.read(write_file, use_future_array_shapes=True)
    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out

    # check warning when antenna diameters vary
    uv_in.antenna_diameters = (
        np.zeros((uv_in.Nants_telescope,), dtype=np.float32) + 14.0
    )
    uv_in.antenna_diameters[1] = 15.0
    with uvtest.check_warnings(
        UserWarning,
        match=[
            "The uvw_array does not match the expected values",
            "Antenna diameters are not uniform",
        ],
    ):
        uv_in.write_miriad(write_file, clobber=True)
    uv_out.read(write_file, use_future_array_shapes=True)
    assert uv_out.antenna_diameters is None
    uv_out.antenna_diameters = uv_in.antenna_diameters
    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only,")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:It is not clear from the file if the data are")
def test_miriad_write_read_diameters(tmp_path):
    uv_in = UVData()
    uv_out = UVData()
    write_file = os.path.join(tmp_path, "outtest_miriad.uv")
    # check for backwards compatibility with old keyword 'diameter' for
    # antenna diameters
    testfile_diameters = os.path.join(DATA_PATH, "zen.2457698.40355.xx.HH.uvcA")
    uv_in.read(testfile_diameters, use_future_array_shapes=True)
    uv_in.write_miriad(write_file, clobber=True)
    uv_out.read(write_file, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2457698.40355.xx.HH.uvcA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_miriad_and_aipy_reads(uv_in_paper):
    uv_in, _, write_file = uv_in_paper
    # check that variables 'ischan' and 'nschan' were written to new file
    # need to use aipy, since pyuvdata is not currently capturing these variables
    uv_aipy = aipy_extracts.UV(write_file)
    nfreqs = uv_in.Nfreqs
    nschan = uv_aipy["nschan"]
    ischan = uv_aipy["ischan"]
    assert nschan == nfreqs
    assert ischan == 1

    # cleanup
    uv_aipy.close()


def test_miriad_telescope_locations():
    # test load_telescope_coords w/ blank Miriad
    uv_in = Miriad()
    uv = aipy_extracts.UV(paper_miriad_file)
    uv_in._load_telescope_coords(uv)
    assert uv_in.telescope_location_lat_lon_alt is not None
    uv.close()
    # test load_antpos w/ blank Miriad
    uv_in = Miriad()
    uv = aipy_extracts.UV(paper_miriad_file)
    uv_in._load_antpos(uv)
    assert uv_in.antenna_positions is not None


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_miriad_integration_time_precision(uv_in_paper):
    uv_in, uv_out, write_file = uv_in_paper

    # test that changing precision of integraiton_time is okay
    # tolerance of integration_time (1e-3) is larger than floating point type
    # conversions
    uv_in.integration_time = uv_in.integration_time.astype(np.float32)
    uv_in.write_miriad(write_file, clobber=True)
    new_uv = UVData()
    new_uv.read(write_file, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert new_uv.filename == ["outtest_miriad.uv"]
    uv_in.filename = new_uv.filename

    new_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == new_uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "select_kwargs",
    [
        {"bls": [(0, 0), (0, 1), (4, 2)]},
        {"bls": [(0, 0), (2, 4)], "antenna_nums": [0, 2, 4]},
        {"bls": (2, 4, "xy")},
        {"bls": [(4, 2, "yx")]},
        {"polarizations": [-7], "bls": [(4, 4)]},
        {"bls": [(4, 4, "xy")]},
    ],
)
def test_read_write_read_miriad_partial_bls(uv_in_paper, select_kwargs, tmp_path):
    # check partial read selections
    full, uv_out, write_file = uv_in_paper

    full.write_miriad(write_file, clobber=True)
    uv_in = UVData()

    # test only specified bls were read, and that flipped antpair is loaded too
    uv_in.read(write_file, use_future_array_shapes=True, **select_kwargs)
    antpairs = uv_in.get_antpairs()
    # indexing here is to ignore polarization if present, maybe there is a better way
    bls = select_kwargs["bls"]
    if isinstance(bls, tuple):
        bls = [bls]
    assert np.all([bl[:2] in antpairs or bl[:2][::-1] in antpairs for bl in bls])
    exp_uv = full.select(inplace=False, **select_kwargs)

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    exp_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == exp_uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_write_read_miriad_partial_antenna_nums(uv_in_paper, tmp_path):
    full, uv_out, write_file = uv_in_paper

    # check partial read selections
    full.write_miriad(write_file, clobber=True)
    uv_in = UVData()
    # test all bls w/ 0 are loaded
    uv_in.read(write_file, antenna_nums=[0], use_future_array_shapes=True)
    diff = set(full.get_antpairs()) - set(uv_in.get_antpairs())
    assert 0 not in np.unique(diff)
    exp_uv = full.select(antenna_nums=[0], inplace=False)
    assert np.max(exp_uv.ant_1_array) == 0
    assert np.max(exp_uv.ant_2_array) == 0

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    exp_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == exp_uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "select_kwargs",
    [
        {"time_range": [2456865.607, 2456865.609]},
        {"time_range": [2456865.607, 2456865.609], "antenna_nums": [0]},
        {"time_range": [2456865.607, 2456865.609], "polarizations": [-7]},
    ],
)
def test_read_write_read_miriad_partial_times(uv_in_paper, select_kwargs, tmp_path):
    full, uv_out, write_file = uv_in_paper

    # check partial read selections
    full.write_miriad(write_file, clobber=True)
    # test time loading
    uv_in = UVData()
    uv_in.read(write_file, use_future_array_shapes=True, **select_kwargs)
    full_times = np.unique(
        full.time_array[
            (full.time_array > select_kwargs["time_range"][0])
            & (full.time_array < select_kwargs["time_range"][1])
        ]
    )
    assert np.isclose(np.unique(uv_in.time_array), full_times).all()
    # The exact time are calculated above, pop out the time range to compare
    # time range with selecting on exact times
    select_kwargs.pop("time_range", None)
    exp_uv = full.select(times=full_times, inplace=False, **select_kwargs)

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    exp_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == exp_uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("pols", [["xy"], [-7]])
def test_read_write_read_miriad_partial_pols(uv_in_paper, pols, tmp_path):
    full, uv_out, write_file = uv_in_paper

    # check partial read selections
    full.write_miriad(write_file, clobber=True)

    # test polarization loading
    uv_in = UVData()
    uv_in.read(write_file, polarizations=pols, use_future_array_shapes=True)
    assert full.polarization_array == uv_in.polarization_array
    exp_uv = full.select(polarizations=pols, inplace=False)

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    exp_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == exp_uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_write_read_miriad_partial_ant_str(uv_in_paper, tmp_path):
    full, uv_out, write_file = uv_in_paper

    # check partial read selections
    full.write_miriad(write_file, clobber=True)
    # test ant_str
    uv_in = UVData()
    uv_in.read(write_file, ant_str="auto", use_future_array_shapes=True)
    assert np.array([blp[0] == blp[1] for blp in uv_in.get_antpairs()]).all()
    exp_uv = full.select(ant_str="auto", inplace=False)

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    exp_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == exp_uv

    uv_in.read(write_file, ant_str="cross", use_future_array_shapes=True)
    assert np.array([blp[0] != blp[1] for blp in uv_in.get_antpairs()]).all()
    exp_uv = full.select(ant_str="cross", inplace=False)

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    exp_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == exp_uv

    uv_in.read(write_file, ant_str="all", use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    uv_in._consolidate_phase_center_catalogs(other=full)
    assert uv_in == full


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "err_type,select_kwargs,err_msg",
    [
        (
            ValueError,
            {"ant_str": "auto", "antenna_nums": [0, 1]},
            "Cannot provide ant_str with antenna_nums or bls",
        ),
        (ValueError, {"bls": "foo"}, "bls must be a list of tuples of antenna numbers"),
        (
            ValueError,
            {"bls": [[0, 1]]},
            "bls must be a list of tuples of antenna numbers",
        ),
        (
            ValueError,
            {"bls": [("foo", "bar")]},
            "bls must be a list of tuples of antenna numbers",
        ),
        (
            ValueError,
            {"bls": [("foo",)]},
            "bls tuples must be all length-2 or all length-3",
        ),
        (
            ValueError,
            {"bls": [(1, 2), (2, 3, "xx")]},
            "bls tuples must be all length-2 or all length-3",
        ),
        (
            ValueError,
            {"bls": [(2, 4, 0)]},
            "The third element in each bl must be a polarization string",
        ),
        (
            ValueError,
            {"bls": [(2, 4, "xy")], "polarizations": ["xy"]},
            "Cannot provide length-3 tuples and also specify polarizations.",
        ),
        (
            ValueError,
            {"antenna_nums": np.array([(0, 10)])},
            "antenna_nums must be a list of antenna number integers",
        ),
        (
            ValueError,
            {"antenna_nums": 5},
            "antenna_nums must be a list of antenna number integers",
        ),
        (
            ValueError,
            {"antenna_nums": ["foo"]},
            "antenna_nums must be a list of antenna number integers",
        ),
        (
            ValueError,
            {"polarizations": "xx"},
            "pols must be a list of polarization strings or ints",
        ),
        (
            ValueError,
            {"polarizations": [5.3]},
            "pols must be a list of polarization strings or ints",
        ),
        (
            ValueError,
            {"polarizations": ["yy"]},
            "No data is present, probably as a result of select on read",
        ),
        (ValueError, {"polarizations": [-9]}, "No polarizations in data matched input"),
        (
            ValueError,
            {"time_range": "foo"},
            "time_range must be a len-2 list of Julian Date floats",
        ),
        (
            ValueError,
            {"time_range": [1, 2, 3]},
            "time_range must be a len-2 list of Julian Date floats",
        ),
        (
            ValueError,
            {"time_range": ["foo", "bar"]},
            "time_range must be a len-2 list of Julian Date floats",
        ),
        (
            ValueError,
            {"time_range": [10.1, 10.2]},
            "No data is present, probably as a result of select on read",
        ),
        (ValueError, {"ant_str": 0}, "ant_str must be a string"),
    ],
)
def test_read_write_read_miriad_partial_errors(
    uv_in_paper, err_type, select_kwargs, err_msg, tmp_path
):
    full, uv_out, write_file = uv_in_paper

    # check partial read selections
    full.write_miriad(write_file, clobber=True)
    uv_in = UVData()

    with pytest.raises(err_type, match=err_msg):
        uv_in.read(write_file, **select_kwargs)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_write_read_miriad_partial_with_warnings(uv_in_paper, tmp_path):
    full, uv_out, write_file = uv_in_paper

    # check partial read selections
    full.write_miriad(write_file, clobber=True)

    uv_in = UVData()
    # check handling for generic read selections unsupported by read_miriad
    unique_times = np.unique(full.time_array)
    times_to_keep = unique_times[
        ((unique_times > 2456865.607) & (unique_times < 2456865.609))
    ]
    with uvtest.check_warnings(
        UserWarning,
        [
            "Warning: a select on read keyword is set",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_in.read(write_file, times=times_to_keep, use_future_array_shapes=True)

    exp_uv = full.select(times=times_to_keep, inplace=False)

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    exp_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == exp_uv

    uv_in = UVData()
    # check handling for generic read selections unsupported by read_miriad
    blts_select = np.where(full.time_array == unique_times[0])[0]
    ants_keep = [0, 2, 4]
    with uvtest.check_warnings(
        UserWarning,
        [
            "Warning: blt_inds is set along with select on read",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_in.read(
            write_file,
            blt_inds=blts_select,
            antenna_nums=ants_keep,
            use_future_array_shapes=True,
        )
    exp_uv = full.select(blt_inds=blts_select, antenna_nums=ants_keep, inplace=False)

    # make sure filenames are what we expect
    assert uv_in.filename == ["outtest_miriad.uv"]
    assert exp_uv.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    uv_in.filename = exp_uv.filename

    exp_uv._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in != exp_uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_write_read_miriad_partial_metadata_only(uv_in_paper, tmp_path):
    uv_in, uv_out, write_file = uv_in_paper
    write_file2 = os.path.join(tmp_path, "outtest_miriad2.uv")

    # try metadata only read
    uv_in_meta = UVData()
    uv_in_meta.read(paper_miriad_file, read_data=False, use_future_array_shapes=True)
    assert uv_in_meta.time_array is None
    assert uv_in_meta.data_array is None
    assert uv_in_meta.integration_time is None
    metadata = [
        "antenna_positions",
        "antenna_names",
        "antenna_positions",
        "channel_width",
        "history",
        "vis_units",
        "telescope_location",
    ]
    for m in metadata:
        assert getattr(uv_in_meta, m) is not None

    # metadata only multiple file read-in
    del uv_in_meta

    new_uv = uv_in.select(freq_chans=np.arange(5), inplace=False)
    new_uv.write_miriad(write_file, clobber=True)
    new_uv = uv_in.select(freq_chans=np.arange(5) + 5, inplace=False)
    new_uv.write_miriad(write_file2, clobber=True)

    uv_in.select(freq_chans=np.arange(10))

    uv_in2 = UVData()
    uv_in2.read(np.array([write_file, write_file2]), use_future_array_shapes=True)

    assert uv_in.history != uv_in2.history
    uv_in2.history = uv_in.history

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert set(uv_in2.filename) == {"outtest_miriad.uv", "outtest_miriad2.uv"}
    uv_in2.filename = uv_in.filename
    uv_in2._filename.form = (1,)

    uv_in2._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_in2


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_ms_write_miriad_casa_history(tmp_path):
    """
    Read in .ms file.
    Write to a miriad file, read back in and check for history parameter
    """
    pytest.importorskip("casacore")
    ms_uv = UVData()
    miriad_uv = UVData()
    ms_file = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.ms")
    testfile = os.path.join(tmp_path, "outtest_miriad")
    ms_uv.read(ms_file, use_future_array_shapes=True)

    ms_uv.write_miriad(testfile, clobber=True)
    miriad_uv.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert miriad_uv.filename == ["outtest_miriad"]
    assert ms_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.ms"]
    miriad_uv.filename = ms_uv.filename

    # propagate scan numbers to the uvfits, ONLY for comparison
    miriad_uv.scan_number_array = ms_uv.scan_number_array

    assert miriad_uv == ms_uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_rwr_miriad_antpos_issues(uv_in_paper, tmp_path):
    """
    test warnings and errors associated with antenna position issues in Miriad files

    Read in Miriad PAPER file, mess with various antpos issues and write out as
    a new Miriad file, read back in and check for appropriate behavior.
    """
    uv_in, uv_out, write_file = uv_in_paper

    uv_in_copy = uv_in.copy()
    uv_in_copy.antenna_positions = None
    uv_in_copy.write_miriad(write_file, clobber=True, run_check=False)
    with uvtest.check_warnings(
        UserWarning, "Antenna positions are not present in the file.", nwarnings=2
    ):
        uv_out.read(write_file, run_check=False, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in_copy.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in_copy.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in_copy)
    assert uv_in_copy == uv_out

    uv_in_copy = uv_in.copy()
    ants_with_data = list(set(uv_in_copy.ant_1_array).union(uv_in_copy.ant_2_array))
    ant_ind = np.where(uv_in_copy.antenna_numbers == ants_with_data[0])[0]
    uv_in_copy.antenna_positions[ant_ind, :] = [0, 0, 0]
    uv_in_copy.write_miriad(write_file, clobber=True, no_antnums=True)
    with uvtest.check_warnings(
        UserWarning,
        [
            "antenna number",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_out.read(write_file, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in_copy.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in_copy.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in_copy)
    assert uv_in_copy == uv_out

    uv_in.antenna_positions = None
    ants_with_data = sorted(set(uv_in.ant_1_array).union(uv_in.ant_2_array))
    new_nums = []
    new_names = []
    for a in ants_with_data:
        new_nums.append(a)
        ind = np.where(uv_in.antenna_numbers == a)[0][0]
        new_names.append(uv_in.antenna_names[ind])
    uv_in.antenna_numbers = np.array(new_nums)
    uv_in.antenna_names = new_names
    uv_in.Nants_telescope = len(uv_in.antenna_numbers)
    uv_in.write_miriad(write_file, clobber=True, no_antnums=True, run_check=False)
    with uvtest.check_warnings(
        UserWarning, "Antenna positions are not present in the file.", nwarnings=2
    ):
        uv_out.read(write_file, run_check=False, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uv"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(other=uv_in)
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_multi_files(casa_uvfits, tmp_path):
    """
    Reading multiple files at once.
    """
    uv_full = casa_uvfits
    testfile1 = os.path.join(tmp_path, "uv1")
    testfile2 = os.path.join(tmp_path, "uv2")
    # rename telescope to avoid name warning
    uv_full.unproject_phase()
    uv_full.conjugate_bls("ant1<ant2")

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_miriad(testfile1, clobber=True)
    uv2.write_miriad(testfile2, clobber=True)
    del uv1
    uv1 = UVData()
    uv1.read([testfile1, testfile2], file_type="miriad", use_future_array_shapes=True)

    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis using"
        " pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1", "uv2"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_full.filename
    uv1._filename.form = (1,)

    uv1._consolidate_phase_center_catalogs(
        reference_catalog=uv_full.phase_center_catalog
    )

    assert uv1 == uv_full

    # again, setting axis
    del uv1
    uv1 = UVData()
    uv1.read([testfile1, testfile2], axis="freq", use_future_array_shapes=True)
    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis using"
        " pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1", "uv2"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_full.filename
    uv1._filename.form = (1,)

    uv1._consolidate_phase_center_catalogs(
        reference_catalog=uv_full.phase_center_catalog
    )

    assert uv1 == uv_full


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_antpos_units(casa_uvfits, tmp_path):
    """
    Read uvfits, write miriad. Check written antpos are in ns.
    """
    uv = casa_uvfits
    testfile = os.path.join(tmp_path, "uv_antpos_units")
    uv.write_miriad(testfile, clobber=True)
    auv = aipy_extracts.UV(testfile)
    aantpos = auv["antpos"].reshape(3, -1).T * const.c.to("m/ns").value
    aantpos = aantpos[uv.antenna_numbers, :]
    aantpos = (
        uvutils.ECEF_from_rotECEF(aantpos, uv.telescope_location_lat_lon_alt[1])
        - uv.telescope_location
    )
    assert np.allclose(aantpos, uv.antenna_positions)


@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only,")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:It is not clear from the file if the data are")
def test_readmiriad_write_miriad_check_time_format(tmp_path):
    """
    test time_array is converted properly from Miriad format
    """
    # test read-in
    fname = os.path.join(DATA_PATH, "zen.2457698.40355.xx.HH.uvcA")
    uvd = UVData()
    uvd.read(fname, use_future_array_shapes=True)
    uvd_t = uvd.time_array.min()
    uvd_l = uvd.lst_array.min()
    uv = aipy_extracts.UV(fname)
    uv_t = uv["time"] + uv["inttime"] / (24 * 3600.0) / 2

    lat, lon, alt = uvd.telescope_location_lat_lon_alt
    t1 = Time(uv["time"], format="jd", location=(lon, lat))
    dt = TimeDelta(uv["inttime"] / 2, format="sec")
    t2 = t1 + dt
    lsts = uvutils.get_lst_for_time(np.array([t1.jd, t2.jd]), lat, lon, alt)
    delta_lst = lsts[1] - lsts[0]
    uv_l = uv["lst"] + delta_lst

    # assert starting time array and lst array are shifted by half integration
    assert np.isclose(uvd_t, uv_t)

    # avoid errors if IERS table is too old (if the iers url is down)
    tolerance = 1e-8
    assert np.allclose(uvd_l, uv_l, atol=tolerance)
    # test write-out
    fout = os.path.join(tmp_path, "ex_miriad")
    uvd.write_miriad(fout, clobber=True)
    # assert equal to original miriad time
    uv2 = aipy_extracts.UV(fout)
    assert np.isclose(uv["time"], uv2["time"])
    assert np.isclose(uv["lst"], uv2["lst"], atol=tolerance)


def test_file_with_bad_extra_words():
    """Test file with bad extra words is iterated and popped correctly."""
    fname = os.path.join(DATA_PATH, "test_miriad_changing_extra.uv")
    uv = UVData()
    warn_message = [
        "Altitude is not present in Miriad file, "
        "using known location values for PAPER.",
        "Mean of empty slice.",
        "invalid value encountered",
        "npols=4 but found 1 pols in data file",
        "Mean of empty slice.",
        "invalid value encountered",
        "antenna number 0 has visibilities associated with it, but it has a "
        "position of (0,0,0)",
        "antenna number 26 has visibilities associated with it, "
        "but it has a position of (0,0,0)",
    ]
    warn_category = (
        [UserWarning]
        + [RuntimeWarning] * 2
        + [UserWarning]
        + [RuntimeWarning] * 2
        + [UserWarning] * 2
    )
    # This is an old PAPER file, run_check must be set to false
    # The antenna positions is (0, 0, 0) vector
    with uvtest.check_warnings(warn_category, warn_message):
        uv.read_miriad(fname, run_check=False, use_future_array_shapes=True)


@pytest.mark.filterwarnings("ignore:The original `phase` method is deprecated")
def test_fix_phase(tmp_path):
    uv_in = UVData()
    uv_out = UVData()
    filepath = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
    writepath = os.path.join(tmp_path, "phasetest.miriad")
    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file,"
            " using known location values for PAPER.",
            "The uvw_array does not match the expected"
            " values given the antenna positions.",
        ],
    ):
        uv_in.read(filepath, use_future_array_shapes=True)

    # UVWs don't seem to line up w/ what we expect on read, so remedy this now.
    uv_in.set_uvws_from_antenna_positions()

    # Make some copies of the data
    uv_in_bad_ant = uv_in.copy()
    uv_in_bad_base = uv_in.copy()

    # These values could be anything -- we're just picking something that we know should
    # be visible from the telescope at the time of obs (ignoring horizon limits).
    phase_ra = uv_in.lst_array[0]
    phase_dec = uv_in.telescope_location_lat_lon_alt[0] * 0.5

    # Do the improved phasing on the data set.
    uv_in.phase(phase_ra, phase_dec, cat_name="foo")

    # First test the case where we are using the old phase method with the uvws
    # calculated from the antenna positions
    with uvtest.check_warnings(
        DeprecationWarning, match="The `use_old_proj` option is deprecated"
    ):
        uv_in_bad_ant.phase(
            phase_ra, phase_dec, use_old_proj=True, use_ant_pos=True, cat_name="foo"
        )
    uv_in_bad_ant.write_miriad(
        writepath, clobber=True, run_check=False, check_extra=False
    )
    with uvtest.check_warnings(UserWarning, "Fixing phases using antenna positions."):
        uv_out.read(
            writepath,
            fix_old_proj=True,
            fix_use_ant_pos=True,
            use_future_array_shapes=True,
        )

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["phasetest.miriad"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out

    # Now test and see what happens if we use the baseline-vector based version of the
    # phasing method.
    with uvtest.check_warnings(
        DeprecationWarning, match="The `use_old_proj` option is deprecated"
    ):
        uv_in_bad_base.phase(
            phase_ra, phase_dec, use_old_proj=True, use_ant_pos=False, cat_name="foo"
        )
    uv_in_bad_base.write_miriad(
        writepath, clobber=True, run_check=False, check_extra=False
    )
    with uvtest.check_warnings(
        UserWarning, "Attempting to fix residual phasing errors from the old `phase`"
    ):
        uv_out.read(
            writepath,
            fix_old_proj=True,
            fix_use_ant_pos=False,
            use_future_array_shapes=True,
        )

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )

    # We have to handle this case a little carefully, because since the old
    # unphase_to_drift was _mostly_ accurate, although it does seem to intoduce errors
    # on the order of a part in 1e5, which translates to about a tenth of a degree phase
    # error in the test data set used here. Check that first, make sure it's good
    assert np.allclose(uv_in.data_array, uv_out.data_array, atol=2e-3)
    assert np.allclose(uv_in.data_array, uv_out.data_array, rtol=2e-1)

    # TODO: figure out why the old tolerance doesn't work and why uvws don't match
    # better
    # assert np.allclose(uv_in.data_array, uv_out.data_array, rtol=3e-4)
    uv_out.uvw_array = uv_in.uvw_array

    # Once we know the data are okay, copy over data array and check for equality btw
    # the other attributes of the two objects.
    uv_out.data_array = uv_in.data_array
    assert uv_in == uv_out


# pyuvdata/uvdata/tests/test_ms.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for MS object.

"""
import os
import shutil

import numpy as np
import pytest
from astropy.time import Time

import pyuvdata.tests as uvtest
from pyuvdata import UVData
from pyuvdata.data import DATA_PATH
from pyuvdata.uvdata.ms import MS
from pyuvdata.uvdata.uvdata import _future_array_shapes_warning

pytest.importorskip("casacore")

allowed_failures = "filename"


def is_within_directory(directory, target):
    abs_directory = os.path.abspath(directory)
    abs_target = os.path.abspath(target)

    prefix = os.path.commonprefix([abs_directory, abs_target])

    return prefix == abs_directory


def check_members(tar, path):
    for member in tar.getmembers():
        member_path = os.path.join(path, member.name)
        if not is_within_directory(path, member_path):
            raise Exception("Attempted Path Traversal in Tar File")

    return tar.getmembers()


def safe_extract(tar, path=".", members=None, *, numeric_owner=False):
    # this is factored this way (splitting out the `check_members` function)
    # to appease bandit.
    tar.extractall(path, members=check_members(tar, path), numeric_owner=numeric_owner)


@pytest.fixture(scope="session")
def nrao_uv_main():
    uvobj = UVData()
    testfile = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.ms")
    uvobj.read(testfile, use_future_array_shapes=True)

    yield uvobj

    del uvobj


@pytest.fixture(scope="function")
def nrao_uv(nrao_uv_main):
    """Make function level NRAO ms object."""
    nrao_ms = nrao_uv_main.copy()
    yield nrao_ms

    # clean up when done
    del nrao_ms

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:ITRF coordinate frame detected,")
@pytest.mark.filterwarnings("ignore:UVW orientation appears to be flipped,")
@pytest.mark.filterwarnings("ignore:telescope_location are not set")
def test_cotter_ms():
    """Test reading in an ms made from MWA data with cotter (no dysco compression)"""
    uvobj = UVData()
    testfile = os.path.join(DATA_PATH, "1102865728_small.ms/")
    uvobj.read(testfile)

    # check that a select on read works
    uvobj2 = UVData()

    with uvtest.check_warnings(
        [UserWarning] * 3 + [DeprecationWarning],
        match=[
            "Warning: select on read keyword set",
            "telescope_location are not set or being overwritten. Using known values "
            "for MWA.",
            "UVW orientation appears to be flipped,",
            _future_array_shapes_warning,
        ],
    ):
        uvobj2.read(testfile, freq_chans=np.arange(2))
    uvobj.select(freq_chans=np.arange(2))
    assert uvobj == uvobj2
    del uvobj


@pytest.mark.filterwarnings("ignore:ITRF coordinate frame detected,")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_nrao_loopback(tmp_path, nrao_uv):
    """Test reading in a CASA tutorial ms file and looping it through write_ms."""
    uvobj = nrao_uv
    expected_extra_keywords = ["DATA_COL", "observer"]

    assert sorted(expected_extra_keywords) == sorted(uvobj.extra_keywords.keys())

    testfile = os.path.join(tmp_path, "ms_testfile.ms")

    with uvtest.check_warnings(
        UserWarning,
        match=[
            "Writing in the MS file that the units of the data are",
            "The uvw_array does not match the expected values",
        ],
    ):
        uvobj.write_ms(testfile)

    uvobj2 = UVData()
    uvobj2.read_ms(testfile, use_future_array_shapes=True)

    # also update filenames
    assert uvobj.filename == ["day2_TDEM0003_10s_norx_1src_1spw.ms"]
    assert uvobj2.filename == ["ms_testfile.ms"]
    uvobj.filename = uvobj2.filename

    # Test that the scan numbers are equal
    assert (uvobj.scan_number_array == uvobj2.scan_number_array).all()

    assert uvobj == uvobj2

    # test that the clobber keyword works by rewriting
    with uvtest.check_warnings(
        UserWarning,
        match=[
            "Writing in the MS file that the units of the data are",
            "The uvw_array does not match the expected values",
        ],
    ):
        uvobj.write_ms(testfile, clobber=True)


@pytest.mark.filterwarnings("ignore:ITRF coordinate frame detected,")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_lwa(tmp_path):
    """Test reading in an LWA ms file."""
    uvobj = UVData()
    testfile = os.path.join(DATA_PATH, "lwasv_cor_58342_05_00_14.ms.tar.gz")
    expected_extra_keywords = ["DATA_COL", "observer"]

    import tarfile

    with tarfile.open(testfile) as tf:
        new_filename = os.path.join(tmp_path, tf.getnames()[0])

        safe_extract(tf, path=tmp_path)

    uvobj.read(new_filename, file_type="ms", use_future_array_shapes=True)
    assert sorted(expected_extra_keywords) == sorted(uvobj.extra_keywords.keys())

    assert uvobj.history == uvobj.pyuvdata_version_str

    # delete the untarred folder
    shutil.rmtree(new_filename)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:telescope_location are not set")
def test_no_spw():
    """Test reading in a PAPER ms converted by CASA from a uvfits with no spw axis."""
    uvobj = UVData()
    testfile_no_spw = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAAM.ms")
    uvobj.read(testfile_no_spw, use_future_array_shapes=True)
    del uvobj


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_spwsupported():
    """Test reading in an ms file with multiple spws."""
    uvobj = UVData()
    testfile = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1scan.ms")
    uvobj.read(testfile, use_future_array_shapes=True)

    assert uvobj.Nspws == 2


@pytest.mark.filterwarnings("ignore:Coordinate reference frame not detected,")
@pytest.mark.filterwarnings("ignore:UVW orientation appears to be flipped,")
@pytest.mark.filterwarnings("ignore:telescope_location are not set")
def test_extra_pol_setup(tmp_path):
    """Test reading in an ms file with extra polarization setups (not used in data)."""
    uvobj = UVData()
    testfile = os.path.join(
        DATA_PATH, "X5707_1spw_1scan_10chan_1time_1bl_noatm.ms.tar.gz"
    )

    import tarfile

    with tarfile.open(testfile) as tf:
        new_filename = os.path.join(tmp_path, tf.getnames()[0])

        safe_extract(tf, path=tmp_path)

    uvobj.read(new_filename, file_type="ms", use_future_array_shapes=True)

    # delete the untarred folder
    shutil.rmtree(new_filename)


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:The older phase attributes")
def test_read_ms_read_uvfits(nrao_uv, casa_uvfits):
    """
    Test that a uvdata object instantiated from an ms file created with CASA's
    importuvfits is equal to a uvdata object instantiated from the original
    uvfits file (tests equivalence with importuvfits in uvdata up to issues around the
    direction of the uvw array).
    Since the history is missing from the ms file, this test sets both uvdata
    histories to identical empty strings before comparing them.
    """
    ms_uv = nrao_uv.copy()
    uvfits_uv = casa_uvfits.copy()
    # set histories to identical blank strings since we do not expect
    # them to be the same anyways.
    ms_uv.history = ""
    uvfits_uv.history = ""

    # the objects won't be equal because uvfits adds some optional parameters
    # and the ms sets default antenna diameters even though the uvfits file
    # doesn't have them
    assert uvfits_uv != ms_uv
    uvfits_uv.integration_time = ms_uv.integration_time

    # The uvfits was written by CASA, which adds one to all the antenna numbers relative
    # to the measurement set. Adjust those:
    uvfits_uv.antenna_numbers = uvfits_uv.antenna_numbers - 1
    uvfits_uv.ant_1_array = uvfits_uv.ant_1_array - 1
    uvfits_uv.ant_2_array = uvfits_uv.ant_2_array - 1
    uvfits_uv.baseline_array = uvfits_uv.antnums_to_baseline(
        uvfits_uv.ant_1_array, uvfits_uv.ant_2_array
    )

    # also need to adjust phase_center_catalogs
    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=ms_uv.phase_center_catalog
    )

    # they are equal if only required parameters are checked:
    # scan numbers only defined for the MS
    assert uvfits_uv.__eq__(ms_uv, check_extra=False, allowed_failures=allowed_failures)

    # set those parameters to none to check that the rest of the objects match
    ms_uv.antenna_diameters = None
    uvfits_required_extra = ["dut1", "earth_omega", "gst0", "rdate", "timesys"]
    for p in uvfits_uv.extra():
        fits_param = getattr(uvfits_uv, p)
        ms_param = getattr(ms_uv, p)
        if fits_param.name in uvfits_required_extra and ms_param.value is None:
            fits_param.value = None
            setattr(uvfits_uv, p, fits_param)

    # extra keywords are also different, set both to empty dicts
    uvfits_uv.extra_keywords = {}
    ms_uv.extra_keywords = {}

    # also update filenames
    assert uvfits_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert ms_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.ms"]
    uvfits_uv.filename = ms_uv.filename

    # propagate scan numbers to the uvfits, ONLY for comparison
    uvfits_uv.scan_number_array = ms_uv.scan_number_array

    assert uvfits_uv == ms_uv


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_ms_write_uvfits(nrao_uv, tmp_path):
    """
    read ms, write uvfits test.
    Read in ms file, write out as uvfits, read back in and check for
    object equality.
    """
    ms_uv = nrao_uv
    uvfits_uv = UVData()
    testfile = os.path.join(tmp_path, "outtest.uvfits")
    ms_uv.write_uvfits(testfile)
    uvfits_uv.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uvfits_uv.filename == ["outtest.uvfits"]
    assert ms_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.ms"]
    uvfits_uv.filename = ms_uv.filename

    # propagate scan numbers to the uvfits, ONLY for comparison
    uvfits_uv.scan_number_array = ms_uv.scan_number_array

    for item in ["dut1", "earth_omega", "gst0", "rdate", "timesys"]:
        # Check to make sure that the UVFITS-specific paramters are set on the
        # UVFITS-based obj, and not on our original object. Then set it to None for the
        # UVFITS-based obj.
        assert getattr(ms_uv, item) is None
        assert getattr(uvfits_uv, item) is not None
        setattr(uvfits_uv, item, None)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=ms_uv.phase_center_catalog
    )
    assert uvfits_uv == ms_uv


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_ms_write_miriad(nrao_uv, tmp_path):
    """
    read ms, write miriad test.
    Read in ms file, write out as miriad, read back in and check for
    object equality.
    """
    pytest.importorskip("pyuvdata._miriad")
    ms_uv = nrao_uv
    miriad_uv = UVData()
    testfile = os.path.join(tmp_path, "outtest_miriad")
    ms_uv.write_miriad(testfile)
    miriad_uv.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert miriad_uv.filename == ["outtest_miriad"]
    assert ms_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.ms"]
    miriad_uv.filename = ms_uv.filename

    # propagate scan numbers to the miriad uvdata, ONLY for comparison
    miriad_uv.scan_number_array = ms_uv.scan_number_array

    assert miriad_uv == ms_uv


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:The older phase attributes")
@pytest.mark.parametrize("axis", [None, "freq"])
def test_multi_files(casa_uvfits, axis):
    """
    Reading multiple files at once.
    """
    uv_full = casa_uvfits.copy()

    # Ensure the scan numbers are defined for the comparison
    uv_full._set_scan_numbers()

    uv_multi = UVData()
    testfile1 = os.path.join(DATA_PATH, "multi_1.ms")
    testfile2 = os.path.join(DATA_PATH, "multi_2.ms")

    # It seems that these two files were made using the CASA importuvfits task, but the
    # history tables are missing, so we can't infer that from the history. This means
    # that the uvws are not flipped/data is not conjugated as they should be. Fix that.

    filesread = [testfile1, testfile2]
    # test once as list and once as an array
    if axis is None:
        filesread = np.array(filesread)

    uv_multi.read(
        filesread, axis=axis, allow_rephase=False, use_future_array_shapes=True
    )

    # histories are different because of combining along freq. axis
    # replace the history
    uv_multi.history = uv_full.history

    # the objects won't be equal because uvfits adds some optional parameters
    # and the ms sets default antenna diameters even though the uvfits file
    # doesn't have them
    assert uv_multi != uv_full
    # The uvfits was written by CASA, which adds one to all the antenna numbers relative
    # to the measurement set. Adjust those:
    uv_full.antenna_numbers = uv_full.antenna_numbers - 1
    uv_full.ant_1_array = uv_full.ant_1_array - 1
    uv_full.ant_2_array = uv_full.ant_2_array - 1
    uv_full.baseline_array = uv_full.antnums_to_baseline(
        uv_full.ant_1_array, uv_full.ant_2_array
    )

    uv_full._consolidate_phase_center_catalogs(
        reference_catalog=uv_multi.phase_center_catalog
    )

    # now they are equal if only required parameters are checked:
    assert uv_multi.__eq__(uv_full, check_extra=False)

    # set those parameters to none to check that the rest of the objects match
    uv_multi.antenna_diameters = None

    uvfits_required_extra = ["dut1", "earth_omega", "gst0", "rdate", "timesys"]
    for p in uv_full.extra():
        fits_param = getattr(uv_full, p)
        ms_param = getattr(uv_multi, p)
        if fits_param.name in uvfits_required_extra and ms_param.value is None:
            fits_param.value = None
            setattr(uv_full, p, fits_param)

    # extra keywords are also different, set both to empty dicts
    uv_full.extra_keywords = {}
    uv_multi.extra_keywords = {}

    # make sure filenames are what we expect
    assert set(uv_multi.filename) == {"multi_1.ms", "multi_2.ms"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv_multi.filename = uv_full.filename
    uv_multi._filename.form = (1,)

    assert uv_multi.__eq__(uv_full, allowed_failures=allowed_failures)
    del uv_full
    del uv_multi


def test_bad_col_name():
    """
    Test error with invalid column name.
    """
    uvobj = UVData()
    testfile = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.ms")

    with pytest.raises(ValueError, match="Invalid data_column value supplied"):
        uvobj.read(testfile, data_column="FOO", use_future_array_shapes=True)


@pytest.mark.parametrize("check_warning", [True, False])
@pytest.mark.parametrize(
    "frame,errtype,msg",
    (
        ["JNAT", NotImplementedError, "Support for the JNAT frame is not yet"],
        ["AZEL", NotImplementedError, "Support for the AZEL frame is not yet"],
        ["GALACTIC", NotImplementedError, "Support for the GALACTIC frame is not yet"],
        ["ABC", ValueError, "The coordinate frame ABC is not one of the supported"],
        ["123", ValueError, "The coordinate frame 123 is not one of the supported"],
    ),
)
def test_parse_casa_frame_ref_errors(check_warning, frame, errtype, msg):
    """
    Test errors with matching CASA frames to astropy frame/epochs
    """
    uvobj = MS()
    if check_warning:
        with uvtest.check_warnings(UserWarning, match=msg):
            uvobj._parse_casa_frame_ref(frame, raise_error=False)
    else:
        with pytest.raises(errtype) as cm:
            uvobj._parse_casa_frame_ref(frame)
        assert str(cm.value).startswith(msg)


@pytest.mark.parametrize("check_warning", [True, False])
@pytest.mark.parametrize(
    "frame,epoch,msg",
    (
        ["fk5", 1991.1, "Frame fk5 (epoch 1991.1) does not have a corresponding match"],
        ["fk4", 1991.1, "Frame fk4 (epoch 1991.1) does not have a corresponding match"],
        ["icrs", 2021.0, "Frame icrs (epoch 2021) does not have a corresponding"],
    ),
)
def test_parse_pyuvdata_frame_ref_errors(check_warning, frame, epoch, msg):
    """
    Test errors with matching CASA frames to astropy frame/epochs
    """
    uvobj = MS()
    if check_warning:
        with uvtest.check_warnings(UserWarning, match=msg):
            uvobj._parse_pyuvdata_frame_ref(frame, epoch, raise_error=False)
    else:
        with pytest.raises(ValueError) as cm:
            uvobj._parse_pyuvdata_frame_ref(frame, epoch)
        assert str(cm.value).startswith(msg)


@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
def test_ms_history_lesson(sma_mir, tmp_path):
    """
    Test that the MS reader/writer can parse complex history
    """
    from casacore import tables

    ms_uv = UVData()
    testfile = os.path.join(tmp_path, "out_ms_history_lesson.ms")
    sma_mir.history = (
        "Line 1.\nBegin measurement set history\nAPP_PARAMS;CLI_COMMAND;"
        "APPLICATION;MESSAGE;OBJECT_ID;OBSERVATION_ID;ORIGIN;PRIORITY;TIME\n"
        "End measurement set history.\nLine 2.\n"
    )
    sma_mir.write_ms(testfile)

    tb_hist = tables.table(testfile + "/HISTORY", readonly=False, ack=False)
    tb_hist.addrows()
    for col in tb_hist.colnames():
        tb_hist.putcell(col, tb_hist.nrows() - 1, tb_hist.getcell(col, 0))
    tb_hist.putcell("ORIGIN", 1, "DUMMY")
    tb_hist.putcell("APPLICATION", 1, "DUMMY")
    tb_hist.putcell("TIME", 1, 0.0)
    tb_hist.putcell("MESSAGE", 2, "Line 3.")
    tb_hist.close()

    ms_uv.read(testfile, use_future_array_shapes=True)

    assert ms_uv.history.startswith(
        "Line 1.\nBegin measurement set history\nAPP_PARAMS;CLI_COMMAND;APPLICATION;"
        "MESSAGE;OBJECT_ID;OBSERVATION_ID;ORIGIN;PRIORITY;TIME\n;;DUMMY;Line 2.;0;-1;"
        "DUMMY;INFO;0.0\nEnd measurement set history.\nLine 3.\n  Read/written with "
        "pyuvdata version:"
    )

    tb_hist = tables.table(os.path.join(testfile, "HISTORY"), ack=False, readonly=False)
    tb_hist.rename(os.path.join(testfile, "FORGOTTEN"))
    tb_hist.close()

    ms_uv.read(testfile, use_future_array_shapes=True)
    assert ms_uv.history.startswith("  Read/written with pyuvdata version:")


@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
def test_ms_multi_spw_data_variation(sma_mir, tmp_path):
    """
    Test that the MS writer/reader appropriately reads in a single-source data set
    as non-multi-phase if it can be, even if the original data set was.
    """
    from casacore import tables

    ms_uv = UVData()
    testfile = os.path.join(tmp_path, "out_ms_multi_spw_data_variation.ms")

    sma_mir.write_ms(testfile)

    tb_main = tables.table(testfile, readonly=False, ack=False)
    tb_main.putcol("EXPOSURE", np.arange(sma_mir.Nblts * sma_mir.Nspws) + 1.0)
    tb_main.close()

    with pytest.raises(ValueError, match="Column EXPOSURE appears to vary on between"):
        ms_uv.read(testfile)

    with uvtest.check_warnings(
        UserWarning, match="Column EXPOSURE appears to vary on between windows, "
    ):
        ms_uv.read(testfile, raise_error=False, use_future_array_shapes=True)

    # Check that the values do indeed match the first entry in the catalog
    assert np.all(ms_uv.integration_time == np.array([1.0]))


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_ms_phasing(sma_mir, future_shapes, tmp_path):
    """
    Test that the MS writer can appropriately handle unphased data sets.
    """
    if not future_shapes:
        sma_mir.use_current_array_shapes()

    ms_uv = UVData()
    testfile = os.path.join(tmp_path, "out_ms_phasing.ms")

    sma_mir.unproject_phase()

    with pytest.raises(ValueError, match="The data are unprojected."):
        sma_mir.write_ms(testfile)

    sma_mir.write_ms(testfile, force_phase=True)

    ms_uv.read(testfile, use_future_array_shapes=True)

    assert np.allclose(ms_uv.phase_center_app_ra, ms_uv.lst_array)
    assert np.allclose(
        ms_uv.phase_center_app_dec, ms_uv.telescope_location_lat_lon_alt[0]
    )


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_ms_single_chan(sma_mir, future_shapes, tmp_path):
    """
    Make sure that single channel writing/reading work as expected
    """
    if not future_shapes:
        sma_mir.use_current_array_shapes()

    ms_uv = UVData()
    testfile = os.path.join(tmp_path, "out_ms_single_chan.ms")

    sma_mir.select(freq_chans=0)
    sma_mir.write_ms(testfile)
    sma_mir.set_lsts_from_time_array()
    sma_mir._set_app_coords_helper()

    with pytest.raises(ValueError, match="No valid data available in the MS file."):
        ms_uv.read(testfile)

    ms_uv.read(
        testfile, ignore_single_chan=False, use_future_array_shapes=future_shapes
    )

    # Easiest way to check that everything worked is to just check for equality, but
    # the MS file is single-spw, single-field, so we have a few things we need to fix

    cat_id = list(sma_mir.phase_center_catalog.keys())[0]
    cat_name = sma_mir.phase_center_catalog[cat_id]["cat_name"]
    ms_uv._update_phase_center_id(list(ms_uv.phase_center_catalog.keys())[0], cat_id)
    ms_uv.phase_center_catalog[cat_id]["cat_name"] = cat_name
    ms_uv.phase_center_catalog[cat_id]["info_source"] = "file"

    # Next, turn on flex-spw
    ms_uv._set_flex_spw()
    if not future_shapes:
        ms_uv.channel_width = np.array([ms_uv.channel_width])
    ms_uv.flex_spw_id_array = ms_uv.spw_array.copy()

    # Finally, take care of the odds and ends
    ms_uv.extra_keywords = {}
    ms_uv.history = sma_mir.history
    ms_uv.filename = sma_mir.filename
    ms_uv.instrument = sma_mir.instrument
    ms_uv.reorder_blts()

    # propagate scan numbers to the uvfits, ONLY for comparison
    sma_mir.scan_number_array = ms_uv.scan_number_array

    assert ms_uv == sma_mir


@pytest.mark.filterwarnings("ignore:pamatten in extra_keywords is a list, array")
@pytest.mark.filterwarnings("ignore:psys in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:psysattn in extra_keywords is a list, array or")
@pytest.mark.filterwarnings("ignore:ambpsys in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:bfmask in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:LST values stored in this file are not ")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
@pytest.mark.parametrize("multi_frame", [True, False])
def test_ms_scannumber_multiphasecenter(tmp_path, multi_frame):
    """
    Make sure that single channel writing/reading work as expected
    """
    carma_file = os.path.join(DATA_PATH, "carma_miriad")
    testfile = os.path.join(tmp_path, "carma_out.ms")

    miriad_uv = UVData()

    # Copied in from test_miriad.py::test_read_carma_miriad_write_ms
    with uvtest.check_warnings(
        UserWarning,
        [
            "Altitude is not present in Miriad file, "
            "using known location values for SZA.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "pamatten in extra_keywords is a list, array or dict",
            "psys in extra_keywords is a list, array or dict",
            "psysattn in extra_keywords is a list, array or dict",
            "ambpsys in extra_keywords is a list, array or dict",
            "bfmask in extra_keywords is a list, array or dict",
        ],
    ):
        miriad_uv.read(carma_file, use_future_array_shapes=True)

    # MIRIAD is missing these in the file, so we'll fill it in here.
    miriad_uv.antenna_diameters = np.zeros(miriad_uv.Nants_telescope)
    miriad_uv.antenna_diameters[:6] = 10.0
    miriad_uv.antenna_diameters[15:] = 3.5

    # We need to recalculate app coords here for one source ("NOISE"), which was
    # not actually correctly calculated in the online CARMA system (long story). Since
    # the MS format requires recalculating apparent coords after read in, we'll
    # calculate them here just to verify that everything matches.
    miriad_uv._set_app_coords_helper()

    if multi_frame:
        cat_id = miriad_uv._look_for_name("NOISE")
        ra_use = miriad_uv.phase_center_catalog[cat_id[0]]["cat_lon"][0]
        dec_use = miriad_uv.phase_center_catalog[cat_id[0]]["cat_lat"][0]
        with pytest.raises(
            ValueError,
            match="lon parameter must be a single value for cat_type sidereal",
        ):
            miriad_uv.phase(
                miriad_uv.phase_center_catalog[cat_id[0]]["cat_lon"],
                dec_use,
                cat_name="foo",
                phase_frame="icrs",
                select_mask=miriad_uv.phase_center_id_array == cat_id[0],
            )

        with pytest.raises(
            ValueError,
            match="lat parameter must be a single value for cat_type sidereal",
        ):
            miriad_uv.phase(
                ra_use,
                miriad_uv.phase_center_catalog[cat_id[0]]["cat_lat"],
                cat_name="foo",
                phase_frame="icrs",
                select_mask=miriad_uv.phase_center_id_array == cat_id[0],
            )

        with uvtest.check_warnings(
            UserWarning,
            match=[
                "The entry name NOISE is not unique",
                "The provided name NOISE is already used",
            ],
        ):
            miriad_uv.phase(
                ra_use,
                dec_use,
                cat_name="NOISE",
                phase_frame="icrs",
                select_mask=miriad_uv.phase_center_id_array == cat_id[0],
            )
    miriad_uv.write_ms(testfile)

    # Check on the scan number grouping based on consecutive integrations per phase
    # center

    # Read back in as MS. Should have 3 scan numbers defined.
    ms_uv = UVData()
    ms_uv.read(testfile, use_future_array_shapes=True)

    assert np.unique(ms_uv.scan_number_array).size == 3
    assert (np.unique(ms_uv.scan_number_array) == np.array([1, 2, 3])).all()

    # The scan numbers should match the phase center IDs, offset by 1
    # so that the scan numbers start with 1, not 0.
    assert ((miriad_uv.phase_center_id_array == (ms_uv.scan_number_array - 1))).all()


@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
def test_ms_extra_data_descrip(sma_mir, tmp_path):
    """
    Make sure that data sets can be read even if the main table doesn't have data
    for a particular listed spectral window in the DATA_DESCRIPTION table.
    """
    from casacore import tables

    ms_uv = UVData()
    testfile = os.path.join(tmp_path, "out_ms_extra_data_descrip.ms")

    sma_mir.write_ms(testfile)

    tb_dd = tables.table(
        os.path.join(testfile, "DATA_DESCRIPTION"), ack=False, readonly=False
    )
    tb_dd.addrows()
    for col in tb_dd.colnames():
        tb_dd.putcell(col, tb_dd.nrows() - 1, tb_dd.getcell(col, 0))
    tb_dd.close()

    ms_uv.read(testfile, ignore_single_chan=False, use_future_array_shapes=True)
    cat_id = list(sma_mir.phase_center_catalog.keys())[0]
    cat_name = sma_mir.phase_center_catalog[cat_id]["cat_name"]
    ms_uv._update_phase_center_id(list(ms_uv.phase_center_catalog.keys())[0], cat_id)
    ms_uv.phase_center_catalog[cat_id]["cat_name"] = cat_name
    ms_uv.phase_center_catalog[cat_id]["info_source"] = "file"

    # There are some minor differences between the values stored by MIR and that
    # calculated by UVData. Since MS format requires these to be calculated on the fly,
    # we calculate them here just to verify that everything is looking okay.
    sma_mir.set_lsts_from_time_array()
    sma_mir._set_app_coords_helper()

    # These reorderings just make sure that data from the two formats are lined up
    # correctly.
    sma_mir.reorder_freqs(spw_order="number")
    ms_uv.reorder_blts()

    # Fix the remaining differences between the two objects, all of which are expected
    sma_mir.instrument = sma_mir.telescope_name
    ms_uv.history = sma_mir.history
    sma_mir.extra_keywords = ms_uv.extra_keywords
    sma_mir.filename = ms_uv.filename = None

    # propagate scan numbers to the miriad uvdata, ONLY for comparison
    sma_mir.scan_number_array = ms_uv.scan_number_array

    # Finally, with all exceptions handled, check for equality.
    assert ms_uv == sma_mir


@pytest.mark.parametrize("onewin", [True, False])
@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
def test_ms_weights(sma_mir, tmp_path, onewin):
    """
    Test that the MS writer/reader appropriately handles data when the
    WEIGHT_SPECTRUM column is missing or bypassed.
    """
    from casacore import tables

    ms_uv = UVData()
    testfile = os.path.join(tmp_path, "out_ms_weights.ms")
    if onewin:
        sma_mir.select(freq_chans=np.arange(16384))

    sma_mir.nsample_array[0, :, :] = np.tile(
        np.arange(sma_mir.Nfreqs / sma_mir.Nspws), (sma_mir.Npols, sma_mir.Nspws)
    ).T
    sma_mir.write_ms(testfile)

    tb_main = tables.table(testfile, readonly=False, ack=False)
    tb_main.removecols("WEIGHT_SPECTRUM")
    tb_main.close()

    ms_uv.read(testfile, use_future_array_shapes=True)

    # Check that the values do indeed match expected (median) value
    assert np.all(ms_uv.nsample_array == np.median(sma_mir.nsample_array))

    ms_uv.read(testfile, read_weights=False, use_future_array_shapes=True)
    # Check that the values do indeed match expected (median) value
    assert np.all(ms_uv.nsample_array == 1.0)


@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
@pytest.mark.parametrize(
    "badcol,badval,errtype,msg",
    (
        [None, None, IOError, "Thisisnofile.ms not found"],
        ["DATA_DESC_ID", [1000] * 8, ValueError, "No valid data available in the MS"],
        ["ARRAY_ID", np.arange(8), ValueError, "This file appears to have multiple"],
        ["DATA_COL", None, ValueError, "Invalid data_column value supplied."],
        ["TEL_LOC", None, ValueError, "Telescope frame is not ITRF and telescope is"],
        [
            "timescale",
            None,
            ValueError,
            "This file has a timescale that is not supported by astropy.",
        ],
    ),
)
def test_ms_reader_errs(sma_mir, tmp_path, badcol, badval, errtype, msg):
    """
    Test whether the reader throws an appripropriate errors on read.
    """
    from casacore import tables

    ms_uv = UVData()
    testfile = os.path.join(tmp_path, "out_ms_reader_errs.ms")
    sma_mir.write_ms(testfile)

    data_col = "DATA"

    if badcol is None:
        testfile = "Thisisnofile.ms"
    elif badcol == "DATA_COL":
        data_col = badval
    elif badcol == "TEL_LOC":
        tb_obs = tables.table(
            os.path.join(testfile, "OBSERVATION"), ack=False, readonly=False
        )
        tb_obs.removecols("TELESCOPE_LOCATION")
        tb_obs.putcol("TELESCOPE_NAME", "ABC")
        tb_obs.close()
        tb_ant = tables.table(
            os.path.join(testfile, "ANTENNA"), ack=False, readonly=False
        )
        tb_ant.putcolkeyword("POSITION", "MEASINFO", {"type": "position", "Ref": "ABC"})
        tb_ant.close()
    elif badcol == "timescale":
        tb_main = tables.table(testfile, ack=False, readonly=False)
        tb_main.putcolkeyword("TIME", "MEASINFO", {"Ref": "GMST"})
    else:
        tb_main = tables.table(testfile, ack=False, readonly=False)
        tb_main.putcol(badcol, badval)
        tb_main.close()

    with pytest.raises(errtype, match=msg):
        ms_uv.read(testfile, data_column=data_col, file_type="ms")

    if badcol == "timescale":
        with uvtest.check_warnings(UserWarning, match=msg):
            ms_uv.read(
                testfile,
                data_column=data_col,
                file_type="ms",
                raise_error=False,
                use_future_array_shapes=True,
            )
        assert ms_uv._time_array == sma_mir._time_array


def test_antenna_diameter_handling(hera_uvh5, tmp_path):
    uv_obj = hera_uvh5

    uv_obj.antenna_diameters = np.asarray(uv_obj.antenna_diameters, dtype=">f4")

    test_file = os.path.join(tmp_path, "dish_diameter_out.ms")
    with uvtest.check_warnings(
        UserWarning, match="Writing in the MS file that the units of the data are"
    ):
        uv_obj.write_ms(test_file, force_phase=True)

    uv_obj2 = UVData.from_file(test_file, use_future_array_shapes=True)

    # MS write/read adds some stuff to history & extra keywords
    uv_obj2.history = uv_obj.history
    uv_obj2.extra_keywords = uv_obj.extra_keywords

    uv_obj2._consolidate_phase_center_catalogs(
        reference_catalog=uv_obj.phase_center_catalog
    )
    assert uv_obj2.__eq__(uv_obj, allowed_failures=allowed_failures)


def test_no_source(sma_mir, tmp_path):
    uv = UVData()
    uv2 = UVData()
    filename = os.path.join(tmp_path, "no_source.ms")

    sma_mir.write_ms(filename)

    uv.read(filename, use_future_array_shapes=True)

    shutil.rmtree(os.path.join(filename, "SOURCE"))
    uv2.read(filename, use_future_array_shapes=True)

    assert uv == uv2


@pytest.mark.filterwarnings("ignore:telescope_location are not set")
@pytest.mark.filterwarnings("ignore:UVW orientation appears to be flipped")
@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only")
def test_timescale_handling():
    ut1_file = os.path.join(DATA_PATH, "1090008640_birli_pyuvdata.ms")

    uvobj = UVData.from_file(ut1_file, use_future_array_shapes=True)
    assert (
        np.round(Time(uvobj.time_array[0], format="jd").gps, decimals=5) == 1090008642.0
    )


# pyuvdata/uvdata/tests/test_mwa_corr_fits.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2019 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for MWACorrFITS object."""

import itertools
import os

import h5py
import numpy as np
import pytest
from astropy.io import fits

import pyuvdata.tests as uvtest
from pyuvdata import UVData
from pyuvdata.data import DATA_PATH
from pyuvdata.uvdata.mwa_corr_fits import input_output_mapping
from pyuvdata.uvdata.uvdata import _future_array_shapes_warning

# set up MWA correlator file list
testdir = os.path.join(DATA_PATH, "mwa_corr_fits_testfiles/")

testfiles = [
    "1131733552.metafits",
    "1131733552_20151116182537_mini_gpubox01_00.fits",
    "1131733552_20151116182637_mini_gpubox06_01.fits",
    "1131733552_mini_01.mwaf",
    "1131733552_mini_06.mwaf",
    "1131733552_mod.metafits",
    "1131733552_mini_cotter.uvfits",
    "1131733552_metafits_ppds.fits",
    "1061315448_20130823175130_mini_gpubox07_01.fits",
    "1061315448.metafits",
    "1061315448_20130823175130_mini_vv_07_01.uvh5",
    "1320409688.metafits",
    "1320409688_20211108122750_mini_ch137_000.fits",
]
filelist = [os.path.join(testdir, filei) for filei in testfiles]


@pytest.fixture(scope="module")
def flag_file_init(tmp_path_factory):
    tmp_path = tmp_path_factory.mktemp("pyuvdata_corr_fits", numbered=True)
    spoof_file1 = str(tmp_path / "spoof_01_00.fits")
    spoof_file6 = str(tmp_path / "spoof_06_00.fits")
    # spoof box files of the appropriate size
    with fits.open(filelist[1]) as mini1:
        mini1[1].data = np.repeat(mini1[1].data, 8, axis=0)
        extra_dat = np.copy(mini1[1].data)
        for _ in range(2):
            mini1.append(fits.ImageHDU(extra_dat))
        mini1[2].header["MILLITIM"] = 500
        mini1[2].header["TIME"] = mini1[1].header["TIME"]
        mini1[3].header["MILLITIM"] = 0
        mini1[3].header["TIME"] = mini1[1].header["TIME"] + 1
        mini1.writeto(spoof_file1)

    with fits.open(filelist[2]) as mini6:
        mini6[1].data = np.repeat(mini6[1].data, 8, axis=0)
        extra_dat = np.copy(mini6[1].data)
        for _ in range(2):
            mini6.append(fits.ImageHDU(extra_dat))
        mini6[2].header["MILLITIM"] = 500
        mini6[2].header["TIME"] = mini6[1].header["TIME"]
        mini6[3].header["MILLITIM"] = 0
        mini6[3].header["TIME"] = mini6[1].header["TIME"] + 1
        mini6.writeto(spoof_file6)

    flag_testfiles = [spoof_file1, spoof_file6, filelist[0]]

    yield flag_testfiles


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
def test_read_mwa_write_uvfits(tmp_path):
    """
    MWA correlator fits to uvfits loopback test.

    Read in MWA correlator files, write out as uvfits, read back in and check
    for object equality.
    """
    mwa_uv = UVData()
    uvfits_uv = UVData()
    with uvtest.check_warnings(
        [UserWarning, DeprecationWarning],
        match=[
            "some coarse channel files were not submitted",
            _future_array_shapes_warning,
        ],
    ):
        mwa_uv.read(
            filelist[0:2], correct_cable_len=True, phase_to_pointing_center=True
        )

    testfile = str(tmp_path / "outtest_MWAcorr.uvfits")
    mwa_uv.write_uvfits(testfile)
    uvfits_uv.read_uvfits(testfile)

    # make sure filenames are what we expect
    assert set(mwa_uv.filename) == {
        "1131733552.metafits",
        "1131733552_20151116182537_mini_gpubox01_00.fits",
    }
    assert uvfits_uv.filename == ["outtest_MWAcorr.uvfits"]
    mwa_uv.filename = uvfits_uv.filename
    mwa_uv._filename.form = (1,)

    for item in ["dut1", "earth_omega", "gst0", "rdate", "timesys"]:
        # Check to make sure that the UVFITS-specific paramters are set on the
        # UVFITS-based obj, and not on our original object. Then set it to None for the
        # UVFITS-based obj.
        assert getattr(mwa_uv, item) is None
        assert getattr(uvfits_uv, item) is not None
        setattr(uvfits_uv, item, None)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=mwa_uv.phase_center_catalog
    )
    assert mwa_uv == uvfits_uv


@pytest.mark.filterwarnings("ignore:antnums_to_baseline")
@pytest.mark.filterwarnings("ignore:Found antenna numbers > 255 in this data")
def test_read_mwax_write_uvfits(tmp_path):
    """
    MWAX correlator fits to uvfits loopback test.

    Read in MWAX correlator files, write out as uvfits, read back in and check
    for object equality.
    """
    # spoof testfile to contain 2 times and 2 freqs
    spoof_file = str(tmp_path / "mwax_spoof_ch137_000.fits")
    with fits.open(filelist[12]) as mini1:
        mini1[1].data = np.repeat(mini1[1].data, 2, axis=1)
        mini1[2].data = np.repeat(mini1[2].data, 2, axis=1)
        extra_dat = np.copy(mini1[1].data)
        extra_samps = np.copy(mini1[2].data)
        mini1.append(fits.ImageHDU(extra_dat))
        mini1.append(fits.ImageHDU(extra_samps))
        mini1[3].header["TIME"] = 1636374472
        mini1[4].header["TIME"] = 1636374472
        mini1[3].header["MILLITIM"] = 0
        mini1[4].header["MILLITIM"] = 0
        mini1.writeto(spoof_file)
    mwax_uv = UVData()
    uvfits_uv = UVData()
    messages = [
        "Fixing auto-correlations to be be real-only, after some imaginary values",
        "some coarse channel files were not submitted",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        mwax_uv.read(
            [spoof_file, filelist[11]],
            correct_cable_len=True,
            phase_to_pointing_center=True,
            use_future_array_shapes=True,
        )
    testfile = str(tmp_path / "outtest_MWAXcorr.uvfits")
    mwax_uv.write_uvfits(testfile)
    uvfits_uv.read_uvfits(testfile, use_future_array_shapes=True)

    for item in ["dut1", "earth_omega", "gst0", "rdate", "timesys"]:
        # Check to make sure that the UVFITS-specific paramters are set on the
        # UVFITS-based obj, and not on our original object. Then set it to None for the
        # UVFITS-based obj.
        assert getattr(mwax_uv, item) is None
        assert getattr(uvfits_uv, item) is not None
        setattr(uvfits_uv, item, None)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=mwax_uv.phase_center_catalog, ignore_name=True
    )
    assert mwax_uv == uvfits_uv


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_mwax_metafits_keys(tmp_path):
    """Check that mwax keywords are removed from extra_keywords for legacy files"""
    meta_spoof_file = str(tmp_path / "spoof_1131733552.metafits")
    with fits.open(filelist[0]) as meta:
        meta[0].header["DELAYMOD"] = None
        meta.writeto(meta_spoof_file)
    uv = UVData()
    uv.read([meta_spoof_file, filelist[1]], use_future_array_shapes=True)

    assert "DELAYMOD" not in uv.extra_keywords.keys()


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_select_on_read():
    mwa_uv = UVData()
    mwa_uv2 = UVData()
    mwa_uv.read(filelist[0:2], correct_cable_len=True, use_future_array_shapes=True)
    unique_times = np.unique(mwa_uv.time_array)
    select_times = unique_times[
        np.where(
            (unique_times >= np.min(mwa_uv.time_array))
            & (unique_times <= np.mean(mwa_uv.time_array))
        )
    ]
    mwa_uv.select(times=select_times)
    with uvtest.check_warnings(
        UserWarning,
        [
            'Warning: select on read keyword set, but file_type is "mwa_corr_fits"',
            "some coarse channel files were not submitted",
        ],
    ):
        mwa_uv2.read(
            filelist[0:2],
            correct_cable_len=True,
            time_range=[np.min(mwa_uv.time_array), np.mean(mwa_uv.time_array)],
            use_future_array_shapes=True,
        )
    assert mwa_uv == mwa_uv2


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
def test_read_mwa_read_cotter():
    """
    Pyuvdata and cotter equality test.

    Read in MWA correlator files and the corresponding cotter file and check
    for data array equality.
    """
    mwa_uv = UVData()
    cotter_uv = UVData()
    # cotter data has cable correction and is unphased
    mwa_uv.read(
        filelist[0:2],
        correct_cable_len=True,
        remove_dig_gains=False,
        remove_coarse_band=False,
        remove_flagged_ants=False,
        use_future_array_shapes=True,
    )
    cotter_uv.read(filelist[6], use_future_array_shapes=True)
    # cotter doesn't record the auto xy polarizations
    # due to a possible bug in cotter, the auto yx polarizations are conjugated
    # fix these before testing data_array
    autos = np.isclose(mwa_uv.ant_1_array - mwa_uv.ant_2_array, 0.0)
    cotter_uv.data_array[autos, :, 2] = cotter_uv.data_array[autos, :, 3]
    cotter_uv.data_array[autos, :, 3] = np.conj(cotter_uv.data_array[autos, :, 3])
    assert np.allclose(mwa_uv.data_array, cotter_uv.data_array, atol=1e-4, rtol=0)
    assert mwa_uv.freq_array == cotter_uv.freq_array


def test_read_mwa_write_uvfits_meta_mod(tmp_path):
    """
    MWA correlator fits to uvfits loopback test with a modified metafits file.

    Read in MWA correlator files, write out as uvfits, read back in and check
    for object equality.
    """
    # The metafits file has been modified to contain some coarse channels < 129,
    # and to have an uncorrected cable length.
    mwa_uv = UVData()
    uvfits_uv = UVData()
    messages = ["some coarse channel files were not submitted"]
    files = [filelist[1], filelist[5]]
    with uvtest.check_warnings(UserWarning, messages):
        mwa_uv.read(
            files,
            correct_cable_len=True,
            phase_to_pointing_center=True,
            use_future_array_shapes=True,
        )
    testfile = str(tmp_path / "outtest_MWAcorr.uvfits")
    mwa_uv.write_uvfits(testfile)
    uvfits_uv.read_uvfits(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(mwa_uv.filename) == {
        "1131733552_20151116182537_mini_gpubox01_00.fits",
        "1131733552_mod.metafits",
    }
    assert uvfits_uv.filename == ["outtest_MWAcorr.uvfits"]
    mwa_uv.filename = uvfits_uv.filename
    mwa_uv._filename.form = (1,)

    for item in ["dut1", "earth_omega", "gst0", "rdate", "timesys"]:
        # Check to make sure that the UVFITS-specific paramters are set on the
        # UVFITS-based obj, and not on our original object. Then set it to None for the
        # UVFITS-based obj.
        assert getattr(mwa_uv, item) is None
        assert getattr(uvfits_uv, item) is not None
        setattr(uvfits_uv, item, None)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=mwa_uv.phase_center_catalog
    )
    assert mwa_uv == uvfits_uv


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:Combined frequencies are separated by more than")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_read_mwa_multi():
    """Test reading in two sets of files."""
    set1 = filelist[0:2]
    set2 = [filelist[0], filelist[2]]
    mwa_uv = UVData()

    mwa_uv.read([set1, set2], use_future_array_shapes=True)

    mwa_uv2 = UVData()
    messages = [
        "some coarse channel files were not submitted",
        "cable length correction is now defaulted to True",
        "some coarse channel files were not submitted",
        "cable length correction is now defaulted to True",
        "Combined frequencies are separated by more than their channel width",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        mwa_uv2.read(
            [set1, set2], file_type="mwa_corr_fits", use_future_array_shapes=True
        )

    assert mwa_uv == mwa_uv2


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:Combined frequencies are separated by more than")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_read_mwa_multi_concat(tmp_path):
    """Test reading in two sets of files with fast concatenation."""
    # modify file so that time arrays are matching
    mod_mini_6 = str(tmp_path / "mini_gpubox06_01.fits")
    with fits.open(filelist[2]) as mini6:
        mini6[1].header["time"] = 1447698337
        mini6.writeto(mod_mini_6)
    set1 = filelist[0:2]
    set2 = [filelist[0], mod_mini_6]
    mwa_uv = UVData()
    mwa_uv.read([set1, set2], axis="freq", use_future_array_shapes=True)

    mwa_uv2 = UVData()
    messages = [
        "some coarse channel files were not submitted",
        "cable length correction is now defaulted to True",
        "some coarse channel files were not submitted",
        "cable length correction is now defaulted to True",
        "Combined frequencies are separated by more than their channel width",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        mwa_uv2.read(
            [set1, set2],
            axis="freq",
            file_type="mwa_corr_fits",
            use_future_array_shapes=True,
        )

    assert mwa_uv == mwa_uv2
    os.remove(mod_mini_6)


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
def test_read_mwa_flags():
    """Test handling of flag files."""
    mwa_uv = UVData()
    subfiles = [filelist[0], filelist[1], filelist[3], filelist[4]]
    messages = [
        "mwaf files submitted with use_aoflagger_flags=False",
        "some coarse channel files were not submitted",
        "cable length correction is now defaulted to True",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        mwa_uv.read(subfiles, use_aoflagger_flags=False, use_future_array_shapes=True)

    del mwa_uv

    mwa_uv = UVData()
    with pytest.raises(ValueError, match="no flag files submitted"):
        mwa_uv.read(
            subfiles[0:2], use_aoflagger_flags=True, use_future_array_shapes=True
        )
    del mwa_uv


def test_multiple_coarse():
    """
    Test two coarse channel files.

    Read in MWA correlator files with two different orderings of the files
    and check for object equality.
    """
    order1 = filelist[0:3]
    order2 = [filelist[0], filelist[2], filelist[1]]
    mwa_uv1 = UVData()
    mwa_uv2 = UVData()
    messages = [
        "coarse channels are not contiguous for this observation",
        "some coarse channel files were not submitted",
        "cable length correction is now defaulted to True",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        mwa_uv1.read(order1, use_future_array_shapes=True)
    with uvtest.check_warnings(UserWarning, messages):
        mwa_uv2.read(order2, use_future_array_shapes=True)

    assert mwa_uv1 == mwa_uv2


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_ppds(tmp_path):
    """Test handling of ppds files"""
    # turnaround test with just ppds file given
    mwa_uv = UVData()
    mwa_uv.read(
        [filelist[1], filelist[7]],
        phase_to_pointing_center=True,
        flag_init=False,
        use_future_array_shapes=True,
    )
    testfile = str(tmp_path / "outtest_MWAcorr.uvfits")
    mwa_uv.write_uvfits(testfile)
    uvfits_uv = UVData()
    uvfits_uv.read_uvfits(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert set(mwa_uv.filename) == {
        "1131733552_20151116182537_mini_gpubox01_00.fits",
        "1131733552_metafits_ppds.fits",
    }
    assert uvfits_uv.filename == ["outtest_MWAcorr.uvfits"]
    mwa_uv.filename = uvfits_uv.filename
    mwa_uv._filename.form = (1,)

    for item in ["dut1", "earth_omega", "gst0", "rdate", "timesys"]:
        # Check to make sure that the UVFITS-specific paramters are set on the
        # UVFITS-based obj, and not on our original object. Then set it to None for the
        # UVFITS-based obj.
        assert getattr(mwa_uv, item) is None
        assert getattr(uvfits_uv, item) is not None
        setattr(uvfits_uv, item, None)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=mwa_uv.phase_center_catalog
    )
    assert mwa_uv == uvfits_uv

    del mwa_uv
    del uvfits_uv

    # check that extra keywords are added when both ppds file and metafits file given
    mwa_uv = UVData()
    mwa_uv.read([filelist[0], filelist[1], filelist[7]], use_future_array_shapes=True)
    assert "MWAVER" in mwa_uv.extra_keywords and "MWADATE" in mwa_uv.extra_keywords


def test_fine_channels(tmp_path):
    """
    Break read_mwa_corr_fits by submitting files with different fine channels.

    Test that error is raised if files with different numbers of fine channels
    are submitted.
    """
    mwa_uv = UVData()
    bad_fine = str(tmp_path / "bad_gpubox06_01.fits")
    with fits.open(filelist[2]) as mini6:
        mini6[1].data = np.concatenate((mini6[1].data, mini6[1].data))
        mini6.writeto(bad_fine)
    with pytest.raises(ValueError, match="files submitted have different numbers"):
        mwa_uv.read([bad_fine, filelist[1]])
    del mwa_uv


def test_fine_channels_mwax(tmp_path):
    """
    Break read_mwa_corr_fits by submitting mwax files with different fine channels.

    Test that error is raised if files with different numbers of fine channels
    are submitted.
    """
    mwax_uv = UVData()
    bad_fine = str(tmp_path / "bad_ch137_000.fits.fits")
    with fits.open(filelist[12]) as mini:
        mini[1].data = np.repeat(mini[1].data, 2, axis=1)
        mini[2].data = np.repeat(mini[2].data, 2, axis=1)
        mini.writeto(bad_fine)
    with pytest.raises(ValueError, match="files submitted have different numbers"):
        mwax_uv.read([bad_fine, filelist[12]])
    del mwax_uv


@pytest.mark.parametrize(
    "files,err_msg",
    [
        ([filelist[0]], "no data files submitted"),
        ([filelist[1]], "no metafits file submitted"),
        (
            [filelist[0], filelist[1], filelist[5]],
            "multiple metafits files in filelist",
        ),
    ],
)
def test_break_read_mwacorrfits(files, err_msg):
    """Break read_mwa_corr_fits by submitting files incorrectly."""
    mwa_uv = UVData()
    with pytest.raises(ValueError, match=err_msg):
        mwa_uv.read(files)
    del mwa_uv


def test_file_extension(tmp_path):
    """
    Break read_mwa_corr_fits by submitting file with the wrong extension.

    Test that error is raised if a file with an extension that is not fits,
    metafits, or mwaf is submitted.
    """
    mwa_uv = UVData()
    bad_ext = str(tmp_path / "1131733552.meta")
    with fits.open(filelist[0]) as meta:
        meta.writeto(bad_ext)
    with pytest.raises(
        ValueError, match="only fits, metafits, and mwaf files supported"
    ):
        mwa_uv.read(bad_ext, file_type="mwa_corr_fits")
    del mwa_uv


def test_diff_obs(tmp_path):
    """
    Break read_mwa_corr_fits by submitting files from different observations.

    Test that error is raised if files from different observations are
    submitted in the same file list.
    """
    mwa_uv = UVData()
    bad_obs = str(tmp_path / "bad2_gpubox06_01.fits")
    with fits.open(filelist[2]) as mini6:
        mini6[0].header["OBSID"] = "1131733555"
        mini6.writeto(bad_obs)
    with pytest.raises(ValueError, match="files from different observations"):
        mwa_uv.read([bad_obs, filelist[0], filelist[1]])
    del mwa_uv


def test_misaligned_times(tmp_path):
    """
    Break read_mwa_corr_fits by submitting files with misaligned times.

    Test that error is raised if file start times are different by an amount
    that is not an integer multiiple of the integration time.
    """
    mwa_uv = UVData()
    bad_obs = str(tmp_path / "bad3_gpubox06_01.fits")
    with fits.open(filelist[2]) as mini6:
        mini6[1].header["MILLITIM"] = 250
        mini6.writeto(bad_obs)
    with pytest.raises(ValueError, match="coarse channel start times are misaligned"):
        mwa_uv.read([bad_obs, filelist[0], filelist[1]])
    del mwa_uv


@pytest.mark.filterwarnings(
    "ignore:coarse channels are not contiguous for this observation"
)
@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_flag_nsample_basic():
    """
    Test that the flag(without flag_int) and nsample arrays correctly reflect data.
    """
    uv = UVData()
    uv.read(
        filelist[0:3],
        flag_init=False,
        propagate_coarse_flags=False,
        remove_flagged_ants=False,
        use_future_array_shapes=True,
    )
    # check that only bad antennas are flagged for all times, freqs, pols
    bad_ants = [59, 114]
    good_ants = np.delete(np.unique(uv.ant_1_array), bad_ants)
    bad = uv.select(antenna_nums=np.unique(uv.ant_1_array)[bad_ants], inplace=False)
    good = uv.select(antenna_nums=good_ants, inplace=False)
    assert np.all(bad.flag_array)
    good.flag_array = good.flag_array.reshape(
        (good.Ntimes, good.Nbls, good.Nfreqs, good.Npols)
    )
    # good ants should be flagged except for the first time and second freq,
    # and for the second time and first freq
    assert np.all(good.flag_array[1:-1, :, :, :])
    assert np.all(good.flag_array[0, :, 1, :] == 0)
    assert np.all(good.flag_array[-1, :, 0, :] == 0)
    assert np.all(good.flag_array[0, :, 0, :])
    assert np.all(good.flag_array[-1, :, 1, :])
    # check that nsample array is filled properly
    uv.nsample_array = uv.nsample_array.reshape(
        (uv.Ntimes, uv.Nbls, uv.Nfreqs, uv.Npols)
    )
    assert np.all(uv.nsample_array[1:-1, :, :, :] == 0.0)
    assert np.all(uv.nsample_array[0, :, 1, :] == 1.0)
    assert np.all(uv.nsample_array[-1, :, 0, :] == 1.0)
    assert np.all(uv.nsample_array[0, :, 0, :] == 0.0)
    assert np.all(uv.nsample_array[-1, :, 1, :] == 0.0)


@pytest.mark.filterwarnings(
    "ignore:coarse channels are not contiguous for this observation"
)
@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_flag_init(flag_file_init):
    """
    Test that routine MWA flagging works as intended.
    """
    uv = UVData()
    uv.read(
        flag_file_init,
        flag_init=True,
        start_flag=0,
        end_flag=0,
        propagate_coarse_flags=False,
        use_future_array_shapes=True,
    )

    freq_inds = [0, 1, 4, 6, 7, 8, 9, 12, 14, 15]
    freq_inds_complement = [ind for ind in range(16) if ind not in freq_inds]

    assert np.all(
        uv.flag_array[:, freq_inds, :]
    ), "Not all of edge and center channels are flagged!"
    assert not np.any(
        np.all(uv.flag_array[:, freq_inds_complement, :], axis=(0, 1, -1))
    ), "Some non-edge/center channels are entirely flagged!"


@pytest.mark.filterwarnings(
    "ignore:coarse channels are not contiguous for this observation"
)
@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_flag_start_flag(flag_file_init):
    uv = UVData()
    uv.read(
        flag_file_init,
        flag_init=True,
        start_flag=1.0,
        end_flag=1.0,
        edge_width=0,
        flag_dc_offset=False,
        propagate_coarse_flags=False,
        use_future_array_shapes=True,
    )

    reshape = [uv.Ntimes, uv.Nbls, uv.Nfreqs, uv.Npols]
    time_inds = [0, 1, -1, -2]
    assert np.all(
        uv.flag_array.reshape(reshape)[time_inds, :, :, :]
    ), "Not all of start and end times are flagged."
    # Check that it didn't just flag everything
    # Should have unflagged data for time inds [2, -3]
    assert not np.any(
        np.all(uv.flag_array.reshape(reshape)[[2, -3], :, :, :], axis=(1, 2, 3))
    ), "All the data is flagged for some intermediate times!"


@pytest.mark.parametrize(
    "err_type,read_kwargs,err_msg",
    [
        (
            ValueError,
            {"flag_init": True, "start_flag": 0, "end_flag": 0, "edge_width": 90e3},
            "The edge_width must be an integer multiple of the channel_width",
        ),
        (
            ValueError,
            {"flag_init": True, "start_flag": 1.2, "end_flag": 0},
            "The start_flag must be an integer multiple of the integration_time",
        ),
        (
            ValueError,
            {"flag_init": True, "start_flag": 0, "end_flag": 1.2},
            "The end_flag must be an integer multiple of the integration_time",
        ),
    ],
)
@pytest.mark.filterwarnings(
    "ignore:coarse channels are not contiguous for this observation"
)
@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_flag_init_errors(flag_file_init, err_type, read_kwargs, err_msg):
    uv = UVData()
    # give noninteger multiple inputs
    with pytest.raises(err_type, match=err_msg):
        uv.read(flag_file_init, **read_kwargs)


def test_read_metadata_only(tmp_path):
    """Test reading an MWA corr fits file as metadata only."""
    uvd = UVData()
    messages = ["some coarse channel files were not submitted"]
    with uvtest.check_warnings(UserWarning, messages):
        uvd.read(
            filelist[0:2],
            correct_cable_len=True,
            phase_to_pointing_center=True,
            read_data=False,
            use_future_array_shapes=True,
        )

    assert uvd.metadata_only


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_data_array_precision():
    uv = UVData()
    uv2 = UVData()
    # read in data array as single precision
    uv.read(filelist[0:2], data_array_dtype=np.complex64, use_future_array_shapes=True)
    # now read as double precision
    uv2.read(
        filelist[0:2], data_array_dtype=np.complex128, use_future_array_shapes=True
    )

    assert uv == uv2
    assert uv.data_array.dtype.type is np.complex64
    assert uv2.data_array.dtype.type is np.complex128

    return


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_nsample_array_precision():
    uv = UVData()
    uv2 = UVData()
    uv3 = UVData()
    # read in nsample array at different precisions
    uv.read(filelist[0:2], nsample_array_dtype=np.float32, use_future_array_shapes=True)
    uv2.read(
        filelist[0:2], nsample_array_dtype=np.float64, use_future_array_shapes=True
    )
    uv3.read(
        filelist[0:2], nsample_array_dtype=np.float16, use_future_array_shapes=True
    )

    assert uv == uv2
    assert uv == uv3
    assert uv.nsample_array.dtype.type is np.float32
    assert uv2.nsample_array.dtype.type is np.float64
    assert uv3.nsample_array.dtype.type is np.float16

    return


def test_invalid_precision_errors():
    uv = UVData()

    # raise errors by passing bogus precision values
    with pytest.raises(ValueError, match="data_array_dtype must be np.complex64"):
        uv.read(filelist[0:2], data_array_dtype=np.float64)

    with pytest.raises(
        ValueError, match="nsample_array_dtype must be one of: np.float64"
    ):
        uv.read(filelist[0:2], nsample_array_dtype=np.complex128)

    return


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_remove_dig_gains():
    """Test digital gain removal."""
    uv1 = UVData()
    uv1.read(filelist[0:2], data_array_dtype=np.complex64, use_future_array_shapes=True)

    uv2 = UVData()
    uv2.read(filelist[0:2], remove_dig_gains=False, use_future_array_shapes=True)

    with fits.open(filelist[0]) as meta:
        meta_tbl = meta[1].data
        antenna_inds = meta_tbl["Antenna"][1::2]
        dig_gains = meta_tbl["Gains"][1::2, :].astype(np.float64) / 64
    reordered_inds = antenna_inds.argsort()
    bad_ant_inds = [59, 114]
    good_ants = np.delete(reordered_inds, bad_ant_inds)
    ant_1_inds, ant_2_inds = np.transpose(
        list(itertools.combinations_with_replacement(np.arange(uv2.Nants_data), 2))
    )
    ant_1_inds = np.tile(np.array(ant_1_inds), uv2.Ntimes).astype(np.int_)
    ant_2_inds = np.tile(np.array(ant_2_inds), uv2.Ntimes).astype(np.int_)
    dig_gains = dig_gains[good_ants, 23]
    uv2.data_array = uv2.data_array / (
        dig_gains[ant_1_inds, np.newaxis, np.newaxis]
        * dig_gains[ant_2_inds, np.newaxis, np.newaxis]
    )
    uv2.history = uv1.history

    # make sure correction doesn't change data_array type
    assert uv1.data_array.dtype == np.complex64
    assert "Divided out digital gains" in uv1.history
    assert uv1 == uv2


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_remove_coarse_band(tmp_path):
    """Test coarse band removal."""
    # generate a spoof file with 32 channels
    cb_spoof = str(tmp_path / "cb_spoof_01_00.fits")
    with fits.open(filelist[1]) as mini1:
        mini1[1].data = np.repeat(mini1[1].data, 32, axis=0)
        mini1.writeto(cb_spoof)

    uv1 = UVData()
    uv1.read(
        [filelist[0], cb_spoof],
        data_array_dtype=np.complex64,
        use_future_array_shapes=True,
    )

    uv2 = UVData()
    uv2.read(
        [filelist[0], cb_spoof], remove_coarse_band=False, use_future_array_shapes=True
    )

    with h5py.File(
        DATA_PATH + "/mwa_config_data/MWA_rev_cb_10khz_doubles.h5", "r"
    ) as f:
        cb = f["coarse_band"][:]
    cb_array = cb.reshape(32, 4)
    cb_array = np.average(cb_array, axis=1)
    uv2.data_array /= cb_array[:, np.newaxis]

    uv2.history = uv1.history

    # make sure correction doesn't change data_array type
    assert uv1.data_array.dtype == np.complex64
    assert "Divided out pfb coarse channel bandpass" in uv1.history
    assert uv1 == uv2


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only")
def test_remove_coarse_band_mwax_40(tmp_path):
    """Test coarse band removal for a 40 kHz mwax file."""
    # generate a spoof file with 32 channels
    cb_spoof = str(tmp_path / "mwax_cb_spoof40_ch137_000.fits")
    meta_spoof = str(tmp_path / "mwax_cb_spoof40.metafits")

    with fits.open(filelist[12]) as mini1:
        mini1[1].data = np.repeat(mini1[1].data, 32, axis=1)
        mini1.writeto(cb_spoof)

    with fits.open(filelist[11]) as meta:
        meta[0].header["FINECHAN"] = 80
        meta.writeto(meta_spoof)

    uv1 = UVData()
    uv1.read([filelist[11], cb_spoof], use_future_array_shapes=True)

    uv2 = UVData()
    uv2.read(
        [filelist[11], cb_spoof], remove_coarse_band=False, use_future_array_shapes=True
    )

    with h5py.File(DATA_PATH + "/mwa_config_data/mwax_pfb_bandpass_40kHz.h5", "r") as f:
        cb_array = f["coarse_band"][:]

    uv2.data_array /= cb_array[:, np.newaxis]

    uv2.history = uv1.history

    assert "Divided out pfb coarse channel bandpass" in uv1.history
    assert uv1 == uv2


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only")
def test_remove_coarse_band_mwax_80(tmp_path):
    """Test coarse band removal for an 80 kHz mwax file."""
    # generate a spoof file with 16 channels
    cb_spoof = str(tmp_path / "mwax_cb_spoof80_ch137_000.fits")
    meta_spoof = str(tmp_path / "mwax_cb_spoof80.metafits")

    with fits.open(filelist[12]) as mini1:
        mini1[1].data = np.repeat(mini1[1].data, 16, axis=1)
        mini1.writeto(cb_spoof)

    with fits.open(filelist[11]) as meta:
        meta[0].header["FINECHAN"] = 80
        meta.writeto(meta_spoof)

    uv1 = UVData()
    uv1.read([meta_spoof, cb_spoof], use_future_array_shapes=True)

    uv2 = UVData()
    uv2.read(
        [meta_spoof, cb_spoof], remove_coarse_band=False, use_future_array_shapes=True
    )

    with h5py.File(DATA_PATH + "/mwa_config_data/mwax_pfb_bandpass_80kHz.h5", "r") as f:
        cb_array = f["coarse_band"][:]

    uv2.data_array /= cb_array[:, np.newaxis]

    uv2.history = uv1.history

    assert "Divided out pfb coarse channel bandpass" in uv1.history
    assert uv1 == uv2


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only")
def test_remove_coarse_band_mwax_warning(tmp_path):
    """Test coarse band removal for a file we don't have a passband for."""
    # generate a spoof file with 16 channels
    cb_spoof = str(tmp_path / "mwax_cb_spoof160_ch137_000.fits")
    meta_spoof = str(tmp_path / "mwax_cb_spoof160.metafits")

    with fits.open(filelist[12]) as mini1:
        mini1[1].data = np.repeat(mini1[1].data, 8, axis=1)
        mini1.writeto(cb_spoof)

    with fits.open(filelist[11]) as meta:
        meta[0].header["FINECHAN"] = 160
        meta.writeto(meta_spoof)

    uv = UVData()
    with pytest.raises(ValueError, match="mwax passband shapes are only available"):
        uv.read([meta_spoof, cb_spoof], flag_init=False, use_future_array_shapes=True)


def test_aoflagger_flags():
    """Test using aoflagger flags"""
    uv = UVData()
    files = filelist[0:2]
    files.append(filelist[3])
    messages = [
        "some coarse channel files were not submitted",
        "coarse channel, start time, and end time flagging will default",
    ]
    with uvtest.check_warnings(UserWarning, messages):
        uv.read(
            files,
            flag_init=False,
            remove_flagged_ants=False,
            correct_cable_len=False,
            use_future_array_shapes=True,
        )

    with fits.open(filelist[3]) as aoflags:
        flags = aoflags[1].data.field("FLAGS")
    flags = flags[:, :, np.newaxis]
    flags = np.repeat(flags, 4, axis=2)

    assert np.all(uv.flag_array == flags)


@pytest.mark.filterwarnings(
    "ignore:coarse channels are not contiguous for this observation"
)
@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:coarse channel, start time, and end time flagging")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_aoflagger_flags_multiple(tmp_path):
    """Test aoflagger flags with multiple coarse bands"""
    mod_mini_6 = str(tmp_path / "mini_gpubox06_01.fits")
    with fits.open(filelist[2]) as mini6:
        mini6[1].header["time"] = 1447698337
        mini6.writeto(mod_mini_6)
    files = filelist[0:2] + filelist[3:5]
    files.append(mod_mini_6)

    uv = UVData()
    uv.read(
        files, flag_init=False, remove_flagged_ants=False, use_future_array_shapes=True
    )

    with fits.open(filelist[3]) as aoflags:
        flags1 = aoflags[1].data.field("FLAGS")
    with fits.open(filelist[4]) as aoflags:
        flags2 = aoflags[1].data.field("FLAGS")
    flags = np.array([flags2[:, 0], flags1[:, 0]])
    flags = np.transpose(flags)
    flags = flags[:, :, np.newaxis]
    flags = np.repeat(flags, 4, axis=2)

    assert np.all(uv.flag_array == flags)


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:coarse channel, start time, and end time flagging")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_mismatch_flags():
    """Break by submitting flag and gpubox files from different coarse bands."""
    uv = UVData()
    files = filelist[0:2]
    files.append(filelist[4])
    with pytest.raises(ValueError, match="flag file coarse bands do not match"):
        uv.read(files)


@pytest.mark.filterwarnings(
    "ignore:coarse channels are not contiguous for this observation"
)
@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_propagate_coarse_flags():
    """
    Test that the flag(without flag_int) and nsample arrays correctly reflect data.
    """
    uv = UVData()
    uv.read(
        filelist[0:3],
        flag_init=False,
        propagate_coarse_flags=True,
        use_future_array_shapes=True,
    )
    assert np.all(uv.flag_array)


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_start_flag(tmp_path):
    """Test the default value of start_flag."""
    uv1 = UVData()
    uv1.read(
        filelist[0:2],
        flag_init=True,
        start_flag="goodtime",
        end_flag=0,
        edge_width=0,
        flag_dc_offset=False,
        use_future_array_shapes=True,
    )
    good_ants = np.delete(np.unique(uv1.ant_1_array), [59, 114])
    uv1.select(antenna_nums=good_ants)
    # start_time is after goodtime, so data for good antennas should be unflagged
    assert np.all(~uv1.flag_array)
    mod_mini = str(tmp_path / "starttime_gpubox01_00.fits")
    with fits.open(filelist[1]) as mini:
        mini[1].header["time"] = 1447698334
        mini.writeto(mod_mini)
    uv2 = UVData()
    uv2.read(
        [filelist[0], mod_mini],
        flag_init=True,
        start_flag="goodtime",
        end_flag=0,
        edge_width=0,
        flag_dc_offset=False,
        use_future_array_shapes=True,
    )
    # start_time is before goodtime, so all data should be flagged
    assert np.all(uv2.flag_array)


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_start_flag_goodtime_ppds():
    """Test that error is thrown using 'goodtime' with only ppds file."""
    uv = UVData()
    with pytest.raises(ValueError, match="To use start_flag='goodtime',"):
        uv.read([filelist[1], filelist[7]], flag_init=True, start_flag="goodtime")


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_start_flag_bad_string():
    """Test that error is thrown if start_flag is given string other than 'goodtime'"""
    uv = UVData()
    with pytest.raises(
        ValueError, match="start_flag must be int or float or 'goodtime'"
    ):
        uv.read(filelist[0:2], flag_init=True, start_flag="badstring")


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_start_flag_int_time(tmp_path):
    """Test goodtime returning a start_flag smaller than integration time."""
    uv = UVData()
    new_meta = str(tmp_path / "1131733552_goodtime.metafits")
    with fits.open(filelist[0]) as meta:
        meta[0].header["GOODTIME"] = 1447698337.25
        meta.writeto(new_meta)
    uv.read(
        [new_meta, filelist[1]],
        flag_init=True,
        start_flag="goodtime",
        use_future_array_shapes=True,
    )
    # first integration time should be flagged
    # data only has one integration time, so all data should be flagged
    assert np.all(uv.flag_array)


def test_input_output_mapping():
    """Test the input_output_mapping function."""
    mapping_dict = {}
    # fmt: off
    pfb_mapper = [
        0, 16, 32, 48, 1, 17, 33, 49, 2, 18, 34, 50, 3, 19, 35, 51,
        4, 20, 36, 52, 5, 21, 37, 53, 6, 22, 38, 54, 7, 23, 39, 55,
        8, 24, 40, 56, 9, 25, 41, 57, 10, 26, 42, 58, 11, 27, 43, 59,
        12, 28, 44, 60, 13, 29, 45, 61, 14, 30, 46, 62, 15, 31, 47, 63,
    ]
    # fmt: on
    for p in range(4):
        for i in range(64):
            mapping_dict[pfb_mapper[i] + p * 64] = p * 64 + i

    # compare with output from function
    function_output = input_output_mapping()
    assert function_output == mapping_dict

    return


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
def test_van_vleck_int():
    """Test van vleck correction integral implementation."""
    uv1 = UVData()
    uv1.read(
        filelist[8:10],
        flag_init=False,
        data_array_dtype=np.complex64,
        correct_van_vleck=True,
        cheby_approx=False,
        remove_coarse_band=False,
        remove_dig_gains=False,
        remove_flagged_ants=False,
        correct_cable_len=False,
        use_future_array_shapes=True,
    )
    # read in file corrected using integrate.quad with 1e-10 precision
    uv2 = UVData()
    uv2.read(filelist[10], use_future_array_shapes=True)
    assert np.allclose(uv1.data_array, uv2.data_array)


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
def test_van_vleck_cheby():
    """Test van vleck correction chebyshev implementation."""
    uv1 = UVData()
    uv1.read(
        filelist[8:10],
        flag_init=False,
        correct_van_vleck=True,
        cheby_approx=True,
        remove_coarse_band=False,
        remove_dig_gains=False,
        correct_cable_len=False,
        use_future_array_shapes=True,
    )
    # read in file corrected using integrate.quad with 1e-10 precision
    uv2 = UVData()
    uv2.read(filelist[10], use_future_array_shapes=True)

    # select only good ants
    good_ants = np.delete(np.unique(uv2.ant_1_array), 76)
    uv2.select(antenna_nums=good_ants)

    assert np.allclose(uv1.data_array, uv2.data_array)


def test_van_vleck_interp(tmp_path):
    """Test van vleck correction with sigmas out of cheby interpolation range."""
    small_sigs = str(tmp_path / "small_sigs07_01.fits")
    with fits.open(filelist[8]) as mini:
        mini[1].data = np.full((1, 66048), 7744)
        mini.writeto(small_sigs)
    messages = ["values are being corrected with the van vleck integral"]
    messages = messages * 10
    messages.append("some coarse channel files were not submitted")
    messages.append("Fixing auto-correlations to be be real-only,")
    uv = UVData()
    with uvtest.check_warnings(UserWarning, messages):
        uv.read(
            [small_sigs, filelist[9]],
            flag_init=False,
            correct_van_vleck=True,
            cheby_approx=True,
            remove_coarse_band=False,
            remove_dig_gains=False,
            correct_cable_len=False,
            use_future_array_shapes=True,
        )


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_remove_flagged_ants(tmp_path):
    """Test remove_flagged_ants."""
    uv1 = UVData()
    uv1.read(filelist[8:10], remove_flagged_ants=True, use_future_array_shapes=True)
    uv2 = UVData()
    uv2.read(filelist[8:10], remove_flagged_ants=False, use_future_array_shapes=True)
    good_ants = np.delete(np.unique(uv2.ant_1_array), 76)

    uv2.select(antenna_nums=good_ants)

    assert uv1 == uv2


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:.*values are being corrected with the van vleck")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
def test_small_sigs(tmp_path):
    """Test flag_small_auto_ants."""
    small_sigs = str(tmp_path / "small_sigs07_02.fits")
    with fits.open(filelist[8]) as mini:
        mini[1].data[0, 0] = 1000
        mini.writeto(small_sigs)
    uv1 = UVData()
    uv1.read(
        [small_sigs, filelist[9]],
        correct_van_vleck=True,
        flag_small_auto_ants=True,
        use_future_array_shapes=True,
    )
    messages = ["values are being corrected with the van vleck integral"]
    messages = messages * 8
    messages.append("some coarse channel files were not submitted")
    messages.append("cable length correction is now defaulted to True")
    uv2 = UVData()
    with uvtest.check_warnings(UserWarning, messages):
        uv2.read(
            [small_sigs, filelist[9]],
            correct_van_vleck=True,
            flag_small_auto_ants=False,
            use_future_array_shapes=True,
        )

    assert "flagged by the Van Vleck" in uv1.history
    assert uv2.Nants_data - uv1.Nants_data == 1


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
def test_deprecated_keywords():
    """Test warnings for deprecated keywords"""
    uv = UVData()
    messages = ["Use `use_aoflagger_flags`"]
    messages.append("Use `flag_small_auto_ants`")
    messages.append("some coarse channel files were not submitted")
    messages.append("cable length correction is now defaulted to True")
    with uvtest.check_warnings(
        [DeprecationWarning, DeprecationWarning, UserWarning, UserWarning], messages
    ):
        uv.read(
            filelist[0:2],
            use_cotter_flags=False,
            flag_small_sig_ants=True,
            use_future_array_shapes=True,
        )


@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
@pytest.mark.filterwarnings("ignore:cable length correction is now defaulted to True")
@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only")
def test_bscale(tmp_path):
    """Test that bscale is saved correctly"""
    # some data does not have bscale in the zeroth hdu
    bscale = str(tmp_path / "bscale_01_00.fits")
    with fits.open(filelist[1], do_not_scale_image_data=True) as mini:
        mini[0].header.remove("BSCALE")
        mini.writeto(bscale)
    uv1 = UVData()
    uv2 = UVData()
    uv3 = UVData()
    uv4 = UVData()
    # check when bscale is not in zeroth hdu but is in first
    uv1.read([filelist[0], bscale], use_future_array_shapes=True)
    assert uv1.extra_keywords["SCALEFAC"] == 0.5
    # check when bscale is in both zeroth and first hdu
    uv2.read(filelist[0:2], use_future_array_shapes=True)
    assert uv2.extra_keywords["SCALEFAC"] == 0.5
    # check pre-October 2014 data
    uv3.read(filelist[8:10], use_future_array_shapes=True)
    assert uv3.extra_keywords["SCALEFAC"] == 0.25
    # check mwax data
    uv4.read(filelist[11:13], use_future_array_shapes=True)
    assert "SCALEFAC" not in uv4.extra_keywords.keys()


# pyuvdata/uvdata/tests/test_uvdata.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for uvdata object."""
import copy
import itertools
import os
import re
import warnings
from collections import Counter, namedtuple

import h5py
import numpy as np
import pytest
from astropy import units
from astropy.coordinates import Angle, EarthLocation, Latitude, Longitude, SkyCoord
from astropy.time import Time
from astropy.utils import iers

import pyuvdata.tests as uvtest
import pyuvdata.utils as uvutils
from pyuvdata import UVCal, UVData
from pyuvdata.data import DATA_PATH
from pyuvdata.uvdata.tests.test_fhd import testfiles as fhd_files
from pyuvdata.uvdata.tests.test_mwa_corr_fits import filelist as mwa_corr_files
from pyuvdata.uvdata.uvdata import _future_array_shapes_warning, old_phase_attrs


@pytest.fixture(scope="function")
def uvdata_props():
    required_properties = [
        "data_array",
        "nsample_array",
        "flag_array",
        "Ntimes",
        "Nbls",
        "Nblts",
        "Nfreqs",
        "Npols",
        "Nspws",
        "uvw_array",
        "time_array",
        "ant_1_array",
        "ant_2_array",
        "lst_array",
        "baseline_array",
        "freq_array",
        "polarization_array",
        "spw_array",
        "integration_time",
        "channel_width",
        "telescope_name",
        "instrument",
        "telescope_location",
        "history",
        "vis_units",
        "Nants_data",
        "Nants_telescope",
        "antenna_names",
        "antenna_numbers",
        "antenna_positions",
        "flex_spw",
        "phase_center_app_ra",
        "phase_center_app_dec",
        "phase_center_frame_pa",
        "Nphase",
        "phase_center_catalog",
        "phase_center_id_array",
        "future_array_shapes",
    ]
    required_parameters = ["_" + prop for prop in required_properties]

    extra_properties = [
        "extra_keywords",
        "x_orientation",
        "antenna_diameters",
        "blt_order",
        "gst0",
        "rdate",
        "earth_omega",
        "dut1",
        "timesys",
        "uvplane_reference_time",
        "scan_number_array",
        "eq_coeffs",
        "eq_coeffs_convention",
        "flex_spw_id_array",
        "flex_spw_polarization_array",
        "filename",
        "blts_are_rectangular",
        "time_axis_faster_than_bls",
    ]
    extra_parameters = ["_" + prop for prop in extra_properties]

    other_properties = [
        "telescope_location_lat_lon_alt",
        "telescope_location_lat_lon_alt_degrees",
        "phase_center_ra_degrees",
        "phase_center_dec_degrees",
        "pyuvdata_version_str",
    ]

    uv_object = UVData()

    DataHolder = namedtuple(
        "DataHolder",
        [
            "uv_object",
            "required_parameters",
            "required_properties",
            "extra_parameters",
            "extra_properties",
            "other_properties",
        ],
    )

    uvdata_props = DataHolder(
        uv_object,
        required_parameters,
        required_properties,
        extra_parameters,
        extra_properties,
        other_properties,
    )
    # yields the data we need but will continue to the del call after tests
    yield uvdata_props

    # some post-test object cleanup
    del uvdata_props

    return


@pytest.fixture(scope="session")
def hera_uvh5_split_main(hera_uvh5_main):
    # Get some meta info up from
    unique_times = np.unique(hera_uvh5_main.time_array)
    mid_pt = int(len(unique_times) * 0.5)

    # We'll split the data in half here
    uv1 = hera_uvh5_main.select(times=unique_times[:mid_pt], inplace=False)
    uv2 = hera_uvh5_main.select(times=unique_times[mid_pt:], inplace=False)

    yield uv1, uv2, hera_uvh5_main

    # clean up when done
    del uv1, uv2

    return


@pytest.fixture(scope="function")
def hera_uvh5_split(hera_uvh5_split_main):
    uv1, uv2, uvfull = hera_uvh5_split_main
    uv1_copy = uv1.copy()
    uv2_copy = uv2.copy()
    uvfull_copy = uvfull.copy()

    yield uv1_copy, uv2_copy, uvfull_copy

    # clean up when done
    del uv1_copy, uv2_copy, uvfull_copy

    return


@pytest.fixture(scope="session")
def hera_uvh5_xx_main():
    """Read in a HERA uvh5 file."""
    hera_uvh5_xx = UVData()
    filename = os.path.join(DATA_PATH, "zen.2457698.40355.xx.HH.uvcA.uvh5")
    with uvtest.check_warnings(
        UserWarning, match="The uvw_array does not match the expected values"
    ):
        hera_uvh5_xx = UVData.from_file(filename, use_future_array_shapes=True)

    yield hera_uvh5_xx

    # clean up when done
    del hera_uvh5_xx

    return


@pytest.fixture(scope="function")
def hera_uvh5_xx(hera_uvh5_xx_main):
    """Make function level HERA uvh5 file based object."""
    hera_uvh5_xx = hera_uvh5_xx_main.copy()

    yield hera_uvh5_xx

    # clean up when done
    del hera_uvh5_xx

    return


@pytest.fixture(scope="session")
def sma_mir_catalog(sma_mir_main):
    catalog_dict = sma_mir_main.phase_center_catalog

    yield catalog_dict


@pytest.fixture(scope="session")
def carma_miriad_main():
    # read in test file for the resampling in time functions
    pytest.importorskip("pyuvdata._miriad")
    uv_object = UVData()
    testfile = os.path.join(DATA_PATH, "carma_miriad")
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", "Altitude is not present in Miriad file")
        uv_object.read(
            testfile, run_check=False, check_extra=False, use_future_array_shapes=True
        )
    uv_object.extra_keywords = {}

    yield uv_object


@pytest.fixture(scope="function")
def carma_miriad(carma_miriad_main):
    pytest.importorskip("pyuvdata._miriad")
    uv_object = carma_miriad_main.copy()

    yield uv_object


@pytest.fixture(scope="session")
def paper_uvh5_main():
    # read in test file for the resampling in time functions
    uv_object = UVData()
    uvh5_file = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA.uvh5")
    uv_object.read_uvh5(uvh5_file, use_future_array_shapes=True)

    yield uv_object

    # cleanup
    del uv_object

    return


@pytest.fixture(scope="function")
def paper_uvh5(paper_uvh5_main):
    # read in test file for the resampling in time functions
    uv_object = paper_uvh5_main.copy()

    yield uv_object

    # cleanup
    del uv_object

    return


@pytest.fixture(scope="session")
def bda_test_file_main():
    # read in test file for BDA-like data
    uv_object = UVData()
    testfile = os.path.join(DATA_PATH, "simulated_bda_file.uvh5")
    with uvtest.check_warnings(
        UserWarning,
        match=[
            "Unknown phase types are no longer supported",
            "Telescope mock-HERA is not in known_telescopes",
        ],
    ):
        uv_object.read(testfile, use_future_array_shapes=True)

    yield uv_object

    # cleanup
    del uv_object

    return


@pytest.fixture(scope="function")
def bda_test_file(bda_test_file_main):
    # read in test file for BDA-like data
    uv_object = bda_test_file_main.copy()

    yield uv_object

    # cleanup
    del uv_object

    return


@pytest.fixture(scope="session")
def pyuvsim_redundant_main():
    # read in test file for the compress/inflate redundancy functions
    uv_object = UVData()
    testfile = os.path.join(DATA_PATH, "fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits")
    uv_object.read(testfile, use_future_array_shapes=True)

    yield uv_object

    # cleanup
    del uv_object

    return


@pytest.fixture(scope="function")
def pyuvsim_redundant(pyuvsim_redundant_main):
    # read in test file for the compress/inflate redundancy functions
    uv_object = pyuvsim_redundant_main.copy()

    yield uv_object

    # cleanup
    del uv_object

    return


@pytest.fixture(scope="function")
def uvdata_baseline():
    uv_object = UVData()
    uv_object.Nants_telescope = 128
    uv_object2 = UVData()
    uv_object2.Nants_telescope = 2147483649
    uv_object3 = UVData()
    uv_object3.Nants_telescope = 2050

    DataHolder = namedtuple("DataHolder", ["uv_object", "uv_object2", "uv_object3"])

    uvdata_baseline = DataHolder(uv_object, uv_object2, uv_object3)

    # yields the data we need but will continue to the del call after tests
    yield uvdata_baseline

    # Post test clean-up
    del uvdata_baseline
    return


@pytest.fixture(scope="session")
def set_uvws_main(hera_uvh5_main):
    uv1 = hera_uvh5_main.copy()
    # uvws in the file are wrong. reset them.
    uv1.set_uvws_from_antenna_positions()

    yield uv1

    del uv1

    return


@pytest.fixture
def uv1_2_set_uvws(set_uvws_main):
    uv1 = set_uvws_main.copy()
    uv2 = set_uvws_main.copy()

    yield uv1, uv2

    del uv1, uv2

    return


@pytest.fixture()
def uv_phase_time_split(uv1_2_set_uvws):
    uv_phase, uv_raw = uv1_2_set_uvws

    uv_phase.reorder_blts(order="time", minor_order="baseline")
    uv_raw.reorder_blts(order="time", minor_order="baseline")

    uv_phase.phase(ra=0, dec=0, cat_name="npole", epoch="J2000", use_ant_pos=True)
    times = np.unique(uv_phase.time_array)
    time_set_1, time_set_2 = times[::2], times[1::2]

    uv_phase_1 = uv_phase.select(times=time_set_1, inplace=False)
    uv_phase_2 = uv_phase.select(times=time_set_2, inplace=False)

    uv_raw_1 = uv_raw.select(times=time_set_1, inplace=False)
    uv_raw_2 = uv_raw.select(times=time_set_2, inplace=False)

    yield uv_phase_1, uv_phase_2, uv_phase, uv_raw_1, uv_raw_2, uv_raw

    del uv_phase_1, uv_phase_2, uv_raw_1, uv_raw_2, uv_phase, uv_raw


@pytest.fixture()
def dummy_phase_dict():
    dummy_dict = {
        "cat_name": "z1",
        "cat_type": "sidereal",
        "cat_lon": 0.0,
        "cat_lat": 1.0,
        "cat_frame": "fk5",
        "cat_epoch": 2000.0,
        "cat_times": None,
        "cat_pm_ra": 0.0,
        "cat_pm_dec": 0.0,
        "cat_dist": 0.0,
        "cat_vrad": 0.0,
        "info_source": "user",
        "cat_id": None,
    }

    return dummy_dict


def test_parameter_iter(uvdata_props):
    """Test expected parameters."""
    all_params = []
    for prop in uvdata_props.uv_object:
        all_params.append(prop)
    for a in uvdata_props.required_parameters + uvdata_props.extra_parameters:
        assert a in all_params, (
            "expected attribute " + a + " not returned in object iterator"
        )


def test_required_parameter_iter(uvdata_props):
    """Test expected required parameters."""
    # at first it's a metadata_only object, so need to modify required_parameters
    required = []
    for prop in uvdata_props.uv_object.required():
        required.append(prop)
    expected_required = copy.copy(uvdata_props.required_parameters)
    expected_required.remove("_data_array")
    expected_required.remove("_nsample_array")
    expected_required.remove("_flag_array")
    for a in expected_required:
        assert a in required, (
            "expected attribute " + a + " not returned in required iterator"
        )

    uvdata_props.uv_object.data_array = 1
    uvdata_props.uv_object.nsample_array = 1
    uvdata_props.uv_object.flag_array = 1
    required = []
    for prop in uvdata_props.uv_object.required():
        required.append(prop)
    for a in uvdata_props.required_parameters:
        assert a in required, (
            "expected attribute " + a + " not returned in required iterator"
        )


def test_extra_parameter_iter(uvdata_props):
    """Test expected optional parameters."""
    extra = []
    for prop in uvdata_props.uv_object.extra():
        extra.append(prop)
    for a in uvdata_props.extra_parameters:
        assert a in extra, "expected attribute " + a + " not returned in extra iterator"


def test_unexpected_parameters(uvdata_props):
    """Test for extra parameters."""
    expected_parameters = (
        uvdata_props.required_parameters + uvdata_props.extra_parameters
    )
    attributes = [i for i in uvdata_props.uv_object.__dict__.keys() if i[0] == "_"]
    for a in attributes:
        assert a in expected_parameters, (
            "unexpected parameter " + a + " found in UVData"
        )


def test_unexpected_attributes(uvdata_props):
    """Test for extra attributes."""
    expected_attributes = (
        uvdata_props.required_properties
        + uvdata_props.extra_properties
        + uvdata_props.other_properties
    )
    attributes = [i for i in uvdata_props.uv_object.__dict__.keys() if i[0] != "_"]
    for a in attributes:
        assert a in expected_attributes, (
            "unexpected attribute " + a + " found in UVData"
        )


def test_properties(uvdata_props):
    """Test that properties can be get and set properly."""
    prop_dict = dict(
        list(
            zip(
                uvdata_props.required_properties + uvdata_props.extra_properties,
                uvdata_props.required_parameters + uvdata_props.extra_parameters,
            )
        )
    )
    for k, v in prop_dict.items():
        rand_num = np.random.rand()
        setattr(uvdata_props.uv_object, k, rand_num)
        this_param = getattr(uvdata_props.uv_object, v)
        try:
            assert rand_num == this_param.value
        except AssertionError:
            print("setting {prop_name} to a random number failed".format(prop_name=k))
            raise


def test_metadata_only_property(casa_uvfits):
    uvobj = casa_uvfits
    uvobj.data_array = None
    assert uvobj.metadata_only is False
    with pytest.raises(
        ValueError, match="Required UVParameter _data_array has not been set."
    ):
        uvobj.check()
    uvobj.flag_array = None
    assert uvobj.metadata_only is False
    with pytest.raises(
        ValueError, match="Required UVParameter _data_array has not been set."
    ):
        uvobj.check()
    uvobj.nsample_array = None
    assert uvobj.metadata_only is True


@pytest.mark.parametrize("filetype", ["miriad", "mir", "ms", "uvfits", "uvh5"])
def test_error_metadata_only_write(casa_uvfits, filetype, tmp_path):
    uvobj = casa_uvfits
    uvobj.data_array = None
    uvobj.flag_array = None
    uvobj.nsample_array = None
    assert uvobj.metadata_only is True

    out_file = os.path.join(tmp_path, "outtest." + filetype)
    with pytest.raises(ValueError, match="Cannot write out metadata only objects to a"):
        getattr(uvobj, "write_" + filetype)(out_file)


def test_equality(casa_uvfits):
    """Basic equality test."""
    uvobj = casa_uvfits
    assert uvobj == uvobj


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_check_nants_data(casa_uvfits):
    """Test check function."""
    uvobj = casa_uvfits

    assert uvobj.check()
    # Check variety of special cases
    uvobj.Nants_data += 1
    with pytest.raises(
        ValueError,
        match=(
            "Nants_data must be equal to the number of unique values in "
            "ant_1_array and ant_2_array"
        ),
    ):
        uvobj.check()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_check_nbls(casa_uvfits):
    uvobj = casa_uvfits
    uvobj.Nbls += 1
    with pytest.raises(
        ValueError,
        match=(
            "Nbls must be equal to the number of unique baselines in the data_array"
        ),
    ):
        uvobj.check()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_check_ntimes(casa_uvfits):
    uvobj = casa_uvfits
    uvobj.Ntimes += 1
    with pytest.raises(
        ValueError,
        match=("Ntimes must be equal to the number of unique times in the time_array"),
    ):
        uvobj.check()
    uvobj.Ntimes -= 1


def test_check_phase_center_id_array(casa_uvfits):
    uvobj = casa_uvfits
    uvobj.phase_center_id_array[0] = 4
    with pytest.raises(
        ValueError,
        match="Phase center id 4 is does not have an entry in `phase_center_catalog`",
    ):
        uvobj.check(strict_uvw_antpos_check=True)


def test_check_strict_uvw(casa_uvfits):
    uvobj = casa_uvfits
    with pytest.raises(
        ValueError,
        match="The uvw_array does not match the expected values given the antenna "
        "positions.",
    ):
        uvobj.check(strict_uvw_antpos_check=True)


@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only,")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_check_autos_only(hera_uvh5_xx):
    """
    Check case where all data is autocorrelations
    """
    uvobj = hera_uvh5_xx

    uvobj.select(blt_inds=np.where(uvobj.ant_1_array == uvobj.ant_2_array)[0])
    assert uvobj.check()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_check_uvw_array(hera_uvh5_xx):
    # test auto and cross corr uvw_array
    uvd = hera_uvh5_xx.copy()
    autos = np.isclose(uvd.ant_1_array - uvd.ant_2_array, 0.0)
    auto_inds = np.where(autos)[0]
    cross_inds = np.where(~autos)[0]

    # make auto have non-zero uvw coords, assert ValueError
    uvd.uvw_array[auto_inds[0], 0] = 0.1
    with pytest.raises(
        ValueError,
        match=("Some auto-correlations have non-zero uvw_array coordinates."),
    ):
        uvd.check()

    # make cross have |uvw| zero, assert ValueError
    uvd = hera_uvh5_xx.copy()
    uvd.uvw_array[cross_inds[0]][:] = 0.0
    with pytest.raises(
        ValueError,
        match=("Some cross-correlations have near-zero uvw_array magnitudes."),
    ):
        uvd.check()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_check_flag_array(casa_uvfits):
    uvobj = casa_uvfits

    uvobj.flag_array = np.ones((uvobj.flag_array.shape), dtype=int)

    with pytest.raises(
        ValueError, match="UVParameter _flag_array is not the appropriate type."
    ):
        uvobj.check()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_future_array_shape(casa_uvfits):
    """Convert to future shapes and check. Convert back and test for equality."""
    # starts as future shapes
    uvobj = casa_uvfits
    uvobj2 = casa_uvfits.copy()

    # test no-op:
    uvobj.use_future_array_shapes()
    assert uvobj == uvobj2

    with uvtest.check_warnings(
        DeprecationWarning,
        match="This method will be removed in version 3.0 when the current array "
        "shapes are no longer supported.",
    ):
        uvobj.use_current_array_shapes()
    uvobj.check()
    uvobj3 = uvobj.copy()

    # test no-op:
    with uvtest.check_warnings(
        DeprecationWarning,
        match="This method will be removed in version 3.0 when the current array "
        "shapes are no longer supported.",
    ):
        uvobj.use_current_array_shapes()
    assert uvobj == uvobj3

    uvobj.use_future_array_shapes()
    uvobj.check()

    assert uvobj == uvobj2

    with uvtest.check_warnings(
        DeprecationWarning,
        match="This method will be removed in version 3.0 when the current array "
        "shapes are no longer supported.",
    ):
        uvobj2.use_current_array_shapes()
    uvobj2.data_array = None
    uvobj2.flag_array = None
    uvobj2.nsample_array = None
    assert uvobj2.metadata_only

    uvobj2.use_future_array_shapes()
    assert uvobj2._data_array.expected_shape(uvobj2) == (
        uvobj2.Nblts,
        uvobj2.Nfreqs,
        uvobj2.Npols,
    )

    uvobj.channel_width[-1] = uvobj.channel_width[0] * 2.0
    uvobj.check()

    with pytest.raises(
        ValueError, match="channel_width parameter contains multiple unique values"
    ):
        with uvtest.check_warnings(
            DeprecationWarning,
            match="This method will be removed in version 3.0 when the current array "
            "shapes are no longer supported.",
        ):
            uvobj.use_current_array_shapes()

    with pytest.raises(ValueError, match="The frequencies are not evenly spaced"):
        uvobj._check_freq_spacing()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_nants_data_telescope_larger(casa_uvfits):
    uvobj = casa_uvfits
    # make sure it's okay for Nants_telescope to be strictly greater than Nants_data
    uvobj.Nants_telescope += 1
    # add dummy information for "new antenna" to pass object check
    uvobj.antenna_names = np.concatenate((uvobj.antenna_names, ["dummy_ant"]))
    uvobj.antenna_numbers = np.concatenate((uvobj.antenna_numbers, [20]))
    uvobj.antenna_positions = np.concatenate(
        (uvobj.antenna_positions, np.zeros((1, 3))), axis=0
    )
    assert uvobj.check()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_ant1_array_not_in_antnums(casa_uvfits):
    uvobj = casa_uvfits
    # make sure an error is raised if antennas in ant_1_array not in antenna_numbers
    # remove antennas from antenna_names & antenna_numbers by hand
    uvobj.antenna_names = uvobj.antenna_names[1:]
    uvobj.antenna_numbers = uvobj.antenna_numbers[1:]
    uvobj.antenna_positions = uvobj.antenna_positions[1:, :]
    uvobj.Nants_telescope = uvobj.antenna_numbers.size
    with pytest.raises(
        ValueError, match="All antennas in ant_1_array must be in antenna_numbers"
    ):
        uvobj.check()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_ant2_array_not_in_antnums(casa_uvfits):
    uvobj = casa_uvfits
    # make sure an error is raised if antennas in ant_2_array not in antenna_numbers
    # remove antennas from antenna_names & antenna_numbers by hand
    uvobj = uvobj
    uvobj.antenna_names = uvobj.antenna_names[:-1]
    uvobj.antenna_numbers = uvobj.antenna_numbers[:-1]
    uvobj.antenna_positions = uvobj.antenna_positions[:-1]
    uvobj.Nants_telescope = uvobj.antenna_numbers.size
    with pytest.raises(
        ValueError, match="All antennas in ant_2_array must be in antenna_numbers"
    ):
        uvobj.check()


def test_converttofiletype(casa_uvfits):
    uvobj = casa_uvfits
    uvobj2 = uvobj.copy()
    fhd_obj = uvobj._convert_to_filetype("fhd")
    uvobj._convert_from_filetype(fhd_obj)
    assert uvobj == uvobj2

    with pytest.raises(
        ValueError, match="filetype must be uvfits, mir, miriad, ms, fhd, or uvh5"
    ):
        uvobj._convert_to_filetype("foo")


def test_baseline_to_antnums(uvdata_baseline):
    """Test baseline to antnum conversion for 256 & larger conventions."""
    assert uvdata_baseline.uv_object.baseline_to_antnums(65536) == (0, 0)
    assert uvdata_baseline.uv_object.baseline_to_antnums(592128) == (257, 256)
    assert uvdata_baseline.uv_object.baseline_to_antnums(4404493223938) == (2051, 2050)

    with pytest.raises(
        Exception,
        match=f"error Nants={uvdata_baseline.uv_object2.Nants_telescope}>2147483648"
        " not supported",
    ):
        uvdata_baseline.uv_object2.baseline_to_antnums(65536)
    with pytest.raises(ValueError, match="negative baseline numbers are not supported"):
        uvdata_baseline.uv_object.baseline_to_antnums(-10)
    with pytest.raises(
        ValueError, match="baseline numbers > 4611686018498691072 are not supported"
    ):
        uvdata_baseline.uv_object.baseline_to_antnums(4611686018498691073)
    ant_pairs = [(10, 20), (280, 310)]
    for pair in ant_pairs:
        if np.max(np.array(pair)) < 255:
            bl = uvdata_baseline.uv_object.antnums_to_baseline(
                pair[0], pair[1], attempt256=True
            )
            ant_pair_out = uvdata_baseline.uv_object.baseline_to_antnums(bl)
            assert pair == ant_pair_out

        bl = uvdata_baseline.uv_object.antnums_to_baseline(
            pair[0], pair[1], attempt256=False
        )
        ant_pair_out = uvdata_baseline.uv_object.baseline_to_antnums(bl)
        assert pair == ant_pair_out


def test_baseline_to_antnums_vectorized(uvdata_baseline):
    """Test vectorized antnum to baseline conversion."""
    ant_1 = [10, 280]
    ant_2 = [20, 310]
    baseline_array = uvdata_baseline.uv_object.antnums_to_baseline(ant_1, ant_2)
    assert np.array_equal(baseline_array, [86036, 639286])
    ant_1_out, ant_2_out = uvdata_baseline.uv_object.baseline_to_antnums(
        baseline_array.tolist()
    )
    assert np.array_equal(ant_1, ant_1_out)
    assert np.array_equal(ant_2, ant_2_out)


def test_antnums_to_baselines(uvdata_baseline):
    """Test antums to baseline conversion for 256 & larger conventions."""
    assert uvdata_baseline.uv_object.antnums_to_baseline(0, 0) == 65536
    assert uvdata_baseline.uv_object.antnums_to_baseline(257, 256) == 592128
    assert uvdata_baseline.uv_object.antnums_to_baseline(2051, 2050) == 4404493223938
    # Check attempt256
    assert uvdata_baseline.uv_object.antnums_to_baseline(0, 0, attempt256=True) == 0
    with uvtest.check_warnings(UserWarning, "found antenna numbers > 255"):
        uvdata_baseline.uv_object.antnums_to_baseline(256, 255, attempt256=True)
    with uvtest.check_warnings(UserWarning, "found antenna numbers > 2047"):
        uvdata_baseline.uv_object.antnums_to_baseline(2051, 2050, attempt256=True)
    with uvtest.check_warnings(UserWarning, "found antenna numbers > 2047"):
        uvdata_baseline.uv_object3.antnums_to_baseline(1112, 1111, attempt256=True)
    with pytest.raises(
        ValueError,
        match="cannot convert ant1, ant2 to a baseline index with Nants={Nants}"
        ">2147483648.".format(Nants=uvdata_baseline.uv_object2.Nants_telescope),
    ):
        uvdata_baseline.uv_object2.antnums_to_baseline(0, 0)
    # check for out of range antenna numbers
    with pytest.raises(
        ValueError,
        match="cannot convert ant1, ant2 to a baseline index "
        "with antenna numbers greater than 2147483647.",
    ):
        uvdata_baseline.uv_object.antnums_to_baseline(2147483649, 2147483648)
    with pytest.raises(
        ValueError,
        match="cannot convert ant1, ant2 to a baseline index "
        "with antenna numbers less than zero.",
    ):
        uvdata_baseline.uv_object.antnums_to_baseline(-10, 2047)
    # check a len-1 array returns as an array
    ant1 = np.array([1])
    ant2 = np.array([2])
    assert isinstance(
        uvdata_baseline.uv_object.antnums_to_baseline(ant1, ant2), np.ndarray
    )


def test_known_telescopes():
    """Test known_telescopes method returns expected results."""
    uv_object = UVData()
    astropy_sites = EarthLocation.get_site_names()
    while "" in astropy_sites:
        astropy_sites.remove("")

    # Using set to drop duplicate entries
    known_telescopes = list(
        set(astropy_sites + ["PAPER", "HERA", "SMA", "SZA", "OVRO-LWA"])
    )
    # calling np.sort().tolist() because [].sort() acts inplace and returns None
    # Before test had None == None
    assert (
        np.sort(known_telescopes).tolist()
        == np.sort(uv_object.known_telescopes()).tolist()
    )


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_hera_diameters(paper_uvh5):
    uv_in = paper_uvh5

    uv_in.telescope_name = "HERA"
    with uvtest.check_warnings(
        UserWarning,
        match="antenna_diameters are not set or being overwritten. Using known values",
    ):
        uv_in.set_telescope_params()

    assert uv_in.telescope_name == "HERA"
    assert uv_in.antenna_diameters is not None

    uv_in.check()


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_generic_read():
    uv_in = UVData()
    uvfits_file = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits")
    uv_in.read(uvfits_file, read_data=False, use_future_array_shapes=True)
    unique_times = np.unique(uv_in.time_array)

    with pytest.raises(
        ValueError, match="Only one of times and time_range can be provided."
    ):
        uv_in.read(
            uvfits_file,
            times=unique_times[0:2],
            time_range=[unique_times[0], unique_times[1]],
            use_future_array_shapes=True,
        )

    with pytest.raises(
        ValueError, match="Only one of antenna_nums and antenna_names can be provided."
    ):
        uv_in.read(
            uvfits_file,
            antenna_nums=uv_in.antenna_numbers[0],
            antenna_names=uv_in.antenna_names[1],
            use_future_array_shapes=True,
        )

    with pytest.raises(ValueError, match="File type could not be determined"):
        uv_in.read("foo")


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize(
    ["phase_kwargs", "partial"],
    [
        ({"cat_name": "loc0", "lon": 0.0, "lat": 0.0, "epoch": "J2000"}, False),
        ({"cat_name": "loc0", "lon": 0.0, "lat": 0.0, "phase_frame": "icrs"}, True),
        (
            {
                "cat_name": "gcrs1",
                "lon": Angle("5d").rad,
                "lat": Angle("30d").rad,
                "phase_frame": "gcrs",
            },
            False,
        ),
        (
            {
                "cat_name": "epoch2010",
                "ra": Angle("180d").rad,
                "dec": Angle("90d").rad,
                "epoch": Time("2010-01-01T00:00:00", format="isot", scale="utc"),
            },
            False,
        ),
    ],
)
def test_phase_unphase_hera(uv1_2_set_uvws, future_shapes, phase_kwargs, partial):
    """
    Read in drift data, phase to an RA/DEC, unphase and check for object equality.
    """
    uv1, uv_raw = uv1_2_set_uvws

    if not future_shapes:
        uv1.use_current_array_shapes()
        uv_raw.use_current_array_shapes()

    if partial:
        mask = np.full(uv1.Nblts, False)
        mask[: uv1.Nblts // 2] = True
        phase_kwargs["select_mask"] = mask
    else:
        mask = None

    uv1.phase(**phase_kwargs)
    if partial:
        uv1.unproject_phase(select_mask=mask)
    else:
        with uvtest.check_warnings(
            DeprecationWarning,
            match="This method is deprecated in favor of `unproject_phase`, which "
            "better describes what is being done. This method will be removed in "
            "version 2.4.",
        ):
            uv1.unphase_to_drift()
    if partial:
        uv1.merge_phase_centers(
            catalog_identifier=list(uv1.phase_center_catalog.keys()), ignore_name=True
        )
    else:
        uv1.rename_phase_center(0, new_name="zenith")
    # check that phase + unphase gets back to raw
    assert uv_raw == uv1


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_phase_unphase_hera_one_bl(uv1_2_set_uvws):
    _, uv_raw = uv1_2_set_uvws
    # check that phase + unphase work with one baseline
    uv_raw_small = uv_raw.select(blt_inds=[0], inplace=False)
    uv_phase_small = uv_raw_small.copy()
    with uvtest.check_warnings(
        DeprecationWarning,
        match="No cat_name provided, this will become an error in pyuvdata v2.4. "
        "Defaulting to naming based on lat/lon.",
    ):
        uv_phase_small.phase(Angle("23h").rad, Angle("15d").rad)
    uv_phase_small.unproject_phase(cat_name="zenith")
    assert uv_raw_small == uv_phase_small


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_phase_unphase_hera_antpos(uv1_2_set_uvws):
    uv_phase, uv_raw = uv1_2_set_uvws
    # check that they match if you phase & unphase using antenna locations
    # first replace the uvws with the right values
    antenna_enu = uvutils.ENU_from_ECEF(
        (uv_raw.antenna_positions + uv_raw.telescope_location),
        *uv_raw.telescope_location_lat_lon_alt,
    )
    uvw_calc = np.zeros_like(uv_raw.uvw_array)
    unique_times, unique_inds = np.unique(uv_raw.time_array, return_index=True)
    for jd in unique_times:
        inds = np.where(uv_raw.time_array == jd)[0]
        for bl_ind in inds:
            wh_ant1 = np.where(uv_raw.antenna_numbers == uv_raw.ant_1_array[bl_ind])
            ant1_index = wh_ant1[0][0]
            wh_ant2 = np.where(uv_raw.antenna_numbers == uv_raw.ant_2_array[bl_ind])
            ant2_index = wh_ant2[0][0]
            uvw_calc[bl_ind, :] = (
                antenna_enu[ant2_index, :] - antenna_enu[ant1_index, :]
            )

    uv_raw_new = uv_raw.copy()
    uv_raw_new.uvw_array = uvw_calc
    uv_phase.phase(ra=0.0, dec=0.0, epoch="J2000", cat_name="foo", use_ant_pos=True)
    uv_phase2 = uv_raw_new.copy()
    uv_phase2.phase(ra=0.0, dec=0.0, epoch="J2000", cat_name="foo")

    # The uvw's only agree to ~1mm. should they be better?
    assert np.allclose(uv_phase2.uvw_array, uv_phase.uvw_array, atol=1e-3)
    # the data array are just multiplied by the w's for phasing, so a difference
    # at the 1e-3 level makes the data array different at that level too.
    # -> change the tolerance on data_array for this test
    uv_phase2._data_array.tols = (0, 1e-3 * np.amax(np.abs(uv_phase2.data_array)))
    assert uv_phase2 == uv_phase

    # check that phase + unphase gets back to raw using antpos
    uv_phase.unproject_phase(use_ant_pos=True, cat_name="zenith")
    assert uv_raw_new == uv_phase


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_phase_hera_zenith_timestamp_minimal_changes(uv1_2_set_uvws):
    uv_phase, uv_raw = uv1_2_set_uvws
    # check that phasing to zenith with one timestamp has small changes
    # (it won't be identical because of precession/nutation changing the
    # coordinate axes)
    # use gcrs rather than icrs to reduce differences (don't include abberation)
    uv_raw_small = uv_raw.select(times=uv_raw.time_array[0], inplace=False)
    uv_phase_simple_small = uv_raw_small.copy()
    uv_phase_simple_small.phase_to_time(
        time=Time(uv_raw.time_array[0], format="jd"), phase_frame="gcrs"
    )

    # it's unclear to me how close this should be...
    assert np.allclose(
        uv_phase_simple_small.uvw_array, uv_raw_small.uvw_array, atol=1e-1
    )


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_phase_to_time_jd_input(uv1_2_set_uvws):
    uv_phase, uv_raw = uv1_2_set_uvws
    uv_phase.phase_to_time(uv_raw.time_array[0])
    uv_phase.unproject_phase(cat_name="zenith")
    assert uv_phase == uv_raw


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_phase_to_time_error(uv1_2_set_uvws):
    uv_phase, uv_raw = uv1_2_set_uvws
    # check error if not passing a Time object to phase_to_time
    with pytest.raises(TypeError, match="time must be an astropy.time.Time object"):
        uv_phase.phase_to_time("foo")


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The original `phase` method is deprecated")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_unphase_drift_data_error(uv1_2_set_uvws, sma_mir, future_shapes):
    uv_phase, uv_drift = uv1_2_set_uvws
    # check error unphasing an unphased object

    if not future_shapes:
        uv_phase.use_current_array_shapes()
        uv_drift.use_current_array_shapes()
        sma_mir.use_current_array_shapes()

    with pytest.raises(ValueError, match="Data are already unprojected."):
        with uvtest.check_warnings(
            DeprecationWarning,
            match="The `use_old_proj` option is deprecated and will be removed in "
            "version 2.4.",
        ):
            uv_drift.unproject_phase(use_old_proj=True)

    with uvtest.check_warnings(
        DeprecationWarning,
        match="The `use_old_proj` option is deprecated and will be removed in "
        "version 2.4.",
    ):
        uv_phase.phase(ra=0.0, dec=0.0, cat_name="foo", use_old_proj=True)

    with pytest.raises(IndexError, match="Selection mask must be of length Nblts."):
        uv_phase.unproject_phase(select_mask=np.full(uv_phase.Nblts // 2, True))

    with pytest.raises(ValueError, match="Selection mask must be a boolean array"):
        uv_phase.unproject_phase(select_mask=np.full(uv_phase.Nblts, 5.3))

    # Now make sure the old proj method works with unphasing
    with uvtest.check_warnings(
        DeprecationWarning,
        match="The `use_old_proj` option is deprecated and will be removed in "
        "version 2.4.",
    ):
        uv_phase.unproject_phase(use_old_proj=True, cat_name="zenith")
    assert uv_drift == uv_phase

    with pytest.raises(
        ValueError, match="Objects with fk5 phase frames are not compatible"
    ):
        with uvtest.check_warnings(
            DeprecationWarning,
            match="The `use_old_proj` option is deprecated and will be removed in "
            "version 2.4.",
        ):
            sma_mir.unproject_phase(use_old_proj=True)

    orig_frame = sma_mir.phase_center_catalog[1]["cat_frame"]
    orig_type = sma_mir.phase_center_catalog[1]["cat_type"]
    sma_mir.phase_center_catalog[1]["cat_frame"] = "icrs"
    sma_mir.phase_center_catalog[1]["cat_type"] = "ephem"

    with pytest.raises(
        ValueError, match="Objects with ephem phase centers are not compatible"
    ):
        with uvtest.check_warnings(
            DeprecationWarning,
            match="The `use_old_proj` option is deprecated and will be removed in "
            "version 2.4.",
        ):
            sma_mir.unproject_phase(use_old_proj=True)

    sma_mir.phase_center_catalog[1]["cat_frame"] = orig_frame
    sma_mir.phase_center_catalog[1]["cat_type"] = orig_type

    mask = np.full((uv_phase.Nblts), False, dtype=bool)
    mask[: uv_phase.Nblts // 2] = True
    uv_phase.phase(ra=0.0, dec=0.0, cat_name="foo", select_mask=mask)

    with pytest.raises(
        ValueError, match="Objects with multiple phase centers are not compatible"
    ):
        with uvtest.check_warnings(
            DeprecationWarning,
            match="The `use_old_proj` option is deprecated and will be removed in "
            "version 2.4.",
        ):
            uv_phase.unproject_phase(use_old_proj=True)

    # Check to make sure that wa can unphase w/o an error getting thrown. The new
    # unphase method does not throw an error when being called twice, but it does warn
    sma_mir.unproject_phase()
    with uvtest.check_warnings(
        UserWarning, match="No selected baselines are projected, doing nothing"
    ):
        sma_mir.unproject_phase()


@pytest.mark.filterwarnings("ignore:The `use_old_proj` option is deprecated")
@pytest.mark.filterwarnings("ignore:The `allow_rephase` option is deprecated")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "phase_func,phase_kwargs,err_msg",
    [
        (
            "phase",
            {
                "ra": 0,
                "dec": 0,
                "epoch": "J2000",
                "allow_rephase": False,
                "cat_name": "bar",
                "use_old_proj": True,
            },
            "Visibilities are already phased; set allow_rephase to True",
        ),
        (
            "phase",
            {
                "lon": 0,
                "lat": 0,
                "epoch": "J2000",
                "cat_type": "driftscan",
                "allow_rephase": True,
                "cat_name": "bar",
                "use_old_proj": True,
            },
            "Old phasing only supports sidereal catalog types, "
            "please change the cat_type or use_old_proj=False.",
        ),
        (
            "phase_to_time",
            {"time": 0, "allow_rephase": False, "use_old_proj": True},
            "Visibilities are already phased; set allow_rephase to True",
        ),
    ],
)
def test_phase_rephase_hera_errors(uv1_2_set_uvws, phase_func, phase_kwargs, err_msg):
    uv_phase, _ = uv1_2_set_uvws

    with pytest.raises(
        ValueError, match="lon parameter must be set if cat_type is not 'unprojected'"
    ):
        uv_phase.phase(dec=0.0, epoch="J2000", cat_name="foo")

    with pytest.raises(
        ValueError, match="lat parameter must be set if cat_type is not 'unprojected'"
    ):
        uv_phase.phase(ra=0.0, epoch="J2000", cat_name="foo")

    uv_phase.phase(ra=0.0, dec=0.0, epoch="J2000", cat_name="foo")
    # if this is phase_to_time, use this index set in the dictionary and
    # assign the value of the time_array associated with that index
    # this is a little hacky, but we cannot acces uv_phase.time_array in the
    # parametrize
    if phase_func == "phase_to_time":
        phase_kwargs["time"] = uv_phase.time_array[int(phase_kwargs["time"])]

    with pytest.raises(ValueError, match=err_msg):
        getattr(uv_phase, phase_func)(**phase_kwargs)


@pytest.mark.filterwarnings("ignore:The `use_old_proj` option is deprecated")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_phase_unphase_hera_bad_frame(uv1_2_set_uvws):
    uv_phase, _ = uv1_2_set_uvws
    # check errors when trying to phase to an unsupported frame
    with pytest.raises(
        ValueError, match="Old phasing only supports 'icrs' and 'gcrs' phase_frames"
    ):
        uv_phase.phase(
            ra=0.0,
            dec=0.0,
            cat_name="cirs0",
            epoch="J2000",
            phase_frame="cirs",
            use_old_proj=True,
        )


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only,")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize("use_ant_pos1", [True, False])
@pytest.mark.parametrize("use_ant_pos2", [True, False])
def test_unphasing(uv_phase_comp, future_shapes, use_ant_pos1, use_ant_pos2):
    uvd1, uvd2 = uv_phase_comp

    if not future_shapes:
        uvd1.use_current_array_shapes()
        uvd2.use_current_array_shapes()

    uvd1.unproject_phase(phase_frame="fk5", use_ant_pos=use_ant_pos1)
    uvd2.unproject_phase(phase_frame="fk5", use_ant_pos=use_ant_pos2)

    if use_ant_pos1 and use_ant_pos2:
        atol = 1e-12
    else:
        atol = 5e-2

    # the tolerances here are empirical -- based on what was seen in the
    # external phasing test. See the phasing memo in docs/references for
    # details.
    assert np.allclose(uvd1.uvw_array, uvd2.uvw_array, atol=atol)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize("use_ant_pos", [True, False])
@pytest.mark.parametrize("unphase_first", [True, False])
def test_phasing(uv_phase_comp, future_shapes, unphase_first, use_ant_pos):
    uvd1, uvd2 = uv_phase_comp

    if not future_shapes:
        uvd1.use_current_array_shapes()
        uvd2.use_current_array_shapes()

    if unphase_first:
        uvd2.unproject_phase(use_ant_pos=use_ant_pos)
        warning_str = ""
        exp_warning = None
    else:
        warning_str = [
            "The entry name UVCeti is not unique inside the phase center catalog, "
            "adding anyways",
            "The provided name UVCeti is already used but has different parameters. "
            "Adding another entry with the same name but a different ID and "
            "parameters.",
        ]
        exp_warning = UserWarning

    if use_ant_pos:
        uvd1.set_uvws_from_antenna_positions()

    uvd1_phase_dict = list(uvd1.phase_center_catalog.values())[0]

    with uvtest.check_warnings(exp_warning, match=warning_str):
        uvd2.phase(
            lon=uvd1_phase_dict["cat_lon"],
            lat=uvd1_phase_dict["cat_lat"],
            epoch=uvd1_phase_dict["cat_epoch"],
            phase_frame=uvd1_phase_dict["cat_frame"],
            cat_name=uvd1_phase_dict["cat_name"],
            use_ant_pos=use_ant_pos,
        )

    if use_ant_pos:
        atol = 1e-12
    else:
        atol = 5e-2

    # the tolerances here are empirical -- based on what was seen in the
    # external phasing test. See the phasing memo in docs/references for
    # details.
    assert np.allclose(uvd1.uvw_array, uvd2.uvw_array, atol=atol)


@pytest.mark.parametrize(
    "arg_dict,err_type,msg",
    [
        [
            {"name": "abc", "oldproj": True},
            ValueError,
            "Objects with fk5 phase frames are not compatible",
        ],
        [
            {"name": "abc", "mask": [True] * 2},
            IndexError,
            "Selection mask must be of length Nblts.",
        ],
        [
            {"name": "abc", "mask": [5.0]},
            ValueError,
            "Selection mask must be a boolean array",
        ],
    ],
)
def test_phasing_multi_phase_errs(sma_mir, arg_dict, err_type, msg):
    # Now do a few things that aren't allowed w/ a multi-phase-ctr data set
    print(sma_mir.Nblts)
    with pytest.raises(err_type, match=msg):
        sma_mir.phase(
            ra=0,
            dec=0,
            cat_name=arg_dict.get("name"),
            use_old_proj=arg_dict.get("oldproj"),
            select_mask=arg_dict.get("mask"),
        )


# We're using the old phase method here since these values were all derived using that
# method, so we'll just filter out those warnings now.
# TODO I don't think the statement above is correct. These files were phased by
# Cotter (I believe), not by pyvudata...
@pytest.mark.filterwarnings("ignore:The `use_old_proj` option is deprecated")
@pytest.mark.filterwarnings("ignore:The `phase_frame` parameter is deprecated")
@pytest.mark.filterwarnings("ignore:The `allow_rephase` option is deprecated")
@pytest.mark.filterwarnings("ignore:The entry name UVCeti is not unique")
@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:The original `phase` method is deprecated")
@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only,")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_old_phasing(future_shapes):
    """Use MWA files phased to 2 different places to test phasing."""
    file1 = os.path.join(DATA_PATH, "1133866760.uvfits")
    file2 = os.path.join(DATA_PATH, "1133866760_rephase.uvfits")
    uvd1 = UVData()
    uvd2 = UVData()
    uvd1.read_uvfits(file1, use_future_array_shapes=future_shapes)
    uvd2.read_uvfits(file2, use_future_array_shapes=future_shapes)

    # change from fk5 to icrs so we can use old phasing (this is what was assumed)
    uvd1.phase_center_catalog[0]["cat_frame"] = "icrs"
    uvd2.phase_center_catalog[0]["cat_frame"] = "icrs"

    uvd1_drift = uvd1.copy()
    uvd1_drift.unproject_phase(phase_frame="gcrs", use_old_proj=True, use_ant_pos=False)
    uvd1_drift_antpos = uvd1.copy()
    uvd1_drift_antpos.unproject_phase(
        phase_frame="gcrs", use_ant_pos=True, use_old_proj=True
    )

    uvd2_drift = uvd2.copy()
    uvd2_drift.unproject_phase(phase_frame="gcrs", use_old_proj=True, use_ant_pos=False)
    uvd2_drift_antpos = uvd2.copy()
    uvd2_drift_antpos.unproject_phase(
        phase_frame="gcrs", use_ant_pos=True, use_old_proj=True
    )

    # the tolerances here are empirical -- based on what was seen in the
    # external phasing test. See the phasing memo in docs/references for
    # details.
    assert np.allclose(uvd1_drift.uvw_array, uvd2_drift.uvw_array, atol=2e-2)
    assert np.allclose(uvd1_drift_antpos.uvw_array, uvd2_drift_antpos.uvw_array)

    uvd1_phase_dict = list(uvd1.phase_center_catalog.values())[0]
    uvd2_rephase = uvd2.copy()
    uvd2_rephase.phase(
        lon=uvd1_phase_dict["cat_lon"],
        lat=uvd1_phase_dict["cat_lat"],
        epoch=uvd1_phase_dict["cat_epoch"],
        cat_name=uvd1_phase_dict["cat_name"],
        orig_phase_frame="gcrs",
        phase_frame="gcrs",
        use_ant_pos=False,
        use_old_proj=True,
    )
    uvd2_rephase_antpos = uvd2.copy()
    uvd2_rephase_antpos.phase(
        lon=uvd1_phase_dict["cat_lon"],
        lat=uvd1_phase_dict["cat_lat"],
        epoch=uvd1_phase_dict["cat_epoch"],
        cat_name=uvd1_phase_dict["cat_name"],
        orig_phase_frame="gcrs",
        phase_frame="gcrs",
        use_ant_pos=True,
        use_old_proj=True,
    )

    # the tolerances here are empirical -- based on what was seen in the
    # external phasing test. See the phasing memo in docs/references for
    # details.
    assert np.allclose(uvd1.uvw_array, uvd2_rephase.uvw_array, atol=2e-2)
    assert np.allclose(uvd1.uvw_array, uvd2_rephase_antpos.uvw_array, atol=5e-3)

    # rephase the drift objects to the original pointing and verify that they
    # match
    uvd1_drift.phase(
        lon=uvd1_phase_dict["cat_lon"],
        lat=uvd1_phase_dict["cat_lat"],
        epoch=uvd1_phase_dict["cat_epoch"],
        cat_name=uvd1_phase_dict["cat_name"],
        phase_frame="gcrs",
        use_ant_pos=False,
        use_old_proj=True,
    )
    uvd1_drift_antpos.phase(
        lon=uvd1_phase_dict["cat_lon"],
        lat=uvd1_phase_dict["cat_lat"],
        epoch=uvd1_phase_dict["cat_epoch"],
        cat_name=uvd1_phase_dict["cat_name"],
        phase_frame="gcrs",
        use_ant_pos=True,
        use_old_proj=True,
    )

    # the tolerances here are empirical -- caused by one unphase/phase cycle.
    # the antpos-based phasing differences are based on what was seen in the
    # external phasing test. See the phasing memo in docs/references for
    # details.
    assert np.allclose(uvd1.uvw_array, uvd1_drift.uvw_array, atol=1e-4)
    assert np.allclose(uvd1.uvw_array, uvd1_drift_antpos.uvw_array, atol=5e-3)

    uvd2_phase_dict = list(uvd2.phase_center_catalog.values())[0]
    uvd2_drift.phase(
        lon=uvd2_phase_dict["cat_lon"],
        lat=uvd2_phase_dict["cat_lat"],
        epoch=uvd2_phase_dict["cat_epoch"],
        cat_name=uvd2_phase_dict["cat_name"],
        phase_frame="gcrs",
        use_ant_pos=False,
        use_old_proj=True,
    )
    uvd2_drift_antpos.phase(
        lon=uvd2_phase_dict["cat_lon"],
        lat=uvd2_phase_dict["cat_lat"],
        epoch=uvd2_phase_dict["cat_epoch"],
        cat_name=uvd2_phase_dict["cat_name"],
        phase_frame="gcrs",
        use_ant_pos=True,
        use_old_proj=True,
    )

    # the tolerances here are empirical -- caused by one unphase/phase cycle.
    # the antpos-based phasing differences are based on what was seen in the
    # external phasing test. See the phasing memo in docs/references for
    # details.
    assert np.allclose(uvd2.uvw_array, uvd2_drift.uvw_array, atol=1e-4)
    assert np.allclose(uvd2.uvw_array, uvd2_drift_antpos.uvw_array, atol=2e-2)

    # Check to make sure that the old errors work
    with pytest.raises(ValueError, match="Visibilities are already phased;"):
        uvd1_drift.phase(
            ra=0, dec=0, cat_name="foo", use_old_proj=True, allow_rephase=False
        )

    uvd1_drift = uvd1.copy()
    # Move the time ~1 µsec off from J2000
    epoch_val = Time(Time(2000, format="jyear").mjd - 1e-11, format="mjd")
    # Unlike in the new phasing system, this should produce different results (since one
    # is FK5, and the other is ICRS)
    uvd1_drift.phase(ra=0, dec=0, cat_name="fk50", epoch=epoch_val, use_old_proj=True)
    uvd1.phase(ra=0, dec=0, cat_name="icrs0", epoch="J2000", use_old_proj=True)
    assert uvd1_drift != uvd1
    uvd1_drift = uvd1.copy()

    # Make sure the old default works for reverting to ICRS if no coord frame is found
    uvd1.unproject_phase(use_old_proj=True)
    uvd1_drift.unproject_phase(use_old_proj=True)
    assert uvd1 == uvd1_drift


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_select_blts(paper_uvh5, future_shapes):
    uv_object = paper_uvh5
    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history
    # fmt: off
    blt_inds = np.array([172, 182, 132, 227, 144, 44, 16, 104, 385, 134, 326, 140, 116,
                         218, 178, 391, 111, 276, 274, 308, 38, 64, 317, 76, 239, 246,
                         34, 39, 83, 184, 208, 60, 374, 295, 118, 337, 261, 21, 375,
                         396, 355, 187, 95, 122, 186, 113, 260, 264, 156, 13, 228, 291,
                         302, 72, 137, 216, 299, 341, 207, 256, 223, 250, 268, 147, 73,
                         32, 142, 383, 221, 203, 258, 286, 324, 265, 170, 236, 8, 275,
                         304, 117, 29, 167, 15, 388, 171, 82, 322, 248, 160, 85, 66,
                         46, 272, 328, 323, 152, 200, 119, 359, 23, 363, 56, 219, 257,
                         11, 307, 336, 289, 136, 98, 37, 163, 158, 80, 125, 40, 298,
                         75, 320, 74, 57, 346, 121, 129, 332, 238, 93, 18, 330, 339,
                         381, 234, 176, 22, 379, 199, 266, 100, 90, 292, 205, 58, 222,
                         350, 109, 273, 191, 368, 88, 101, 65, 155, 2, 296, 306, 398,
                         369, 378, 254, 67, 249, 102, 348, 392, 20, 28, 169, 262, 269,
                         287, 86, 300, 143, 177, 42, 290, 284, 123, 189, 175, 97, 340,
                         242, 342, 331, 282, 235, 344, 63, 115, 78, 30, 226, 157, 133,
                         71, 35, 212, 333])
    # fmt: on
    selected_data = uv_object.data_array[np.sort(blt_inds)]

    uv_object2 = uv_object.copy()
    uv_object2.select(blt_inds=blt_inds)
    assert len(blt_inds) == uv_object2.Nblts

    # verify that histories are different
    assert not uvutils._check_histories(old_history, uv_object2.history)

    assert uvutils._check_histories(
        old_history + "  Downselected to specific baseline-times using pyuvdata.",
        uv_object2.history,
    )

    assert np.all(selected_data == uv_object2.data_array)

    # check that it also works with higher dimension array
    uv_object2 = uv_object.copy()
    uv_object2.select(blt_inds=blt_inds[np.newaxis, :])
    assert len(blt_inds) == uv_object2.Nblts

    assert uvutils._check_histories(
        old_history + "  Downselected to specific baseline-times using pyuvdata.",
        uv_object2.history,
    )
    assert np.all(selected_data == uv_object2.data_array)

    # check that just doing the metadata works properly
    uv_object3 = uv_object.copy()
    uv_object3.data_array = None
    uv_object3.flag_array = None
    uv_object3.nsample_array = None
    assert uv_object3.metadata_only is True
    uv_object4 = uv_object3.select(blt_inds=blt_inds, inplace=False)
    uv_object5 = uv_object2.copy(metadata_only=True)
    assert uv_object4 == uv_object5

    # also check with inplace=True
    uv_object3.select(blt_inds=blt_inds)
    assert uv_object3 == uv_object4

    # check for errors associated with out of bounds indices
    with pytest.raises(ValueError, match="blt_inds contains indices that are negative"):
        uv_object.select(blt_inds=np.arange(-10, -5))
    with pytest.raises(
        ValueError, match="blt_inds contains indices that are too large"
    ):
        uv_object.select(blt_inds=np.arange(uv_object.Nblts + 1, uv_object.Nblts + 10))


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_phase_center_id(tmp_path, carma_miriad):
    uv_obj = carma_miriad
    testfile = os.path.join(tmp_path, "outtest.uvh5")

    uv1 = uv_obj.select(phase_center_ids=0, inplace=False)
    uv2 = uv_obj.select(phase_center_ids=[1, 2], inplace=False)

    uv_sum = uv1 + uv2
    assert uvutils._check_histories(
        uv_obj.history + "  Downselected to specific phase center IDs using pyuvdata.  "
        "Combined data along baseline-time axis using pyuvdata.",
        uv_sum.history,
    )
    uv_sum.history = uv_obj.history

    assert uv_sum == uv_obj

    uv_obj.write_uvh5(testfile)

    uv1_read = UVData.from_file(
        testfile, phase_center_ids=0, use_future_array_shapes=True
    )
    assert uv1_read == uv1

    uv2_read = UVData.from_file(
        testfile, phase_center_ids=[1, 2], use_future_array_shapes=True
    )
    assert uv2_read == uv2


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_phase_center_id_blts(carma_miriad):
    uv_obj = carma_miriad
    uv_obj.reorder_blts(order="baseline")

    uv1 = uv_obj.select(
        phase_center_ids=0, blt_inds=np.arange(uv_obj.Nblts // 2), inplace=False
    )
    uv2 = uv_obj.select(
        phase_center_ids=[1, 2], blt_inds=np.arange(uv_obj.Nblts // 2), inplace=False
    )
    uv3 = uv_obj.select(
        blt_inds=np.arange(uv_obj.Nblts // 2, uv_obj.Nblts), inplace=False
    )

    uv_sum = uv1 + uv2 + uv3
    print(uv_sum.history)
    assert uvutils._check_histories(
        uv_obj.history
        + "  Downselected to specific baseline-times, phase center IDs using pyuvdata. "
        "Combined data along baseline-time axis using pyuvdata.  "
        "Combined data along baseline-time axis using pyuvdata.  "
        "Unique part of next object history follows.  baseline-times",
        uv_sum.history,
    )
    uv_sum.history = uv_obj.history

    uv_sum.reorder_blts()
    uv_obj.reorder_blts()

    assert uv_sum == uv_obj


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_antennas(casa_uvfits):
    uv_object = casa_uvfits

    old_history = uv_object.history
    unique_ants = np.unique(
        uv_object.ant_1_array.tolist() + uv_object.ant_2_array.tolist()
    )
    ants_to_keep = np.array([1, 20, 12, 25, 4, 24, 2, 21, 22])

    blts_select = [
        (a1 in ants_to_keep) & (a2 in ants_to_keep)
        for (a1, a2) in zip(uv_object.ant_1_array, uv_object.ant_2_array)
    ]
    Nblts_selected = np.sum(blts_select)

    uv_object2 = uv_object.copy()
    uv_object2.select(antenna_nums=ants_to_keep)

    assert len(ants_to_keep) == uv_object2.Nants_data
    assert Nblts_selected == uv_object2.Nblts
    for ant in ants_to_keep:
        assert ant in uv_object2.ant_1_array or ant in uv_object2.ant_2_array
    for ant in np.unique(
        uv_object2.ant_1_array.tolist() + uv_object2.ant_2_array.tolist()
    ):
        assert ant in ants_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific antennas using pyuvdata.",
        uv_object2.history,
    )

    # check that it also works with higher dimension array
    uv_object2 = uv_object.copy()
    uv_object2.select(antenna_nums=ants_to_keep[np.newaxis, :])

    assert len(ants_to_keep) == uv_object2.Nants_data
    assert Nblts_selected == uv_object2.Nblts
    for ant in ants_to_keep:
        assert ant in uv_object2.ant_1_array or ant in uv_object2.ant_2_array
    for ant in np.unique(
        uv_object2.ant_1_array.tolist() + uv_object2.ant_2_array.tolist()
    ):
        assert ant in ants_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific antennas using pyuvdata.",
        uv_object2.history,
    )

    # now test using antenna_names to specify antennas to keep
    uv_object3 = uv_object.copy()
    ants_to_keep = np.array(sorted(ants_to_keep))
    ant_names = []
    for a in ants_to_keep:
        ind = np.where(uv_object3.antenna_numbers == a)[0][0]
        ant_names.append(uv_object3.antenna_names[ind])

    uv_object3.select(antenna_names=ant_names)

    assert uv_object2 == uv_object3

    # check that it also works with higher dimension array
    uv_object3 = uv_object.copy()
    ants_to_keep = np.array(sorted(ants_to_keep))
    ant_names = []
    for a in ants_to_keep:
        ind = np.where(uv_object3.antenna_numbers == a)[0][0]
        ant_names.append(uv_object3.antenna_names[ind])

    uv_object3.select(antenna_names=[ant_names])

    assert uv_object2 == uv_object3

    # test removing metadata associated with antennas that are no longer present
    # also add (different) antenna_diameters to test downselection
    uv_object.antenna_diameters = 1.0 * np.ones(
        (uv_object.Nants_telescope,), dtype=np.float64
    )
    for i in range(uv_object.Nants_telescope):
        uv_object.antenna_diameters += i
    uv_object4 = uv_object.copy()
    uv_object4.select(antenna_nums=ants_to_keep, keep_all_metadata=False)
    assert uv_object4.Nants_telescope == 9
    assert set(uv_object4.antenna_numbers) == set(ants_to_keep)
    for a in ants_to_keep:
        idx1 = uv_object.antenna_numbers.tolist().index(a)
        idx2 = uv_object4.antenna_numbers.tolist().index(a)
        assert uv_object.antenna_names[idx1] == uv_object4.antenna_names[idx2]
        assert np.allclose(
            uv_object.antenna_positions[idx1, :], uv_object4.antenna_positions[idx2, :]
        )
        assert uv_object.antenna_diameters[idx1], uv_object4.antenna_diameters[idx2]

    # remove antenna_diameters from object
    uv_object.antenna_diameters = None

    # check for errors associated with antennas not included in data, bad names
    # or providing numbers and names
    with pytest.raises(ValueError, match=r"Antenna number \[29 30\] is not present"):
        uv_object.select(antenna_nums=np.max(unique_ants) + np.arange(1, 3))

    # check the same thing with passing a list
    with pytest.raises(ValueError, match=r"Antenna number \[29 30\] is not present"):
        uv_object.select(antenna_nums=(np.max(unique_ants) + np.arange(1, 3)).tolist())

    with pytest.raises(
        ValueError, match="Antenna name test1 is not present in the antenna_names"
    ):
        uv_object.select(antenna_names="test1")
    with pytest.raises(
        ValueError, match="Only one of antenna_nums and antenna_names can be provided."
    ):
        uv_object.select(antenna_nums=ants_to_keep, antenna_names=ant_names)


def sort_bl(p):
    """Sort a tuple that starts with a pair of antennas, and may have stuff after."""
    if p[1] >= p[0]:
        return p
    return (p[1], p[0]) + p[2:]


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_bls(casa_uvfits):
    uv_object = casa_uvfits
    old_history = uv_object.history
    first_ants = [7, 3, 8, 3, 22, 28, 9]
    second_ants = [1, 21, 9, 2, 3, 4, 23]
    new_unique_ants = np.unique(first_ants + second_ants)
    ant_pairs_to_keep = list(zip(first_ants, second_ants))
    sorted_pairs_to_keep = [sort_bl(p) for p in ant_pairs_to_keep]

    blts_select = [
        sort_bl((a1, a2)) in sorted_pairs_to_keep
        for (a1, a2) in zip(uv_object.ant_1_array, uv_object.ant_2_array)
    ]
    Nblts_selected = np.sum(blts_select)

    uv_object2 = uv_object.copy()
    uv_object2.select(bls=ant_pairs_to_keep)
    sorted_pairs_object2 = [
        sort_bl(p) for p in zip(uv_object2.ant_1_array, uv_object2.ant_2_array)
    ]

    assert len(new_unique_ants) == uv_object2.Nants_data
    assert Nblts_selected == uv_object2.Nblts
    for ant in new_unique_ants:
        assert ant in uv_object2.ant_1_array or ant in uv_object2.ant_2_array
    for ant in np.unique(
        uv_object2.ant_1_array.tolist() + uv_object2.ant_2_array.tolist()
    ):
        assert ant in new_unique_ants
    for pair in sorted_pairs_to_keep:
        assert pair in sorted_pairs_object2
    for pair in sorted_pairs_object2:
        assert pair in sorted_pairs_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific baselines using pyuvdata.",
        uv_object2.history,
    )

    # check using baseline number parameter
    uv_object3 = uv_object.copy()
    bls_nums_to_keep = [
        uv_object.antnums_to_baseline(ant1, ant2) for ant1, ant2 in sorted_pairs_to_keep
    ]

    uv_object3.select(bls=bls_nums_to_keep)
    sorted_pairs_object3 = [
        sort_bl(p) for p in zip(uv_object3.ant_1_array, uv_object3.ant_2_array)
    ]

    assert len(new_unique_ants) == uv_object3.Nants_data
    assert Nblts_selected == uv_object3.Nblts
    for ant in new_unique_ants:
        assert ant in uv_object3.ant_1_array or ant in uv_object3.ant_2_array
    for ant in np.unique(
        uv_object3.ant_1_array.tolist() + uv_object3.ant_2_array.tolist()
    ):
        assert ant in new_unique_ants
    for pair in sorted_pairs_to_keep:
        assert pair in sorted_pairs_object3
    for pair in sorted_pairs_object3:
        assert pair in sorted_pairs_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific baselines using pyuvdata.",
        uv_object3.history,
    )

    # check select with polarizations
    first_ants = [7, 3, 8, 3, 22, 28, 9]
    second_ants = [1, 21, 9, 2, 3, 4, 23]
    pols = ["RR", "RR", "RR", "RR", "RR", "RR", "RR"]
    new_unique_ants = np.unique(first_ants + second_ants)
    bls_to_keep = list(zip(first_ants, second_ants, pols))
    sorted_bls_to_keep = [sort_bl(p) for p in bls_to_keep]

    blts_select = [
        sort_bl((a1, a2, "RR")) in sorted_bls_to_keep
        for (a1, a2) in zip(uv_object.ant_1_array, uv_object.ant_2_array)
    ]
    Nblts_selected = np.sum(blts_select)

    uv_object2 = uv_object.copy()
    uv_object2.select(bls=bls_to_keep)
    sorted_pairs_object2 = [
        sort_bl(p) + ("RR",)
        for p in zip(uv_object2.ant_1_array, uv_object2.ant_2_array)
    ]

    assert len(new_unique_ants) == uv_object2.Nants_data
    assert Nblts_selected == uv_object2.Nblts
    for ant in new_unique_ants:
        assert ant in uv_object2.ant_1_array or ant in uv_object2.ant_2_array
    for ant in np.unique(
        uv_object2.ant_1_array.tolist() + uv_object2.ant_2_array.tolist()
    ):
        assert ant in new_unique_ants
    for bl in sorted_bls_to_keep:
        assert bl in sorted_pairs_object2
    for bl in sorted_pairs_object2:
        assert bl in sorted_bls_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to "
        "specific baselines, polarizations using pyuvdata.",
        uv_object2.history,
    )

    # check that you can use numpy integers with out errors:
    first_ants = list(map(np.int32, [7, 3, 8, 3, 22, 28, 9]))
    second_ants = list(map(np.int32, [1, 21, 9, 2, 3, 4, 23]))
    ant_pairs_to_keep = list(zip(first_ants, second_ants))

    uv_object2 = uv_object.select(bls=ant_pairs_to_keep, inplace=False)
    sorted_pairs_object2 = [
        sort_bl(p) for p in zip(uv_object2.ant_1_array, uv_object2.ant_2_array)
    ]

    assert len(new_unique_ants) == uv_object2.Nants_data
    assert Nblts_selected == uv_object2.Nblts
    for ant in new_unique_ants:
        assert ant in uv_object2.ant_1_array or ant in uv_object2.ant_2_array
    for ant in np.unique(
        uv_object2.ant_1_array.tolist() + uv_object2.ant_2_array.tolist()
    ):
        assert ant in new_unique_ants
    for pair in sorted_pairs_to_keep:
        assert pair in sorted_pairs_object2
    for pair in sorted_pairs_object2:
        assert pair in sorted_pairs_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific baselines using pyuvdata.",
        uv_object2.history,
    )

    # check that you can specify a single pair without errors
    uv_object2.select(bls=(1, 7))
    sorted_pairs_object2 = [
        sort_bl(p) for p in zip(uv_object2.ant_1_array, uv_object2.ant_2_array)
    ]
    assert list(set(sorted_pairs_object2)) == [(1, 7)]

    # check for errors associated with antenna pairs not included in data and bad inputs
    with pytest.raises(
        ValueError, match="bls must be a list of tuples of antenna numbers"
    ):
        uv_object.select(bls=list(zip(first_ants, second_ants)) + [1, 7])

    with pytest.raises(
        ValueError, match="bls must be a list of tuples of antenna numbers"
    ):
        uv_object.select(bls=[(uv_object.antenna_names[0], uv_object.antenna_names[1])])

    with pytest.raises(
        ValueError, match=re.escape("Antenna pair (5, 1) does not have any data")
    ):
        uv_object.select(bls=(5, 1))

    with pytest.raises(
        ValueError, match=re.escape("Antenna pair (1, 5) does not have any data")
    ):
        uv_object.select(bls=(1, 5))

    with pytest.raises(
        ValueError, match=re.escape("Antenna pair (27, 27) does not have any data")
    ):
        uv_object.select(bls=(27, 27))

    with pytest.raises(
        ValueError,
        match="Cannot provide length-3 tuples and also specify polarizations.",
    ):
        uv_object.select(bls=(7, 1, "RR"), polarizations="RR")

    with pytest.raises(
        ValueError, match="The third element in each bl must be a polarization string"
    ):
        uv_object.select(bls=(7, 1, 7))

    with pytest.raises(
        ValueError, match="bls must be a list of tuples of antenna numbers"
    ):
        uv_object.select(bls=[])

    with pytest.raises(ValueError, match="Baseline number 100 is not present in the"):
        uv_object.select(bls=[100])


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_select_times(casa_uvfits, future_shapes):
    uv_object = casa_uvfits

    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history
    unique_times = np.unique(uv_object.time_array)
    times_to_keep = unique_times[[0, 3, 5, 6, 7, 10, 14]]

    Nblts_selected = np.sum([t in times_to_keep for t in uv_object.time_array])

    uv_object2 = uv_object.copy()
    uv_object2.select(times=times_to_keep)

    assert len(times_to_keep) == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for t in times_to_keep:
        assert t in uv_object2.time_array
    for t in np.unique(uv_object2.time_array):
        assert t in times_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific times using pyuvdata.",
        uv_object2.history,
    )
    # check that it also works with higher dimension array
    uv_object2 = uv_object.copy()
    uv_object2.select(times=times_to_keep[np.newaxis, :])

    assert len(times_to_keep) == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for t in times_to_keep:
        assert t in uv_object2.time_array
    for t in np.unique(uv_object2.time_array):
        assert t in times_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific times using pyuvdata.",
        uv_object2.history,
    )

    # check for errors associated with times not included in data
    t_use = np.min(unique_times) - uv_object.integration_time[0]
    with pytest.raises(
        ValueError, match=f"Time {t_use} is not present in the time_array"
    ):
        uv_object.select(times=[t_use])


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_time_range(casa_uvfits):
    uv_object = casa_uvfits
    old_history = uv_object.history
    unique_times = np.unique(uv_object.time_array)
    mean_time = np.mean(unique_times)
    time_range = [np.min(unique_times), mean_time]
    times_to_keep = unique_times[
        np.nonzero((unique_times <= time_range[1]) & (unique_times >= time_range[0]))
    ]

    Nblts_selected = np.nonzero(
        (uv_object.time_array <= time_range[1])
        & (uv_object.time_array >= time_range[0])
    )[0].size

    uv_object2 = uv_object.copy()
    uv_object2.select(time_range=time_range)

    assert times_to_keep.size == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for t in times_to_keep:
        assert t in uv_object2.time_array
    for t in np.unique(uv_object2.time_array):
        assert t in times_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific times using pyuvdata.",
        uv_object2.history,
    )


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_select_lsts(casa_uvfits, tmp_path, future_shapes):
    uv_object = casa_uvfits

    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history
    unique_lsts = np.unique(uv_object.lst_array)
    lsts_to_keep = unique_lsts[[0, 3, 5, 6, 7, 10, 14]]

    Nblts_selected = np.sum([lst in lsts_to_keep for lst in uv_object.lst_array])

    uv_object2 = uv_object.copy()
    uv_object2.select(lsts=lsts_to_keep)

    assert len(lsts_to_keep) == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for lst in lsts_to_keep:
        assert lst in uv_object2.lst_array
    for lst in np.unique(uv_object2.lst_array):
        assert lst in lsts_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific lsts using pyuvdata.",
        uv_object2.history,
    )
    # check that it also works with higher dimension array
    uv_object2 = uv_object.copy()
    uv_object2.select(lsts=lsts_to_keep[np.newaxis, :])

    assert len(lsts_to_keep) == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for lst in lsts_to_keep:
        assert lst in uv_object2.lst_array
    for lst in np.unique(uv_object2.lst_array):
        assert lst in lsts_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific lsts using pyuvdata.",
        uv_object2.history,
    )

    testfile = os.path.join(tmp_path, "outtest.uvh5")
    uv_object.write_uvh5(testfile)

    uv2_in = UVData.from_file(
        testfile, lsts=lsts_to_keep, use_future_array_shapes=future_shapes
    )
    if not future_shapes:
        uv2_in.use_current_array_shapes()

    assert uv2_in == uv_object2


def test_consolidate_phase_centers(casa_uvfits):
    uv1 = casa_uvfits
    uv2 = uv1.copy()
    uv3 = uv1.copy()

    init_phase_dict = uv1.phase_center_catalog[0]

    uv1.phase(ra=0, dec=0, phase_frame="icrs", cat_name="foo")
    assert uv1.Nphase == 1

    phase_dict1 = uv1.phase_center_catalog[1]

    with pytest.raises(
        ValueError,
        match="Either the reference_catalog or the other parameter must be set.",
    ):
        uv1._consolidate_phase_center_catalogs()

    uv2.phase(ra=0.5, dec=0.5, phase_frame="fk4", cat_name="bar")
    assert uv1.Nphase == 1

    phase_dict2 = uv2.phase_center_catalog[1]

    uv1._consolidate_phase_center_catalogs(
        other=uv2, reference_catalog=uv3.phase_center_catalog
    )

    assert uv1.phase_center_catalog[0] == init_phase_dict
    assert uv1.phase_center_catalog[1] == phase_dict2
    assert uv1.phase_center_catalog[2] == phase_dict1

    uv2._consolidate_phase_center_catalogs(other=uv1)
    assert uv1._phase_center_catalog == uv2._phase_center_catalog


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize("select_type", ["range", "specified"])
def test_select_lsts_casa(casa_uvfits, tmp_path, future_shapes, select_type):
    pytest.importorskip("casacore")

    uv_object = casa_uvfits

    if future_shapes:
        uv_object.use_future_array_shapes()

    unique_lsts = np.unique(uv_object.lst_array)

    if select_type == "range":
        mean_lst = np.mean(unique_lsts)
        lst_range = [np.min(unique_lsts), mean_lst]
        lsts_to_keep = unique_lsts[
            np.nonzero((unique_lsts <= lst_range[1]) & (unique_lsts >= lst_range[0]))
        ]
    elif select_type == "specified":
        lsts_to_keep = unique_lsts[[0, 3, 5, 6, 7, 10, 14]]

    uv_object.select(lsts=lsts_to_keep)

    testfile = os.path.join(tmp_path, "outtest.ms")
    uv_object.write_ms(testfile)

    with uvtest.check_warnings(
        UserWarning,
        match=[
            'select on read keyword set, but file_type is "ms" which does not '
            "support select on read",
            "The uvw_array does not match the expected values",
            "The uvw_array does not match the expected values",
        ],
    ):
        uv_in = UVData.from_file(
            testfile, lsts=lsts_to_keep, use_future_array_shapes=True
        )
    if future_shapes:
        uv_in.use_future_array_shapes()

    uv_in.history = uv_object.history
    uv_in._consolidate_phase_center_catalogs(
        reference_catalog=uv_object.phase_center_catalog, ignore_name=True
    )
    params_to_update = [
        "dut1",
        "earth_omega",
        "gst0",
        "rdate",
        "timesys",
        "extra_keywords",
        "scan_number_array",
    ]
    for param in params_to_update:
        setattr(uv_in, param, getattr(uv_object, param))

    assert uv_in == uv_object


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_select_lsts_multi_day(casa_uvfits, future_shapes):
    uv_object = casa_uvfits
    # check that times come from a single JD
    assert len(np.unique(np.asarray(uv_object.time_array, dtype=np.int_))) == 1
    # artificially make a "double object" with times on 2 different days
    # the addition of 0.9973 days is cleverly chosen so LSTs will roughly line up
    uv_object2 = uv_object.copy()
    uv_object2.time_array += 0.9973
    assert len(np.unique(np.asarray(uv_object2.time_array, dtype=np.int_))) == 1
    uv_object2.set_lsts_from_time_array()
    uv_object += uv_object2
    # check we have times from 2 days
    assert len(np.unique(np.asarray(uv_object.time_array, dtype=np.int_))) == 2

    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history
    unique_lsts = np.unique(uv_object.lst_array)
    mean_lst = np.mean(unique_lsts)
    lst_range = [np.min(unique_lsts), mean_lst]
    lsts_to_keep = unique_lsts[
        np.nonzero((unique_lsts <= lst_range[1]) & (unique_lsts >= lst_range[0]))
    ]

    Nblts_selected = np.nonzero(
        (uv_object.lst_array <= lst_range[1]) & (uv_object.lst_array >= lst_range[0])
    )[0].size

    uv_object2 = uv_object.copy()
    uv_object2.select(lst_range=lst_range)

    assert lsts_to_keep.size == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for lst in lsts_to_keep:
        assert lst in uv_object2.lst_array
    for lst in np.unique(uv_object2.lst_array):
        assert lst in lsts_to_keep
    unique_jds = np.unique(np.asarray(uv_object2.time_array, dtype=np.int_))
    assert len(unique_jds) == 2

    assert uvutils._check_histories(
        old_history + "  Downselected to specific lsts using pyuvdata.",
        uv_object2.history,
    )

    return


def test_select_lsts_out_of_range_error(casa_uvfits):
    uv_object = casa_uvfits
    unique_lsts = np.unique(uv_object.lst_array)
    target_lst = np.min(unique_lsts) - 0.1
    with pytest.raises(
        ValueError, match=f"LST {target_lst} is not present in the lst_array"
    ):
        uv_object.select(lsts=[target_lst])

    return


def test_select_lsts_too_big(casa_uvfits):
    uv_object = casa_uvfits
    # replace one LST with bogus value larger than 2*pi; otherwise we'll get an
    # error that the value isn't actually in the LST array
    lst0 = uv_object.lst_array[0]
    uv_object.lst_array = np.where(
        uv_object.lst_array == lst0, 7.0, uv_object.lst_array
    )
    unique_lsts = np.unique(uv_object.lst_array)
    lsts_to_keep = unique_lsts[[0, 3, 5, 6, 7, 10, 14]]
    assert 7.0 in lsts_to_keep

    Nblts_selected = np.sum([lst in lsts_to_keep for lst in uv_object.lst_array])

    uv_object2 = uv_object.copy()
    with uvtest.check_warnings(
        UserWarning,
        [
            "The lsts parameter contained a value greater than 2*pi",
            "The uvw_array does not match the expected values",
        ],
    ):
        uv_object2.select(lsts=lsts_to_keep)

    assert len(lsts_to_keep) == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for lst in lsts_to_keep:
        assert lst in uv_object2.lst_array
    for lst in np.unique(uv_object2.lst_array):
        assert lst in lsts_to_keep

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Writing in the MS file that the units of the data")
def test_select_lst_range(casa_uvfits, tmp_path):
    uv_object = casa_uvfits
    old_history = uv_object.history
    unique_lsts = np.unique(uv_object.lst_array)
    mean_lst = np.mean(unique_lsts)
    lst_range = [np.min(unique_lsts), mean_lst]
    lsts_to_keep = unique_lsts[
        np.nonzero((unique_lsts <= lst_range[1]) & (unique_lsts >= lst_range[0]))
    ]

    Nblts_selected = np.nonzero(
        (uv_object.lst_array <= lst_range[1]) & (uv_object.lst_array >= lst_range[0])
    )[0].size

    uv_object2 = uv_object.copy()
    uv_object2.select(lst_range=lst_range)

    assert lsts_to_keep.size == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for lst in lsts_to_keep:
        assert lst in uv_object2.lst_array
    for lst in np.unique(uv_object2.lst_array):
        assert lst in lsts_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific lsts using pyuvdata.",
        uv_object2.history,
    )

    testfile = os.path.join(tmp_path, "outtest.uvh5")
    uv_object.write_uvh5(testfile)

    with uvtest.check_warnings(
        UserWarning,
        [
            "Telescope EVLA is not in known_telescopes",
            "The uvw_array does not match the expected values",
        ],
    ):
        uv2_in = UVData.from_file(
            testfile, lst_range=lst_range, use_future_array_shapes=True
        )

    assert uv2_in == uv_object2


def test_select_lst_range_too_big(casa_uvfits):
    uv_object = casa_uvfits
    old_history = uv_object.history
    unique_lsts = np.unique(uv_object.lst_array)
    mean_lst = np.mean(unique_lsts)
    lst_range = [mean_lst, np.max(unique_lsts)]
    lsts_to_keep = unique_lsts[
        np.nonzero((unique_lsts <= lst_range[1]) & (unique_lsts >= lst_range[0]))
    ]

    Nblts_selected = np.nonzero(
        (uv_object.lst_array <= lst_range[1]) & (uv_object.lst_array >= lst_range[0])
    )[0].size

    # make max value larger than 2*pi
    lst_range[1] = 7.0
    uv_object2 = uv_object.copy()
    with uvtest.check_warnings(
        UserWarning,
        [
            "The lst_range contained a value greater than 2*pi",
            "The uvw_array does not match the expected values",
        ],
    ):
        uv_object2.select(lst_range=lst_range)

    assert lsts_to_keep.size == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for lst in lsts_to_keep:
        assert lst in uv_object2.lst_array
    for lst in np.unique(uv_object2.lst_array):
        assert lst in lsts_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific lsts using pyuvdata.",
        uv_object2.history,
    )

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_lst_range_wrap_around(casa_uvfits):
    uv_object = casa_uvfits
    old_history = uv_object.history
    unique_lsts = np.unique(uv_object.lst_array)
    mean_lst = np.mean(unique_lsts)
    min_lst = np.min(unique_lsts)
    max_lst = np.max(unique_lsts)
    lst_range = [max_lst + 0.1, mean_lst]
    lsts_to_keep = unique_lsts[
        np.nonzero((unique_lsts <= mean_lst) & (unique_lsts >= min_lst))
    ]

    Nblts_selected = np.nonzero(
        (uv_object.lst_array <= mean_lst) & (uv_object.lst_array >= min_lst)
    )[0].size

    uv_object2 = uv_object.copy()
    uv_object2.select(lst_range=lst_range)

    assert lsts_to_keep.size == uv_object2.Ntimes
    assert Nblts_selected == uv_object2.Nblts
    for lst in lsts_to_keep:
        assert lst in uv_object2.lst_array
    for lst in np.unique(uv_object2.lst_array):
        assert lst in lsts_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific lsts using pyuvdata.",
        uv_object2.history,
    )

    return


def test_select_time_range_no_data(casa_uvfits):
    """Check for error associated with times not included in data."""
    uv_object = casa_uvfits
    unique_times = np.unique(uv_object.time_array)
    with pytest.raises(ValueError, match="No elements in time range"):
        uv_object.select(
            time_range=[
                np.min(unique_times) - uv_object.integration_time[0] * 2,
                np.min(unique_times) - uv_object.integration_time[0],
            ]
        )


def test_select_lst_range_no_data(casa_uvfits):
    """Check for error associated with LSTS not included in data."""
    uv_object = casa_uvfits
    unique_lsts = np.unique(uv_object.lst_array)
    with pytest.raises(ValueError, match="No elements in LST range"):
        uv_object.select(
            lst_range=[np.min(unique_lsts) - 0.2, np.min(unique_lsts) - 0.1]
        )


def test_select_time_and_time_range(casa_uvfits):
    """Check for error setting times and time_range."""
    uv_object = casa_uvfits
    unique_times = np.unique(uv_object.time_array)
    mean_time = np.mean(unique_times)
    time_range = [np.min(unique_times), mean_time]
    times_to_keep = unique_times[[0, 3, 5, 6, 7, 10, 14]]
    with pytest.raises(
        ValueError,
        match=re.escape(
            "Only one of [times, time_range, lsts, lst_range] may be specified"
        ),
    ):
        uv_object.select(time_range=time_range, times=times_to_keep)


def test_select_time_range_one_elem(casa_uvfits):
    """Check for error if time_range not length 2."""
    uv_object = casa_uvfits
    unique_times = np.unique(uv_object.time_array)
    mean_time = np.mean(unique_times)
    time_range = [np.min(unique_times), mean_time]
    with pytest.raises(ValueError, match="time_range must be length 2"):
        uv_object.select(time_range=time_range[0])


def test_select_lst_range_one_elem(casa_uvfits):
    """Check for error if time_range not length 2."""
    uv_object = casa_uvfits
    unique_lsts = np.unique(uv_object.lst_array)
    mean_lst = np.mean(unique_lsts)
    lst_range = [np.min(unique_lsts), mean_lst]
    with pytest.raises(ValueError, match="lst_range must be length 2"):
        uv_object.select(lst_range=lst_range[0])


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_select_frequencies_writeerrors(casa_uvfits, future_shapes, tmp_path):
    uv_object = casa_uvfits

    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history

    if future_shapes:
        freqs_to_keep = uv_object.freq_array[np.arange(12, 22)]
    else:
        freqs_to_keep = uv_object.freq_array[0, np.arange(12, 22)]

    uv_object2 = uv_object.copy()
    uv_object2.select(frequencies=freqs_to_keep)

    assert len(freqs_to_keep) == uv_object2.Nfreqs
    for f in freqs_to_keep:
        assert f in uv_object2.freq_array
    for f in np.unique(uv_object2.freq_array):
        assert f in freqs_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific frequencies using pyuvdata.",
        uv_object2.history,
    )

    # check that it also works with higher dimension array
    uv_object2 = uv_object.copy()
    uv_object2.select(frequencies=freqs_to_keep[np.newaxis, :])

    assert len(freqs_to_keep) == uv_object2.Nfreqs
    for f in freqs_to_keep:
        assert f in uv_object2.freq_array
    for f in np.unique(uv_object2.freq_array):
        assert f in freqs_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to specific frequencies using pyuvdata.",
        uv_object2.history,
    )

    # check that selecting one frequency works
    uv_object2 = uv_object.copy()
    uv_object2.select(frequencies=freqs_to_keep[0])
    assert 1 == uv_object2.Nfreqs
    assert freqs_to_keep[0] in uv_object2.freq_array
    for f in uv_object2.freq_array:
        assert f in [freqs_to_keep[0]]

    assert uvutils._check_histories(
        old_history + "  Downselected to specific frequencies using pyuvdata.",
        uv_object2.history,
    )

    # check for errors associated with frequencies not included in data
    with pytest.raises(ValueError, match="Frequency "):
        uv_object.select(
            frequencies=[np.max(uv_object.freq_array) + uv_object.channel_width]
        )
    write_file_miriad = str(tmp_path / "select_test")
    write_file_uvfits = str(tmp_path / "select_test.uvfits")

    # check for warnings and errors associated with unevenly spaced or
    # non-contiguous frequencies
    uv_object2 = uv_object.copy()
    with uvtest.check_warnings(
        UserWarning,
        [
            "Selected frequencies are not evenly spaced",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        if future_shapes:
            uv_object2.select(frequencies=uv_object2.freq_array[[0, 5, 6]])
        else:
            uv_object2.select(frequencies=uv_object2.freq_array[0, [0, 5, 6]])

    with pytest.raises(ValueError, match="The frequencies are not evenly spaced"):
        uv_object2.write_uvfits(write_file_uvfits)

    try:
        import pyuvdata._miriad

        with pytest.raises(ValueError, match="The frequencies are not evenly spaced"):
            uv_object2.write_miriad(write_file_miriad)
    except ImportError:
        pass

    uv_object2 = uv_object.copy()
    with uvtest.check_warnings(
        UserWarning,
        [
            "Selected frequencies are not contiguous",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        if future_shapes:
            uv_object2.select(frequencies=uv_object2.freq_array[[0, 2, 4]])
        else:
            uv_object2.select(frequencies=uv_object2.freq_array[0, [0, 2, 4]])

    with pytest.raises(
        ValueError,
        match="The frequencies are separated by more than their channel width",
    ):
        uv_object2.write_uvfits(write_file_uvfits)

    try:
        import pyuvdata._miriad  # noqa

        with pytest.raises(
            ValueError,
            match="The frequencies are separated by more than their channel width",
        ):
            uv_object2.write_miriad(write_file_miriad)
    except ImportError:
        pass


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_select_freq_chans(casa_uvfits, future_shapes):
    uv_object = casa_uvfits

    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history
    chans_to_keep = np.arange(12, 22)

    uv_object2 = uv_object.copy()
    uv_object2.select(freq_chans=chans_to_keep)

    assert len(chans_to_keep) == uv_object2.Nfreqs
    for chan in chans_to_keep:
        if future_shapes:
            assert uv_object.freq_array[chan] in uv_object2.freq_array
        else:
            assert uv_object.freq_array[0, chan] in uv_object2.freq_array
    for f in np.unique(uv_object2.freq_array):
        if future_shapes:
            assert f in uv_object.freq_array[chans_to_keep]
        else:
            assert f in uv_object.freq_array[0, chans_to_keep]

    assert uvutils._check_histories(
        old_history + "  Downselected to specific frequencies using pyuvdata.",
        uv_object2.history,
    )

    # check that it also works with higher dimension array
    uv_object2 = uv_object.copy()
    uv_object2.select(freq_chans=chans_to_keep[np.newaxis, :])

    assert len(chans_to_keep) == uv_object2.Nfreqs
    for chan in chans_to_keep:
        if future_shapes:
            assert uv_object.freq_array[chan] in uv_object2.freq_array
        else:
            assert uv_object.freq_array[0, chan] in uv_object2.freq_array
    for f in np.unique(uv_object2.freq_array):
        if future_shapes:
            assert f in uv_object.freq_array[chans_to_keep]
        else:
            assert f in uv_object.freq_array[0, chans_to_keep]

    assert uvutils._check_histories(
        old_history + "  Downselected to specific frequencies using pyuvdata.",
        uv_object2.history,
    )

    # Test selecting both channels and frequencies
    if future_shapes:
        freqs_to_keep = uv_object.freq_array[np.arange(20, 30)]  # Overlaps with chans
    else:
        freqs_to_keep = uv_object.freq_array[
            0, np.arange(20, 30)
        ]  # Overlaps with chans
    all_chans_to_keep = np.arange(12, 30)

    uv_object2 = uv_object.copy()
    uv_object2.select(frequencies=freqs_to_keep, freq_chans=chans_to_keep)

    assert len(all_chans_to_keep) == uv_object2.Nfreqs
    for chan in all_chans_to_keep:
        if future_shapes:
            assert uv_object.freq_array[chan] in uv_object2.freq_array
        else:
            assert uv_object.freq_array[0, chan] in uv_object2.freq_array
    for f in np.unique(uv_object2.freq_array):
        if future_shapes:
            assert f in uv_object.freq_array[all_chans_to_keep]
        else:
            assert f in uv_object.freq_array[0, all_chans_to_keep]


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize(
    "pols_to_keep", ([-5, -6], ["xx", "yy"], ["nn", "ee"], [[-5, -6]])
)
def test_select_polarizations(hera_uvh5, future_shapes, pols_to_keep):
    uv_object = hera_uvh5

    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history

    uv_object2 = uv_object.copy()
    uv_object2.select(polarizations=pols_to_keep)

    if isinstance(pols_to_keep[0], list):
        pols_to_keep = pols_to_keep[0]

    assert len(pols_to_keep) == uv_object2.Npols
    for p in pols_to_keep:
        if isinstance(p, int):
            assert p in uv_object2.polarization_array
        else:
            assert (
                uvutils.polstr2num(p, x_orientation=uv_object2.x_orientation)
                in uv_object2.polarization_array
            )
    for p in np.unique(uv_object2.polarization_array):
        if isinstance(pols_to_keep[0], int):
            assert p in pols_to_keep
        else:
            assert p in uvutils.polstr2num(
                pols_to_keep, x_orientation=uv_object2.x_orientation
            )

    assert uvutils._check_histories(
        old_history + "  Downselected to specific polarizations using pyuvdata.",
        uv_object2.history,
    )


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_polarizations_errors(casa_uvfits, tmp_path):
    uv_object = casa_uvfits
    uv_object2 = uv_object.copy()
    uv_object2.select(polarizations=[-1, -2])

    # check for errors associated with polarizations not included in data
    with pytest.raises(
        ValueError, match="Polarization -3 is not present in the polarization_array"
    ):
        uv_object2.select(polarizations=[-3, -4])

    # check for warnings and errors associated with unevenly spaced polarizations
    with uvtest.check_warnings(
        UserWarning,
        [
            "Selected polarization values are not evenly spaced",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_object.select(polarizations=uv_object.polarization_array[[0, 1, 3]])
    write_file_uvfits = str(tmp_path / "select_test.uvfits")
    with pytest.raises(
        ValueError, match="The polarization values are not evenly spaced"
    ):
        uv_object.write_uvfits(write_file_uvfits)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_select(casa_uvfits, future_shapes):
    # now test selecting along all axes at once
    uv_object = casa_uvfits

    # Set the scan numbers so that we can check to make sure they are selected correctly
    uv_object._set_scan_numbers()

    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history
    # fmt: off
    blt_inds = np.array([1057, 461, 1090, 354, 528, 654, 882, 775, 369, 906, 748,
                         875, 296, 773, 554, 395, 1003, 476, 762, 976, 1285, 874,
                         717, 383, 1281, 924, 264, 1163, 297, 857, 1258, 1000, 180,
                         1303, 1139, 393, 42, 135, 789, 713, 527, 1218, 576, 100,
                         1311, 4, 653, 724, 591, 889, 36, 1033, 113, 479, 322,
                         118, 898, 1263, 477, 96, 935, 238, 195, 531, 124, 198,
                         992, 1131, 305, 154, 961, 6, 1175, 76, 663, 82, 637,
                         288, 1152, 845, 1290, 379, 1225, 1240, 733, 1172, 937, 1325,
                         817, 416, 261, 1316, 957, 723, 215, 237, 270, 1309, 208,
                         17, 1028, 895, 574, 166, 784, 834, 732, 1022, 1068, 1207,
                         356, 474, 313, 137, 172, 181, 925, 201, 190, 1277, 1044,
                         1242, 702, 567, 557, 1032, 1352, 504, 545, 422, 179, 780,
                         280, 890, 774, 884])
    # fmt: on
    ants_to_keep = np.array([12, 7, 21, 27, 3, 28, 8, 15])

    ant_pairs_to_keep = [(3, 12), (21, 27), (7, 8), (4, 28), (15, 7)]
    sorted_pairs_to_keep = [sort_bl(p) for p in ant_pairs_to_keep]

    if future_shapes:
        freqs_to_keep = uv_object.freq_array[np.arange(31, 39)]
    else:
        freqs_to_keep = uv_object.freq_array[0, np.arange(31, 39)]

    unique_times = np.unique(uv_object.time_array)
    times_to_keep = unique_times[[0, 2, 6, 8, 10, 13, 14]]

    pols_to_keep = [-1, -3]

    # Independently count blts that should be selected
    blts_blt_select = [i in blt_inds for i in np.arange(uv_object.Nblts)]
    blts_ant_select = [
        (a1 in ants_to_keep) & (a2 in ants_to_keep)
        for (a1, a2) in zip(uv_object.ant_1_array, uv_object.ant_2_array)
    ]
    blts_pair_select = [
        sort_bl((a1, a2)) in sorted_pairs_to_keep
        for (a1, a2) in zip(uv_object.ant_1_array, uv_object.ant_2_array)
    ]
    blts_time_select = [t in times_to_keep for t in uv_object.time_array]
    Nblts_select = np.sum(
        [
            bi & (ai & pi) & ti
            for (bi, ai, pi, ti) in zip(
                blts_blt_select, blts_ant_select, blts_pair_select, blts_time_select
            )
        ]
    )

    uv_object2 = uv_object.copy()
    uv_object2.select(
        blt_inds=blt_inds,
        antenna_nums=ants_to_keep,
        bls=ant_pairs_to_keep,
        frequencies=freqs_to_keep,
        times=times_to_keep,
        polarizations=pols_to_keep,
    )

    assert Nblts_select == uv_object2.Nblts
    for ant in np.unique(
        uv_object2.ant_1_array.tolist() + uv_object2.ant_2_array.tolist()
    ):
        assert ant in ants_to_keep

    assert len(freqs_to_keep) == uv_object2.Nfreqs
    for f in freqs_to_keep:
        assert f in uv_object2.freq_array
    for f in np.unique(uv_object2.freq_array):
        assert f in freqs_to_keep

    for t in np.unique(uv_object2.time_array):
        assert t in times_to_keep

    assert len(pols_to_keep) == uv_object2.Npols
    for p in pols_to_keep:
        assert p in uv_object2.polarization_array
    for p in np.unique(uv_object2.polarization_array):
        assert p in pols_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to "
        "specific baseline-times, antennas, "
        "baselines, times, frequencies, "
        "polarizations using pyuvdata.",
        uv_object2.history,
    )

    # test that a ValueError is raised if the selection eliminates all blts
    with pytest.raises(
        ValueError, match="No baseline-times were found that match criteria"
    ):
        uv_object.select(times=unique_times[0], antenna_nums=1)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_select_with_lst(casa_uvfits, future_shapes):
    # now test selecting along all axes at once, but with LST instead of times
    uv_object = casa_uvfits

    if not future_shapes:
        uv_object.use_current_array_shapes()

    old_history = uv_object.history
    # fmt: off
    blt_inds = np.array([1057, 461, 1090, 354, 528, 654, 882, 775, 369, 906, 748,
                         875, 296, 773, 554, 395, 1003, 476, 762, 976, 1285, 874,
                         717, 383, 1281, 924, 264, 1163, 297, 857, 1258, 1000, 180,
                         1303, 1139, 393, 42, 135, 789, 713, 527, 1218, 576, 100,
                         1311, 4, 653, 724, 591, 889, 36, 1033, 113, 479, 322,
                         118, 898, 1263, 477, 96, 935, 238, 195, 531, 124, 198,
                         992, 1131, 305, 154, 961, 6, 1175, 76, 663, 82, 637,
                         288, 1152, 845, 1290, 379, 1225, 1240, 733, 1172, 937, 1325,
                         817, 416, 261, 1316, 957, 723, 215, 237, 270, 1309, 208,
                         17, 1028, 895, 574, 166, 784, 834, 732, 1022, 1068, 1207,
                         356, 474, 313, 137, 172, 181, 925, 201, 190, 1277, 1044,
                         1242, 702, 567, 557, 1032, 1352, 504, 545, 422, 179, 780,
                         280, 890, 774, 884])
    # fmt: on
    ants_to_keep = np.array([12, 7, 21, 27, 3, 28, 8, 15])

    ant_pairs_to_keep = [(3, 12), (21, 27), (7, 8), (4, 28), (15, 7)]
    sorted_pairs_to_keep = [sort_bl(p) for p in ant_pairs_to_keep]

    if future_shapes:
        freqs_to_keep = uv_object.freq_array[np.arange(31, 39)]
    else:
        freqs_to_keep = uv_object.freq_array[0, np.arange(31, 39)]

    unique_lsts = np.unique(uv_object.lst_array)
    lsts_to_keep = unique_lsts[[0, 2, 6, 8, 10, 13, 14]]

    pols_to_keep = [-1, -3]

    # Independently count blts that should be selected
    blts_blt_select = [i in blt_inds for i in np.arange(uv_object.Nblts)]
    blts_ant_select = [
        (a1 in ants_to_keep) & (a2 in ants_to_keep)
        for (a1, a2) in zip(uv_object.ant_1_array, uv_object.ant_2_array)
    ]
    blts_pair_select = [
        sort_bl((a1, a2)) in sorted_pairs_to_keep
        for (a1, a2) in zip(uv_object.ant_1_array, uv_object.ant_2_array)
    ]
    blts_lst_select = [lst in lsts_to_keep for lst in uv_object.lst_array]
    Nblts_select = np.sum(
        [
            bi & (ai & pi) & li
            for (bi, ai, pi, li) in zip(
                blts_blt_select, blts_ant_select, blts_pair_select, blts_lst_select
            )
        ]
    )

    uv_object2 = uv_object.copy()
    uv_object2.select(
        blt_inds=blt_inds,
        antenna_nums=ants_to_keep,
        bls=ant_pairs_to_keep,
        frequencies=freqs_to_keep,
        lsts=lsts_to_keep,
        polarizations=pols_to_keep,
    )

    assert Nblts_select == uv_object2.Nblts
    for ant in np.unique(
        uv_object2.ant_1_array.tolist() + uv_object2.ant_2_array.tolist()
    ):
        assert ant in ants_to_keep

    assert len(freqs_to_keep) == uv_object2.Nfreqs
    for f in freqs_to_keep:
        assert f in uv_object2.freq_array
    for f in np.unique(uv_object2.freq_array):
        assert f in freqs_to_keep

    for lst in np.unique(uv_object2.lst_array):
        assert lst in lsts_to_keep

    assert len(pols_to_keep) == uv_object2.Npols
    for p in pols_to_keep:
        assert p in uv_object2.polarization_array
    for p in np.unique(uv_object2.polarization_array):
        assert p in pols_to_keep

    assert uvutils._check_histories(
        old_history + "  Downselected to "
        "specific baseline-times, antennas, "
        "baselines, lsts, frequencies, "
        "polarizations using pyuvdata.",
        uv_object2.history,
    )

    # test that a ValueError is raised if the selection eliminates all blts
    with pytest.raises(
        ValueError, match="No baseline-times were found that match criteria"
    ):
        uv_object.select(lsts=unique_lsts[0], antenna_nums=1)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_not_inplace(casa_uvfits):
    # Test non-inplace select
    uv_object = casa_uvfits
    old_history = uv_object.history
    uv1 = uv_object.select(freq_chans=np.arange(32), inplace=False)
    uv1 += uv_object.select(freq_chans=np.arange(32, 64), inplace=False)
    assert uvutils._check_histories(
        old_history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis "
        "using pyuvdata.",
        uv1.history,
    )

    uv1.history = old_history
    assert uv1 == uv_object


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("metadata_only", [True, False])
@pytest.mark.parametrize("future_shapes", [True, False])
def test_conjugate_bls(casa_uvfits, metadata_only, future_shapes):
    testfile = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits")

    if not metadata_only:
        uv1 = casa_uvfits
        if not future_shapes:
            uv1.use_current_array_shapes()
    else:
        uv1 = UVData()
        uv1.read_uvfits(
            testfile, read_data=False, use_future_array_shapes=future_shapes
        )
    if metadata_only:
        assert uv1.metadata_only

    # file comes in with ant1<ant2
    assert np.min(uv1.ant_2_array - uv1.ant_1_array) >= 0

    # check everything swapped & conjugated when go to ant2<ant1
    uv2 = uv1.copy()
    uv2.conjugate_bls(convention="ant2<ant1")
    assert np.min(uv2.ant_1_array - uv2.ant_2_array) >= 0

    assert np.allclose(uv1.ant_1_array, uv2.ant_2_array)
    assert np.allclose(uv1.ant_2_array, uv2.ant_1_array)
    assert np.allclose(
        uv1.uvw_array,
        -1 * uv2.uvw_array,
        rtol=uv1._uvw_array.tols[0],
        atol=uv1._uvw_array.tols[1],
    )

    if not metadata_only:
        # complicated because of the polarization swaps
        # polarization_array = [-1 -2 -3 -4]
        if future_shapes:
            assert np.allclose(
                uv1.data_array[:, :, :2],
                np.conj(uv2.data_array[:, :, :2]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

            assert np.allclose(
                uv1.data_array[:, :, 2],
                np.conj(uv2.data_array[:, :, 3]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

            assert np.allclose(
                uv1.data_array[:, :, 3],
                np.conj(uv2.data_array[:, :, 2]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )
        else:
            assert np.allclose(
                uv1.data_array[:, :, :, :2],
                np.conj(uv2.data_array[:, :, :, :2]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

            assert np.allclose(
                uv1.data_array[:, :, :, 2],
                np.conj(uv2.data_array[:, :, :, 3]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

            assert np.allclose(
                uv1.data_array[:, :, :, 3],
                np.conj(uv2.data_array[:, :, :, 2]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

    # check everything returned to original values with original convention
    uv2.conjugate_bls(convention="ant1<ant2")
    assert uv1 == uv2

    # conjugate a particular set of blts
    blts_to_conjugate = np.arange(uv2.Nblts // 2)
    blts_not_conjugated = np.arange(uv2.Nblts // 2, uv2.Nblts)
    uv2.conjugate_bls(convention=blts_to_conjugate)

    assert np.allclose(
        uv1.ant_1_array[blts_to_conjugate], uv2.ant_2_array[blts_to_conjugate]
    )
    assert np.allclose(
        uv1.ant_2_array[blts_to_conjugate], uv2.ant_1_array[blts_to_conjugate]
    )
    assert np.allclose(
        uv1.ant_1_array[blts_not_conjugated], uv2.ant_1_array[blts_not_conjugated]
    )
    assert np.allclose(
        uv1.ant_2_array[blts_not_conjugated], uv2.ant_2_array[blts_not_conjugated]
    )

    assert np.allclose(
        uv1.uvw_array[blts_to_conjugate],
        -1 * uv2.uvw_array[blts_to_conjugate],
        rtol=uv1._uvw_array.tols[0],
        atol=uv1._uvw_array.tols[1],
    )
    assert np.allclose(
        uv1.uvw_array[blts_not_conjugated],
        uv2.uvw_array[blts_not_conjugated],
        rtol=uv1._uvw_array.tols[0],
        atol=uv1._uvw_array.tols[1],
    )
    if not metadata_only:
        # complicated because of the polarization swaps
        # polarization_array = [-1 -2 -3 -4]
        if future_shapes:
            assert np.allclose(
                uv1.data_array[blts_to_conjugate, :, :2],
                np.conj(uv2.data_array[blts_to_conjugate, :, :2]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )
            assert np.allclose(
                uv1.data_array[blts_not_conjugated, :, :2],
                uv2.data_array[blts_not_conjugated, :, :2],
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

            assert np.allclose(
                uv1.data_array[blts_to_conjugate, :, 2],
                np.conj(uv2.data_array[blts_to_conjugate, :, 3]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )
            assert np.allclose(
                uv1.data_array[blts_not_conjugated, :, 2],
                uv2.data_array[blts_not_conjugated, :, 2],
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

            assert np.allclose(
                uv1.data_array[blts_to_conjugate, :, 3],
                np.conj(uv2.data_array[blts_to_conjugate, :, 2]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )
            assert np.allclose(
                uv1.data_array[blts_not_conjugated, :, 3],
                uv2.data_array[blts_not_conjugated, :, 3],
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )
        else:
            assert np.allclose(
                uv1.data_array[blts_to_conjugate, :, :, :2],
                np.conj(uv2.data_array[blts_to_conjugate, :, :, :2]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )
            assert np.allclose(
                uv1.data_array[blts_not_conjugated, :, :, :2],
                uv2.data_array[blts_not_conjugated, :, :, :2],
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

            assert np.allclose(
                uv1.data_array[blts_to_conjugate, :, :, 2],
                np.conj(uv2.data_array[blts_to_conjugate, :, :, 3]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )
            assert np.allclose(
                uv1.data_array[blts_not_conjugated, :, :, 2],
                uv2.data_array[blts_not_conjugated, :, :, 2],
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

            assert np.allclose(
                uv1.data_array[blts_to_conjugate, :, :, 3],
                np.conj(uv2.data_array[blts_to_conjugate, :, :, 2]),
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )
            assert np.allclose(
                uv1.data_array[blts_not_conjugated, :, :, 3],
                uv2.data_array[blts_not_conjugated, :, :, 3],
                rtol=uv1._data_array.tols[0],
                atol=uv1._data_array.tols[1],
            )

    # check uv half plane conventions
    uv2.conjugate_bls(convention="u<0", use_enu=False)
    assert np.max(uv2.uvw_array[:, 0]) <= 0

    uv2.conjugate_bls(convention="u>0", use_enu=False)
    assert np.min(uv2.uvw_array[:, 0]) >= 0

    uv2.conjugate_bls(convention="v<0", use_enu=False)
    assert np.max(uv2.uvw_array[:, 1]) <= 0

    uv2.conjugate_bls(convention="v>0", use_enu=False)
    assert np.min(uv2.uvw_array[:, 1]) >= 0

    # unphase to drift to test using ENU positions
    uv2.unproject_phase(use_ant_pos=True)
    uv2.conjugate_bls(convention="u<0")
    assert np.max(uv2.uvw_array[:, 0]) <= 0

    uv2.conjugate_bls(convention="u>0")
    assert np.min(uv2.uvw_array[:, 0]) >= 0

    uv2.conjugate_bls(convention="v<0")
    assert np.max(uv2.uvw_array[:, 1]) <= 0

    uv2.conjugate_bls(convention="v>0")
    assert np.min(uv2.uvw_array[:, 1]) >= 0

    # test errors
    with pytest.raises(ValueError, match="convention must be one of"):
        uv2.conjugate_bls(convention="foo")

    with pytest.raises(ValueError, match="If convention is an index array"):
        uv2.conjugate_bls(convention=np.arange(5) - 1)

    with pytest.raises(ValueError, match="If convention is an index array"):
        uv2.conjugate_bls(convention=[uv2.Nblts])


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_reorder_pols(casa_uvfits, future_shapes):
    # Test function to fix polarization order
    uv1 = casa_uvfits

    if not future_shapes:
        uv1.use_current_array_shapes()

    uv2 = uv1.copy()
    uv3 = uv1.copy()
    # reorder uv2 manually
    order = [1, 3, 2, 0]
    uv2.polarization_array = uv2.polarization_array[order]
    if future_shapes:
        uv2.data_array = uv2.data_array[:, :, order]
        uv2.nsample_array = uv2.nsample_array[:, :, order]
        uv2.flag_array = uv2.flag_array[:, :, order]
    else:
        uv2.data_array = uv2.data_array[:, :, :, order]
        uv2.nsample_array = uv2.nsample_array[:, :, :, order]
        uv2.flag_array = uv2.flag_array[:, :, :, order]
    uv1.reorder_pols(order=order)
    assert uv1 == uv2

    # Restore original order
    uv1 = uv3.copy()
    uv2.reorder_pols()
    assert uv1 == uv2

    uv1.reorder_pols(order="AIPS")
    # check that we have aips ordering
    aips_pols = np.array([-1, -2, -3, -4]).astype(int)
    assert np.all(uv1.polarization_array == aips_pols)

    uv2 = uv1.copy()
    uv2.reorder_pols(order="CASA")
    # check that we have casa ordering
    casa_pols = np.array([-1, -3, -4, -2]).astype(int)
    assert np.all(uv2.polarization_array == casa_pols)
    order = np.array([0, 2, 3, 1])
    if future_shapes:
        assert np.all(uv2.data_array == uv1.data_array[:, :, order])
        assert np.all(uv2.flag_array == uv1.flag_array[:, :, order])
    else:
        assert np.all(uv2.data_array == uv1.data_array[:, :, :, order])
        assert np.all(uv2.flag_array == uv1.flag_array[:, :, :, order])

    uv2.reorder_pols(order="AIPS")
    # check that we have aips ordering again
    assert uv1 == uv2

    # check error on unknown order
    with pytest.raises(
        ValueError,
        match="order must be one of: 'AIPS', 'CASA', or an index array of length Npols",
    ):
        uv2.reorder_pols({"order": "foo"})

    # check error if order is an array of the wrong length
    with pytest.raises(ValueError, match="If order is an index array, it must"):
        uv2.reorder_pols(order=[3, 2, 1])


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "order,minor_order,msg",
    [
        ["foo", None, "order must be one of"],
        [np.arange(5), None, "If order is an index array, it must"],
        [np.arange(5, dtype=np.float64), None, "If order is an index array, it must"],
        [np.arange(1360), "time", "Minor order cannot be set if order is an index"],
        ["bda", "time", "minor_order cannot be specified if order is"],
        ["baseline", "ant1", "minor_order conflicts with order"],
        ["time", "foo", "minor_order can only be one of"],
    ],
)
def test_reorder_blts_errs(casa_uvfits, order, minor_order, msg):
    """
    Verify that reorder_blts throws expected errors when supplied with bad args
    """
    with pytest.raises(ValueError, match=msg):
        casa_uvfits.reorder_blts(order=order, minor_order=minor_order)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_reorder_blts(casa_uvfits, future_shapes):
    uv1 = casa_uvfits

    if not future_shapes:
        uv1.use_current_array_shapes()

    # test default reordering in detail
    uv2 = uv1.copy()
    uv2.reorder_blts()
    assert uv2.blt_order == ("time", "baseline")
    assert np.min(np.diff(uv2.time_array)) >= 0
    for this_time in np.unique(uv2.time_array):
        bls_2 = uv2.baseline_array[np.where(uv2.time_array == this_time)]
        bls_1 = uv1.baseline_array[np.where(uv2.time_array == this_time)]
        assert bls_1.shape == bls_2.shape
        assert np.min(np.diff(bls_2)) >= 0
        bl_inds = [np.where(bls_1 == bl)[0][0] for bl in bls_2]
        assert np.allclose(bls_1[bl_inds], bls_2)

        uvw_1 = uv1.uvw_array[np.where(uv2.time_array == this_time)[0], :]
        uvw_2 = uv2.uvw_array[np.where(uv2.time_array == this_time)[0], :]
        assert uvw_1.shape == uvw_2.shape
        assert np.allclose(uvw_1[bl_inds, :], uvw_2)

        data_1 = uv1.data_array[np.where(uv2.time_array == this_time)[0]]
        data_2 = uv2.data_array[np.where(uv2.time_array == this_time)[0]]
        assert data_1.shape == data_2.shape
        assert np.allclose(data_1[bl_inds], data_2)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize(
    "args1,args2",
    [
        [{"order": "time"}, {"order": "time", "minor_order": "time"}],
        [{"order": "time"}, {"order": "time", "minor_order": "ant1"}],
        [{"order": "time"}, {"order": "time", "minor_order": "baseline"}],
        [{}, {"order": np.arange(1360)}],  # casa_uvfits already in default order
        [{"order": "time"}, {"order": "time", "conj_convention": "ant1<ant2"}],
        [{"autos_first": True}, {"autos_first": False}],  # No autos in this file
    ],
)
def test_reorder_blts_equiv(casa_uvfits, args1, args2, future_shapes):
    """
    Test that sorting orders that _should_ be equivalent actually are
    """
    uv1 = casa_uvfits
    if not future_shapes:
        uv1.use_current_array_shapes()
    uv2 = uv1.copy()

    uv1.reorder_blts(**args1)
    uv2.reorder_blts(**args2)
    # Ignore the blt_order for now, since we check this elsewhere and we know its not
    # going to be consistent between the two different sorts
    uv1.blt_order = None
    uv2.blt_order = None
    assert uv1 == uv2

    # check that loopback works
    uv1.reorder_blts()
    uv2.reorder_blts()
    assert uv1 == uv2


@pytest.mark.parametrize(
    "order,m_order,check_tuple,check_attr",
    [
        ["time", "ant1", ("time", "ant1"), ("time_array",)],
        ["time", "ant2", ("time", "ant2"), ("time_array",)],
        ["time", "baseline", ("time", "baseline"), ("time_array",)],
        ["baseline", None, ("baseline", "time"), ("baseline_array",)],
        ["ant1", None, ("ant1", "ant2"), ("ant_1_array",)],
        ["ant1", "time", ("ant1", "time"), ("ant_1_array",)],
        ["ant1", "baseline", ("ant1", "baseline"), ("ant_1_array",)],
        ["ant2", None, ("ant2", "ant1"), ("ant_2_array",)],
        ["ant2", "time", ("ant2", "time"), ("ant_2_array",)],
        ["ant2", "baseline", ("ant2", "baseline"), ("ant_2_array",)],
        ["bda", None, ("bda",), ("integration_time", "baseline_array")],
        # Below is the ant1 order in the hera_uvh5 file for one integration, for
        # testing the case of providing an index array
        [
            np.argsort(np.tile([0, 0, 2, 0, 2, 1, 0, 2, 1, 11], 20)),
            None,
            None,
            ("ant_1_array",),
        ],
    ],
)
@pytest.mark.parametrize("autos_first", [True, False])
def test_reorder_blts_sort_order(
    hera_uvh5, order, m_order, check_tuple, check_attr, autos_first
):
    hera_uvh5.reorder_blts(order=order, minor_order=m_order, autos_first=autos_first)
    assert hera_uvh5.blt_order == check_tuple
    if isinstance(order, str) and autos_first:
        auto_inds = np.nonzero(hera_uvh5.ant_1_array == hera_uvh5.ant_2_array)[0]
        cross_inds = np.nonzero(hera_uvh5.ant_1_array != hera_uvh5.ant_2_array)[0]

        assert np.max(auto_inds) < np.min(cross_inds)
        for item in check_attr:
            attr_arr = getattr(hera_uvh5, item)
            attr_auto = attr_arr[: np.min(cross_inds)]
            attr_cross = attr_arr[np.min(cross_inds) :]
            assert np.all(np.diff(attr_auto) >= 0)
            assert np.all(np.diff(attr_cross) >= 0)

    else:
        for item in check_attr:
            assert np.all(np.diff(getattr(hera_uvh5, item)) >= 0)


@pytest.mark.parametrize(
    "arg_dict,msg",
    [
        [{"spord": [1]}, "Index array for spw_order must contain all indicies for"],
        [{"spord": "karto"}, "spw_order can only be one of 'number', '-number',"],
        [{"chord": [1]}, "Index array for channel_order must contain all indicies"],
        [{"chord": "karto"}, "channel_order can only be one of 'freq' or '-freq'"],
    ],
)
def test_reorder_freqs_errs(sma_mir, arg_dict, msg):
    """
    Verify that appropriate errors are thrown when providing bad arguments to
    reorder_freqs.
    """
    with pytest.raises(ValueError, match=msg):
        sma_mir.reorder_freqs(
            spw_order=arg_dict.get("spord"), channel_order=arg_dict.get("chord")
        )


@pytest.mark.parametrize(
    "arg_dict,msg",
    [
        [{}, "Not specifying either spw_order or channel_order"],
        [
            {"spword": "number", "selspw": [1, 3]},
            [
                "The spw_order argument is ignored when providing an argument for",
                "Specifying select_spw without providing channel_order causes",
            ],
        ],
        [
            {"spword": "number", "selspw": [1, 4], "chord": np.arange(131072)},
            [
                "The select_spw argument is ignored when providing an array_like",
                "The spw_order argument is ignored when providing an array_like",
            ],
        ],
    ],
)
def test_reorder_freqs_warnings(sma_mir, sma_mir_main, arg_dict, msg):
    """
    Verify that reorder_freqs throws appropriate warnings, all of which effectively
    warn of no-ops (so verify that the UVData object is unchanged).
    """
    with uvtest.check_warnings(UserWarning, msg):
        sma_mir.reorder_freqs(
            select_spw=arg_dict.get("selspw"),
            spw_order=arg_dict.get("spword"),
            channel_order=arg_dict.get("chord"),
        )

    assert sma_mir == sma_mir_main


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize(
    "sel_spw,spord,chord",
    [
        [[1, [1]], [None] * 2, ["freq"] * 2],
        [[None] * 2, ["number", "freq"], ["freq"] * 2],
        [[None] * 2, ["-number", "-freq"], ["-freq"] * 2],
    ],
)
def test_reorder_freqs_equal(sma_mir, future_shapes, sel_spw, spord, chord):
    if not future_shapes:
        sma_mir.use_current_array_shapes()

    # Create a dummy copy that we can muck with at will
    sma_mir_copy = sma_mir.copy()

    # Make sure that arrays and ints work for select_spw
    sma_mir.reorder_freqs(
        select_spw=sel_spw[0], spw_order=spord[0], channel_order=chord[0]
    )
    sma_mir_copy.reorder_freqs(
        select_spw=sel_spw[1], spw_order=spord[1], channel_order=chord[1]
    )
    assert sma_mir == sma_mir_copy


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_reorder_freqs_flipped(sma_mir, future_shapes):
    """
    Test that when sorting the data in ways that _should_ flip the frequency
    axis, that it actually does so.
    """
    if not future_shapes:
        sma_mir.use_current_array_shapes()

    # Make a copy
    sma_mir_copy = sma_mir.copy()

    # Order the data in opposite orientations -- note that for SMA, SPW numbers are
    # ordered in frequency order, so spw_order="number" _should_ be the same as
    # spw_order="freq"
    sma_mir.reorder_freqs(spw_order="number", channel_order="freq")
    sma_mir_copy.reorder_freqs(spw_order="-freq", channel_order="-freq")

    # This is kind of a sneaky test, but basically, there are 8 SPWs in the MIR
    # data file, with 6 pairs of windows partially overlapping. If all is correct,
    # then across the freq axis we should only see 6 instances of the freq
    # incrementing downwards/upwards for the two data sets.
    assert np.sum(np.diff(sma_mir.freq_array) < 0) == 6
    assert np.sum(np.diff(sma_mir_copy.freq_array) > 0) == 6

    # Check that the ordering of the spw_array makes sense
    assert np.all(sma_mir.spw_array == np.sort(sma_mir.spw_array))
    assert np.all(sma_mir_copy.spw_array == np.flip(np.sort(sma_mir.spw_array)))

    # Finally, lets make sure that the major freq arrays are flipped when sorting in
    # opposite directions
    assert np.all(np.flip(sma_mir.freq_array, axis=-1) == sma_mir_copy.freq_array)
    assert np.all(np.flip(sma_mir.data_array, axis=-2) == sma_mir_copy.data_array)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_reorder_freqs_eq_coeffs(casa_uvfits):
    # No test datasets to examine this with, so let's generate some mock data,
    # with a pre-determined order that we can flip
    casa_uvfits.reorder_freqs(channel_order="-freq")
    casa_uvfits.eq_coeffs = np.tile(
        np.arange(casa_uvfits.Nfreqs, dtype=float), (casa_uvfits.Nants_telescope, 1)
    )
    # modify the channel widths so we can check them too
    casa_uvfits.channel_width += np.arange(casa_uvfits.Nfreqs, dtype=float)
    casa_uvfits.reorder_freqs(channel_order="freq")
    assert np.all(np.diff(casa_uvfits.eq_coeffs, axis=1) == -1)
    assert np.all(np.diff(casa_uvfits.channel_width) == -1)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_sum_vis(casa_uvfits, future_shapes):
    # check sum_vis
    uv_full = casa_uvfits

    if not future_shapes:
        uv_full.use_current_array_shapes()

    uv_half = uv_full.copy()
    uv_half.data_array = uv_full.data_array / 2
    uv_half_mod = uv_half.copy()
    uv_half_mod.history += " testing the history. "
    uv_summed = uv_half.sum_vis(uv_half_mod)

    assert np.array_equal(uv_summed.data_array, uv_full.data_array)
    assert uvutils._check_histories(
        uv_half.history + " Visibilities summed using pyuvdata. Unique part of second "
        "object history follows.  testing the history.",
        uv_summed.history,
    )
    # add a test for full coverage of _combine_history_addition function
    assert (
        uvutils._combine_history_addition(
            uv_half.history
            + " Visibilities summed using pyuvdata. Unique part of second "
            "object history follows.  testing the history.",
            uv_summed.history,
        )
        is None
    )

    uv_summed = uv_half.sum_vis(uv_half_mod, verbose_history=True)

    assert np.array_equal(uv_summed.data_array, uv_full.data_array)
    assert uvutils._check_histories(
        uv_half.history + " Visibilities summed using pyuvdata. Second object history "
        "follows. " + uv_half_mod.history,
        uv_summed.history,
    )

    # check diff_vis
    uv_diffed = uv_full.diff_vis(uv_half)

    assert np.array_equal(uv_diffed.data_array, uv_half.data_array)
    assert uvutils._check_histories(
        uv_full.history + " Visibilities differenced using pyuvdata.", uv_diffed.history
    )

    # check in place
    uv_summed.diff_vis(uv_half, inplace=True)
    assert np.array_equal(uv_summed.data_array, uv_half.data_array)

    # check extra_keywords handling
    uv_keys = uv_full.copy()
    uv_keys.extra_keywords["test_key"] = "test_value"
    uv_keys.extra_keywords["SPECSYS"] = "altered_value"
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "Keyword SPECSYS in _extra_keywords is different in the two objects. "
            "Taking the first object's entry.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_merged_keys = uv_keys.sum_vis(uv_full)
    assert uv_merged_keys.extra_keywords["test_key"] == "test_value"
    assert uv_merged_keys.extra_keywords["SPECSYS"] == "altered_value"

    # check override_params
    uv_overrides = uv_full.copy()
    uv_overrides.instrument = "test_telescope"
    uv_overrides.telescope_location = [
        -1601183.15377712,
        -5042003.74810822,
        3554841.17192104,
    ]
    uv_overrides_2 = uv_overrides.sum_vis(
        uv_full, override_params=["instrument", "telescope_location"]
    )

    assert uv_overrides_2.instrument == "test_telescope"
    assert uv_overrides_2.telescope_location == [
        -1601183.15377712,
        -5042003.74810822,
        3554841.17192104,
    ]


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "attr_to_get,attr_to_set,arg_dict,msg",
    [
        [["use_current_array_shapes"], {}, {}, "Both objects must have the same `futu"],
        [[], {}, {"override": ["fake"]}, "Provided parameter fake is not a recogniza"],
        [[], {"__class__": UVCal}, {}, "Only UVData (or subclass) objects can be"],
        [[], {"instrument": "foo"}, {"inplace": True}, "UVParameter instrument does"],
    ],
)
def test_sum_vis_errors(uv1_2_set_uvws, attr_to_get, attr_to_set, arg_dict, msg):
    uv1, uv2 = uv1_2_set_uvws
    for method in attr_to_get:
        getattr(uv2, method)()
    for attr in attr_to_set.keys():
        setattr(uv2, attr, attr_to_set[attr])

    with pytest.raises(ValueError, match=re.escape(msg)):
        uv1.sum_vis(
            uv2,
            override_params=arg_dict.get("override"),
            inplace=arg_dict.get("inplace"),
        )


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_add(casa_uvfits, hera_uvh5_xx, future_shapes):
    uv_full = casa_uvfits

    if not future_shapes:
        uv_full.use_current_array_shapes()

    # Add frequencies
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1 += uv2
    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis "
        "using pyuvdata.",
        uv1.history,
    )

    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add frequencies - out of order
    # also remove flex_spw_id_array from one to check handling
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.flex_spw_id_array = None
    with uvtest.check_warnings(
        [DeprecationWarning] + [UserWarning] * 4,
        match=[
            "flex_spw_id_array is not set. It will be required starting in version 3.0",
            "One object has the flex_spw_id_array set and one does not. Combined "
            "object will have it set.",
        ]
        + ["The uvw_array does not match the expected values"] * 3,
    ):
        uv2 += uv1
    uv2.history = uv_full.history
    assert uv2 == uv_full

    # Add polarizations
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[2:4])
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific polarizations using pyuvdata. "
        "Combined data along polarization axis "
        "using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add polarizations - out of order
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[2:4])
    uv2 += uv1
    uv2.history = uv_full.history
    assert uv2 == uv_full

    # Add times
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 2])
    uv2.select(times=times[len(times) // 2 :])
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times using pyuvdata. "
        "Combined data along baseline-time axis "
        "using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add baselines
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    ant_list = list(range(15))  # Roughly half the antennas in the data
    # All blts where ant_1 is in list
    ind1 = [i for i in range(uv1.Nblts) if uv1.ant_1_array[i] in ant_list]
    ind2 = [i for i in range(uv1.Nblts) if uv1.ant_1_array[i] not in ant_list]
    uv1.select(blt_inds=ind1)
    uv2.select(blt_inds=ind2)
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific baseline-times using pyuvdata. "
        "Combined data along baseline-time axis "
        "using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add baselines - out of order
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv3 = uv_full.copy()
    ants = uv_full.get_ants()
    ants1 = ants[0:6]
    ants2 = ants[6:12]
    ants3 = ants[12:]

    # All blts where ant_1 is in list
    ind1 = [i for i in range(uv1.Nblts) if uv1.ant_1_array[i] in ants1]
    ind2 = [i for i in range(uv2.Nblts) if uv2.ant_1_array[i] in ants2]
    ind3 = [i for i in range(uv3.Nblts) if uv3.ant_1_array[i] in ants3]
    uv1.select(blt_inds=ind1)
    uv2.select(blt_inds=ind2)
    uv3.select(blt_inds=ind3)
    uv3.data_array = uv3.data_array[-1::-1]
    uv3.nsample_array = uv3.nsample_array[-1::-1]
    uv3.flag_array = uv3.flag_array[-1::-1]
    uv3.uvw_array = uv3.uvw_array[-1::-1, :]
    uv3.time_array = uv3.time_array[-1::-1]
    uv3.lst_array = uv3.lst_array[-1::-1]
    uv3.ant_1_array = uv3.ant_1_array[-1::-1]
    uv3.ant_2_array = uv3.ant_2_array[-1::-1]
    uv3.baseline_array = uv3.baseline_array[-1::-1]
    uv1 += uv3
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific baseline-times using pyuvdata. "
        "Combined data along baseline-time axis "
        "using pyuvdata. Combined data along "
        "baseline-time axis using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add multiple axes
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv_ref = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(
        times=times[0 : len(times) // 2], polarizations=uv1.polarization_array[0:2]
    )
    uv2.select(
        times=times[len(times) // 2 :], polarizations=uv2.polarization_array[2:4]
    )
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times, polarizations using "
        "pyuvdata. Combined data along "
        "baseline-time, polarization axis "
        "using pyuvdata.",
        uv1.history,
    )
    blt_ind1 = np.array(
        [
            ind
            for ind in range(uv_full.Nblts)
            if uv_full.time_array[ind] in times[0 : len(times) // 2]
        ]
    )
    blt_ind2 = np.array(
        [
            ind
            for ind in range(uv_full.Nblts)
            if uv_full.time_array[ind] in times[len(times) // 2 :]
        ]
    )
    # Zero out missing data in reference object
    if future_shapes:
        uv_ref.data_array[blt_ind1, :, 2:] = 0.0
        uv_ref.nsample_array[blt_ind1, :, 2:] = 0.0
        uv_ref.flag_array[blt_ind1, :, 2:] = True
        uv_ref.data_array[blt_ind2, :, 0:2] = 0.0
        uv_ref.nsample_array[blt_ind2, :, 0:2] = 0.0
        uv_ref.flag_array[blt_ind2, :, 0:2] = True
    else:
        uv_ref.data_array[blt_ind1, :, :, 2:] = 0.0
        uv_ref.nsample_array[blt_ind1, :, :, 2:] = 0.0
        uv_ref.flag_array[blt_ind1, :, :, 2:] = True
        uv_ref.data_array[blt_ind2, :, :, 0:2] = 0.0
        uv_ref.nsample_array[blt_ind2, :, :, 0:2] = 0.0
        uv_ref.flag_array[blt_ind2, :, :, 0:2] = True
    uv1.history = uv_full.history
    assert uv1 == uv_ref

    # Another combo
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv_ref = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 2], freq_chans=np.arange(0, 32))
    uv2.select(times=times[len(times) // 2 :], freq_chans=np.arange(32, 64))
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times, frequencies using "
        "pyuvdata. Combined data along "
        "baseline-time, frequency axis using "
        "pyuvdata.",
        uv1.history,
    )
    blt_ind1 = np.array(
        [
            ind
            for ind in range(uv_full.Nblts)
            if uv_full.time_array[ind] in times[0 : len(times) // 2]
        ]
    )
    blt_ind2 = np.array(
        [
            ind
            for ind in range(uv_full.Nblts)
            if uv_full.time_array[ind] in times[len(times) // 2 :]
        ]
    )
    # Zero out missing data in reference object
    if future_shapes:
        uv_ref.data_array[blt_ind1, 32:, :] = 0.0
        uv_ref.nsample_array[blt_ind1, 32:, :] = 0.0
        uv_ref.flag_array[blt_ind1, 32:, :] = True
        uv_ref.data_array[blt_ind2, 0:32, :] = 0.0
        uv_ref.nsample_array[blt_ind2, 0:32, :] = 0.0
        uv_ref.flag_array[blt_ind2, 0:32, :] = True
    else:
        uv_ref.data_array[blt_ind1, :, 32:, :] = 0.0
        uv_ref.nsample_array[blt_ind1, :, 32:, :] = 0.0
        uv_ref.flag_array[blt_ind1, :, 32:, :] = True
        uv_ref.data_array[blt_ind2, :, 0:32, :] = 0.0
        uv_ref.nsample_array[blt_ind2, :, 0:32, :] = 0.0
        uv_ref.flag_array[blt_ind2, :, 0:32, :] = True
    uv1.history = uv_full.history
    assert uv1 == uv_ref

    # Add without inplace
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 2])
    uv2.select(times=times[len(times) // 2 :])
    uv1 = uv1 + uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times using pyuvdata. "
        "Combined data along baseline-time "
        "axis using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Check warnings
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(33, 64))
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "Combined frequencies are not evenly spaced",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv1.__add__(uv2)

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=[0])
    uv2.select(freq_chans=[3])
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "Combined frequencies are separated by more than their channel width.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv1.__iadd__(uv2)

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=[0])
    uv2.select(freq_chans=[1])
    uv2.freq_array += uv2._channel_width.tols[1] / 2.0
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv1.__iadd__(uv2)

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[3])
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv1.__iadd__(uv2)

    # Combining histories
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[2:4])
    uv2.history += " testing the history. AIPS WTSCAL = 1.0"
    uv_new = uv1 + uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to specific polarizations using pyuvdata. "
        "Combined data along polarization axis using pyuvdata. Unique part of next "
        "object history follows.  testing the history.",
        uv_new.history,
    )
    uv_new.history = uv_full.history
    assert uv_new == uv_full

    uv_new = uv1.__add__(uv2, verbose_history=True)
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to specific polarizations using pyuvdata. "
        "Combined data along polarization axis using pyuvdata. Next object history "
        "follows.  " + uv2.history,
        uv_new.history,
    )

    # test add of autocorr-only and crosscorr-only objects
    uv_full = hera_uvh5_xx
    bls = uv_full.get_antpairs()
    autos = [bl for bl in bls if bl[0] == bl[1]]
    cross = sorted(set(bls) - set(autos))
    uv_auto = uv_full.select(bls=autos, inplace=False)
    uv_cross = uv_full.select(bls=cross, inplace=False)
    uv1 = uv_auto + uv_cross
    assert uv1.Nbls == uv_auto.Nbls + uv_cross.Nbls
    uv2 = uv_cross + uv_auto
    assert uv2.Nbls == uv_auto.Nbls + uv_cross.Nbls


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_add_unprojected(casa_uvfits):
    uv_full = casa_uvfits
    uv_full.unproject_phase()

    # Add frequencies
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1 += uv2
    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency "
        "axis using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add polarizations
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[2:4])
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific polarizations using pyuvdata. "
        "Combined data along polarization "
        "axis using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add times
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 2])
    uv2.select(times=times[len(times) // 2 :])
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times using pyuvdata. "
        "Combined data along baseline-time "
        "axis using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add baselines
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    ant_list = list(range(15))  # Roughly half the antennas in the data
    # All blts where ant_1 is in list
    ind1 = [i for i in range(uv1.Nblts) if uv1.ant_1_array[i] in ant_list]
    ind2 = [i for i in range(uv1.Nblts) if uv1.ant_1_array[i] not in ant_list]
    uv1.select(blt_inds=ind1)
    uv2.select(blt_inds=ind2)
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific baseline-times using pyuvdata. "
        "Combined data along baseline-time "
        "axis using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add multiple axes
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv_ref = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(
        times=times[0 : len(times) // 2], polarizations=uv1.polarization_array[0:2]
    )
    uv2.select(
        times=times[len(times) // 2 :], polarizations=uv2.polarization_array[2:4]
    )
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times, polarizations using "
        "pyuvdata. Combined data along "
        "baseline-time, polarization "
        "axis using pyuvdata.",
        uv1.history,
    )
    blt_ind1 = np.array(
        [
            ind
            for ind in range(uv_full.Nblts)
            if uv_full.time_array[ind] in times[0 : len(times) // 2]
        ]
    )
    blt_ind2 = np.array(
        [
            ind
            for ind in range(uv_full.Nblts)
            if uv_full.time_array[ind] in times[len(times) // 2 :]
        ]
    )
    # Zero out missing data in reference object
    uv_ref.data_array[blt_ind1, :, 2:] = 0.0
    uv_ref.nsample_array[blt_ind1, :, 2:] = 0.0
    uv_ref.flag_array[blt_ind1, :, 2:] = True
    uv_ref.data_array[blt_ind2, :, 0:2] = 0.0
    uv_ref.nsample_array[blt_ind2, :, 0:2] = 0.0
    uv_ref.flag_array[blt_ind2, :, 0:2] = True
    uv1.history = uv_full.history
    assert uv1 == uv_ref

    # Another combo
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv_ref = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 2], freq_chans=np.arange(0, 32))
    uv2.select(times=times[len(times) // 2 :], freq_chans=np.arange(32, 64))
    uv1 += uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times, frequencies using "
        "pyuvdata. Combined data along "
        "baseline-time, frequency "
        "axis using pyuvdata.",
        uv1.history,
    )
    blt_ind1 = np.array(
        [
            ind
            for ind in range(uv_full.Nblts)
            if uv_full.time_array[ind] in times[0 : len(times) // 2]
        ]
    )
    blt_ind2 = np.array(
        [
            ind
            for ind in range(uv_full.Nblts)
            if uv_full.time_array[ind] in times[len(times) // 2 :]
        ]
    )
    # Zero out missing data in reference object
    uv_ref.data_array[blt_ind1, 32:, :] = 0.0
    uv_ref.nsample_array[blt_ind1, 32:, :] = 0.0
    uv_ref.flag_array[blt_ind1, 32:, :] = True
    uv_ref.data_array[blt_ind2, 0:32, :] = 0.0
    uv_ref.nsample_array[blt_ind2, 0:32, :] = 0.0
    uv_ref.flag_array[blt_ind2, 0:32, :] = True
    uv1.history = uv_full.history
    assert uv1 == uv_ref

    # Add without inplace
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 2])
    uv2.select(times=times[len(times) // 2 :])
    uv1 = uv1 + uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times using pyuvdata. "
        "Combined data along baseline-time "
        "axis using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Check warnings
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(33, 64))
    with uvtest.check_warnings(
        UserWarning, "Combined frequencies are not evenly spaced"
    ):
        uv1.__add__(uv2)

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=[0])
    uv2.select(freq_chans=[3])
    with uvtest.check_warnings(
        UserWarning,
        ["Combined frequencies are separated by more than their channel width"],
    ):
        uv1.__iadd__(uv2)

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[3])
    with uvtest.check_warnings(None, None):
        uv1.__iadd__(uv2)

    # Combining histories
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[2:4])
    uv2.history += " testing the history. AIPS WTSCAL = 1.0"
    uv_new = uv1 + uv2
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to specific polarizations using pyuvdata. "
        "Combined data along polarization axis using pyuvdata.  Unique part of next "
        "object history follows.  testing the history.",
        uv_new.history,
    )
    uv_new.history = uv_full.history
    assert uv_new == uv_full

    uv_new = uv1.__add__(uv2, verbose_history=True)
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to specific polarizations using pyuvdata. "
        "Combined data along polarization axis using pyuvdata. Next object history "
        "follows." + uv2.history,
        uv_new.history,
    )


def test_check_flex_spw_contiguous(sma_mir, casa_uvfits):
    """
    Verify that check_flex_spw_contiguous passes silently as expected when windows are
    contiguous.
    """
    sma_mir._check_flex_spw_contiguous()

    casa_uvfits._check_flex_spw_contiguous()


def test_check_flex_spw_contiguous_error(sma_mir):
    """
    Verify that check_flex_spw_contiguous errors as expected when windows are
    not contiguous.
    """
    sma_mir.flex_spw_id_array[0] = 1
    with pytest.raises(
        ValueError,
        match="Channels from different spectral windows are interspersed with "
        "one another, rather than being grouped together along the "
        "frequency axis. Most file formats do not support such "
        "non-grouping of data.",
    ):
        sma_mir._check_flex_spw_contiguous()


def test_check_flex_spw_contiguous_no_flex_spw(hera_uvh5):
    """
    Verify that with a non-flex-spw dataset, the _check_flex_spw_contiguous
    check returns as True.
    """
    hera_uvh5._check_flex_spw_contiguous()


@pytest.mark.parametrize(
    "chan_width,msg",
    [
        [np.arange(131072), "The frequencies are not evenly spaced"],
        [np.zeros(131072), "The frequencies are separated by more"],
    ],
)
def test_check_freq_spacing_flex_spw(sma_mir, chan_width, msg):
    """
    Verify that _check_freq_spacing works as expected with flex_spw data sets (throws
    an error if windows are not contiguous, otherwise no error raised).
    """
    sma_mir.channel_width = chan_width
    with pytest.raises(ValueError, match=msg):
        sma_mir._check_freq_spacing()


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:LST values stored in this file are not ")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize(
    "add_method,screen1,screen2",
    # All of these tests are passing bool arrays as selection screens for grabbing out
    # different frequency indexes.
    [
        [  # First-half of all windows vs last-half of all windows
            ["__add__", {}],
            np.arange(8 * 16384) < 4 * 16384,
            np.arange(8 * 16384) >= 4 * 16384,
        ],
        [  # First-half of all windows vs last 3/4ths of all windows (overlap flagged)
            ["__add__", {}],
            np.arange(8 * 16384) < 4 * 16384,
            np.arange(8 * 16384) >= 2 * 16384,
        ],
        [  # Last vs first half of channels in each window, across all windows
            ["__add__", {}],
            np.mod(np.arange(8 * 16384), 16384) >= 8192,
            np.mod(np.arange(8 * 16384), 16384) < 8192,
        ],
        [  # fast-concat w/ first vs last half of all windows
            ["fast_concat", {"axis": "freq"}],
            np.arange(8 * 16384) < 4 * 16384,
            np.arange(8 * 16384) >= 4 * 16384,
        ],
        [  # First window, first half vs last half of all channels
            ["__add__", {}],
            np.arange(8 * 16384) < 8192,
            np.logical_and(np.arange(8 * 16384) >= 8192, np.arange(8 * 16384) < 16384),
        ],
        [  # First half of first spw vs full first window (overlap flagged)
            ["__add__", {}],
            np.arange(8 * 16384) < 8192,
            np.arange(8 * 16384) < 16384,
        ],
        [  # Fast concat with first window, first half vs last half of all channels
            ["fast_concat", {"axis": "freq"}],
            np.arange(8 * 16384) < 8192,
            np.logical_and(np.arange(8 * 16384) >= 8192, np.arange(8 * 16384) < 16384),
        ],
    ],
)
def test_flex_spw_add_concat(sma_mir, future_shapes, add_method, screen1, screen2):
    """
    Test add & fast concat with flexible spws using Mir file.

    Read in Mir files using flexible spectral windows, all of the same nchan
    """
    if not future_shapes:
        sma_mir.use_current_array_shapes()

    uv1 = sma_mir.select(freq_chans=np.where(screen1), inplace=False)
    uv2 = sma_mir.select(freq_chans=np.where(screen2), inplace=False)

    if np.any(np.logical_and(screen1, screen2)):
        flag_screen = screen2[screen1]
        if future_shapes:
            uv1.data_array[:, flag_screen] = 0.0
            uv1.flag_array[:, flag_screen] = True
        else:
            uv1.data_array[:, :, flag_screen] = 0.0
            uv1.flag_array[:, :, flag_screen] = True

    uv_recomb = getattr(uv1, add_method[0])(uv2, **add_method[1])

    if np.any(~np.logical_or(screen1, screen2)):
        sma_mir.select(freq_chans=np.where(np.logical_or(screen1, screen2)))

    # Make sure the two datasets are in the same frequency order
    uv_recomb.reorder_freqs(
        spw_order=np.argsort(uv_recomb.spw_array), channel_order="freq"
    )
    sma_mir.reorder_freqs(spw_order=np.argsort(sma_mir.spw_array), channel_order="freq")

    # Check the history first
    assert uv_recomb.history.startswith(sma_mir.history)
    sma_mir.history = uv_recomb.history
    assert uv_recomb == sma_mir


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "attr_to_set,attr_to_get,msg",
    [
        [[], [], "These objects have overlapping data and cannot be combined."],
        [[["__class__", UVCal]], [], "Only UVData"],
        [[], [["use_current_array_shapes", {}]], "Both objects must have the same `fu"],
        [
            [],
            [["unproject_phase", {}], ["select", {"freq_chans": np.arange(32, 64)}]],
            "UVParameter phase_center_catalog does not match. Cannot combine objects.",
        ],
        [
            [["vis_units", "Jy"]],
            [["select", {"freq_chans": np.arange(32, 64)}]],
            "UVParameter vis_units does not match. Cannot combine objects.",
        ],
        [
            [["integration_time", np.zeros(1360)]],
            [["select", {"freq_chans": np.arange(32, 64)}]],
            "UVParameter integration_time does not match.",
        ],
        [
            [["channel_width", np.ones(64)], ["flex_spw_id_array", np.array([0] * 64)]],
            [["_set_flex_spw", {}], ["select", {"freq_chans": np.arange(32, 64)}]],
            "To combine these data, flex_spw must be set",
        ],
    ],
)
def test_break_add(casa_uvfits, attr_to_set, attr_to_get, msg):
    """
    Verify that the add function throws errors appropriately when trying to combine
    objects that cannot be combined.
    """
    # Test failure modes of add function
    uv1 = casa_uvfits
    uv2 = uv1.copy()
    uv1.select(freq_chans=np.arange(0, 32))

    for item in attr_to_set:
        setattr(uv2, item[0], item[1])

    for item in attr_to_get:
        getattr(uv2, item[0])(**item[1])
    with pytest.raises(ValueError, match=msg):
        uv1 += uv2


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:The `unphase_to_drift` option is deprecated")
@pytest.mark.filterwarnings("ignore:The `phase_center_radec` parameter is deprecated")
@pytest.mark.parametrize(
    "test_func,extra_kwargs", [("__add__", {}), ("fast_concat", {"axis": "blt"})]
)
def test_add_error_drift_and_rephase(casa_uvfits, test_func, extra_kwargs):
    uv_full = casa_uvfits

    with pytest.raises(
        ValueError,
        match="phase_center_radec cannot be set if unphase_to_drift is True.",
    ):
        getattr(uv_full, test_func)(
            uv_full, phase_center_radec=(0, 45), unphase_to_drift=True, **extra_kwargs
        )


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "test_func,extra_kwargs", [("__add__", {}), ("fast_concat", {"axis": "blt"})]
)
def test_add_this_phased_unproject_phase(uv_phase_time_split, test_func, extra_kwargs):
    (uv_phase_1, _, _, _, uv_raw_2, uv_raw) = uv_phase_time_split
    func_kwargs = {"unphase_to_drift": True, "inplace": False, "ignore_name": True}
    func_kwargs.update(extra_kwargs)
    with uvtest.check_warnings(
        [DeprecationWarning, UserWarning],
        [
            "The `unphase_to_drift` option is deprecated",
            "Unprojecting this UVData object.",
        ],
    ):
        uv_out = getattr(uv_phase_1, test_func)(uv_raw_2, **func_kwargs)
    assert uv_out.Nphase == 1
    # the histories will be different here
    # but everything else should match.
    uv_out.history = copy.deepcopy(uv_raw.history)
    # ensure baseline time order is the same
    # because fast_concat will not order for us
    uv_out.reorder_blts(order="time", minor_order="baseline")
    assert np.all(uv_out._check_for_cat_type("unprojected"))
    assert uv_out == uv_raw


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "test_func,extra_kwargs", [("__add__", {}), ("fast_concat", {"axis": "blt"})]
)
def test_add_other_phased_unproject_phase(uv_phase_time_split, test_func, extra_kwargs):
    (_, uv_phase_2, _, uv_raw_1, _, uv_raw) = uv_phase_time_split

    func_kwargs = {"unphase_to_drift": True, "inplace": False, "ignore_name": True}
    func_kwargs.update(extra_kwargs)
    with uvtest.check_warnings(
        [DeprecationWarning, UserWarning],
        [
            "The `unphase_to_drift` option is deprecated",
            "Unprojecting other UVData object.",
        ],
    ):
        uv_out = getattr(uv_raw_1, test_func)(uv_phase_2, **func_kwargs)

    # the histories will be different here
    # but everything else should match.
    uv_out.history = copy.deepcopy(uv_raw.history)
    # ensure baseline time order is the same
    # because fast_concat will not order for us
    uv_out.reorder_blts(order="time", minor_order="baseline")
    uv_out.rename_phase_center(0, new_name="zenith")
    assert np.all(uv_out._check_for_cat_type("unprojected"))
    assert uv_out == uv_raw


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    ["test_func", "extra_kwargs", "phase_kwargs"],
    [
        ("__add__", {}, {"dec": Angle("-30d").rad}),
        ("__add__", {}, {"frame": "fk4"}),
        ("fast_concat", {"axis": "blt"}, {"ra": Angle("30d").rad}),
        ("fast_concat", {"axis": "blt"}, {"type": "ephem"}),
    ],
)
@pytest.mark.parametrize("rephase_obj", ["this", "other"])
def test_add_rephase_new_phase_center(
    uv_phase_time_split, test_func, extra_kwargs, phase_kwargs, rephase_obj
):
    (_, _, _, uv_raw_1, uv_raw_2, uv_raw) = uv_phase_time_split

    phase_center_radec = [Angle("0d").rad, Angle("0d").rad]
    if "ra" in phase_kwargs:
        phase_center_radec[0] = phase_kwargs["ra"]
    if "dec" in phase_kwargs:
        phase_center_radec[1] = phase_kwargs["dec"]

    rephase_frame = "icrs"
    if "frame" in phase_kwargs:
        final_frame = phase_kwargs["frame"]
    else:
        final_frame = rephase_frame

    # phase each half to different spots
    rephase_ra = Angle("30d").rad
    rephase_dec = Angle("30d").rad
    rephase_name = "bar"
    final_ra = phase_center_radec[0]
    final_dec = phase_center_radec[1]
    final_name = "foo"

    rephase_type = "sidereal"
    if "type" in phase_kwargs:
        final_type = phase_kwargs["type"]
        final_times = [np.min(uv_raw.time_array) * 0.9, np.max(uv_raw.time_array) * 1.1]
        final_ra = [final_ra] * 2
        final_dec = [final_dec] * 2
    else:
        final_type = rephase_type
        final_times = None

    if rephase_obj == "this":
        this_ra = rephase_ra
        this_dec = rephase_dec
        this_name = rephase_name
        this_frame = rephase_frame
        this_type = rephase_type
        this_times = None
        other_ra = final_ra
        other_dec = final_dec
        other_name = final_name
        other_frame = final_frame
        other_type = final_type
        other_times = final_times
    else:
        this_ra = final_ra
        this_dec = final_dec
        this_name = final_name
        this_frame = final_frame
        this_type = final_type
        this_times = final_times
        other_ra = rephase_ra
        other_dec = rephase_dec
        other_name = rephase_name
        other_frame = rephase_frame
        other_type = rephase_type
        other_times = None
    uv_raw_1.phase(
        ra=this_ra,
        dec=this_dec,
        use_ant_pos=True,
        cat_name=this_name,
        phase_frame=this_frame,
        cat_type=this_type,
        ephem_times=this_times,
    )
    uv_raw_2.phase(
        ra=other_ra,
        dec=other_dec,
        cat_name=other_name,
        use_ant_pos=True,
        phase_frame=other_frame,
        cat_type=other_type,
        ephem_times=other_times,
    )
    # phase original to phase_center_radec
    uv_raw.phase(
        ra=final_ra,
        dec=final_dec,
        cat_name=final_name,
        use_ant_pos=True,
        phase_frame=final_frame,
        cat_type=final_type,
        ephem_times=final_times,
    )

    warn_types = [DeprecationWarning] * 4 + [UserWarning]
    msg = [
        "The `use_ant_pos` option is deprecated",
        "The `phase_center_radec` parameter is deprecated",
        "The `phase_frame` parameter is deprecated",
        "The `orig_phase_frame` option is deprecated",
        f"Phasing {rephase_obj} UVData object to {phase_center_radec}",
    ]
    func_kwargs = {
        "inplace": False,
        "phase_center_radec": phase_center_radec,
        "use_ant_pos": True,
        "phase_frame": "icrs",
        "orig_phase_frame": "icrs",
    }
    if "frame" in phase_kwargs or "type" in phase_kwargs:
        if "frame" in phase_kwargs:
            func_kwargs["phase_frame"] = phase_kwargs["frame"]
        warn_types.append(UserWarning)
        msg.append("Phasing")
    func_kwargs.update(extra_kwargs)
    with uvtest.check_warnings(warn_types, match=msg):
        uv_out = getattr(uv_raw_1, test_func)(uv_raw_2, **func_kwargs)

    # the histories will be different here
    # but everything else should match.
    uv_out.history = copy.deepcopy(uv_raw.history)
    # ensure baseline time order is the same
    # because fast_concat will not order for us
    uv_out.reorder_blts(order="time", minor_order="baseline")

    if "type" in phase_kwargs:
        uv_out.phase(
            ra=final_ra,
            dec=final_dec,
            cat_name=final_name,
            use_ant_pos=True,
            phase_frame=final_frame,
            cat_type=final_type,
            ephem_times=final_times,
        )
    else:
        # update catalog to make them match
        if uv_out.Nphase == 2:
            uv_out.merge_phase_centers([0, 1], ignore_name=True)
        uv_out._consolidate_phase_center_catalogs(other=uv_raw, ignore_name=True)
        assert uv_out.Nphase == 1
        assert [
            uv_out.phase_center_catalog[1]["cat_lon"],
            uv_out.phase_center_catalog[1]["cat_lat"],
        ] == phase_center_radec
    assert uv_out == uv_raw


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "test_func,extra_kwargs", [("__add__", {}), ("fast_concat", {"axis": "blt"})]
)
def test_add_error_too_long_phase_center(uv_phase_time_split, test_func, extra_kwargs):
    (uv_phase_1, uv_phase_2, _, _, _, _) = uv_phase_time_split
    phase_center_radec = (Angle("0d").rad, Angle("-30d").rad, 7)
    func_kwargs = {"inplace": False, "phase_center_radec": phase_center_radec}
    func_kwargs.update(extra_kwargs)
    with pytest.raises(ValueError, match="phase_center_radec should have length 2."):
        with uvtest.check_warnings(
            DeprecationWarning, match="The `phase_center_radec` parameter is deprecated"
        ):
            getattr(uv_phase_1, test_func)(uv_phase_2, **func_kwargs)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_fast_concat(casa_uvfits, hera_uvh5_xx, future_shapes):
    uv_full = casa_uvfits

    if not future_shapes:
        uv_full.use_current_array_shapes()

    # Add frequencies
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv3 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 20))
    uv2.select(freq_chans=np.arange(20, 40))
    uv3.select(freq_chans=np.arange(40, 64))
    uv1.fast_concat([uv2, uv3], "freq", inplace=True)
    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis "
        "using pyuvdata.",
        uv1.history,
    )

    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add frequencies - out of order
    # also remove flex_spw_id_array from one to check handling
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv3 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 20))
    uv2.select(freq_chans=np.arange(20, 40))
    uv3.select(freq_chans=np.arange(40, 64))
    uv1.flex_spw_id_array = None
    with uvtest.check_warnings(
        [UserWarning] * 6 + [DeprecationWarning],
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions."
        ]
        * 4
        + [
            "Combined frequencies are not evenly spaced",
            "Some objects have the flex_spw_id_array set and some do not. Combined "
            "object will have it set.",
            "flex_spw_id_array is not set. It will be required starting in version 3.0",
        ],
    ):
        uv2.fast_concat([uv1, uv3], "freq", inplace=True)

    assert uv2.Nfreqs == uv_full.Nfreqs
    assert uv2._freq_array != uv_full._freq_array
    assert uv2._data_array != uv_full._data_array

    # reorder frequencies and test that they are equal
    if future_shapes:
        index_array = np.argsort(uv2.freq_array)
        uv2.freq_array = uv2.freq_array[index_array]
        uv2.data_array = uv2.data_array[:, index_array, :]
        uv2.nsample_array = uv2.nsample_array[:, index_array, :]
        uv2.flag_array = uv2.flag_array[:, index_array, :]
    else:
        index_array = np.argsort(uv2.freq_array[0, :])
        uv2.freq_array = uv2.freq_array[:, index_array]
        uv2.data_array = uv2.data_array[:, :, index_array, :]
        uv2.nsample_array = uv2.nsample_array[:, :, index_array, :]
        uv2.flag_array = uv2.flag_array[:, :, index_array, :]
    uv2.history = uv_full.history
    assert uv2._freq_array == uv_full._freq_array
    assert uv2 == uv_full

    # Add polarizations
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv3 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:1])
    uv2.select(polarizations=uv2.polarization_array[1:3])
    uv3.select(polarizations=uv3.polarization_array[3:4])
    uv1.fast_concat([uv2, uv3], "polarization", inplace=True)
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific polarizations using pyuvdata. "
        "Combined data along polarization axis "
        "using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add polarizations - out of order
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv3 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:1])
    uv2.select(polarizations=uv2.polarization_array[1:3])
    uv3.select(polarizations=uv3.polarization_array[3:4])
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions."
        ]
        * 4
        + ["Combined polarizations are not evenly spaced"],
    ):
        uv2.fast_concat([uv1, uv3], "polarization", inplace=True)

    assert uv2._polarization_array != uv_full._polarization_array
    assert uv2._data_array != uv_full._data_array

    # reorder pols
    uv2.reorder_pols()
    uv2.history = uv_full.history
    assert uv2 == uv_full

    # Add times
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv3 = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 3])
    uv2.select(times=times[len(times) // 3 : (len(times) // 3) * 2])
    uv3.select(times=times[(len(times) // 3) * 2 :])
    uv1.fast_concat([uv2, uv3], "blt", inplace=True)
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times using pyuvdata. "
        "Combined data along baseline-time axis "
        "using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Add baselines
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    # divide in half to keep in order
    ind1 = np.arange(uv1.Nblts // 2)
    ind2 = np.arange(uv1.Nblts // 2, uv1.Nblts)
    uv1.select(blt_inds=ind1)
    uv2.select(blt_inds=ind2)
    uv1.fast_concat(uv2, "blt", inplace=True)
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific baseline-times using pyuvdata. "
        "Combined data along baseline-time axis "
        "using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1, uv_full

    # Add baselines out of order
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(blt_inds=ind1)
    uv2.select(blt_inds=ind2)
    uv2.fast_concat(uv1, "blt", inplace=True)
    # test freq & pol arrays equal
    assert uv2._freq_array == uv_full._freq_array
    assert uv2._polarization_array == uv_full._polarization_array

    # test Nblt length arrays not equal but same shape
    assert uv2._ant_1_array != uv_full._ant_1_array
    assert uv2.ant_1_array.shape == uv_full.ant_1_array.shape
    assert uv2._ant_2_array != uv_full._ant_2_array
    assert uv2.ant_2_array.shape == uv_full.ant_2_array.shape
    assert uv2._uvw_array != uv_full._uvw_array
    assert uv2.uvw_array.shape == uv_full.uvw_array.shape
    assert uv2._time_array != uv_full._time_array
    assert uv2.time_array.shape == uv_full.time_array.shape
    assert uv2._baseline_array != uv_full._baseline_array
    assert uv2.baseline_array.shape == uv_full.baseline_array.shape
    assert uv2._data_array != uv_full._data_array
    assert uv2.data_array.shape == uv_full.data_array.shape

    # reorder blts to enable comparison
    uv2.reorder_blts()
    assert uv2.blt_order == ("time", "baseline")
    uv2.blt_order = None
    uv2.history = uv_full.history
    assert uv2 == uv_full

    # add baselines such that Nants_data needs to change
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    ant_list = list(range(15))  # Roughly half the antennas in the data
    # All blts where ant_1 is in list
    ind1 = [i for i in range(uv1.Nblts) if uv1.ant_1_array[i] in ant_list]
    ind2 = [i for i in range(uv1.Nblts) if uv1.ant_1_array[i] not in ant_list]
    uv1.select(blt_inds=ind1)
    uv2.select(blt_inds=ind2)
    uv2.fast_concat(uv1, "blt", inplace=True)

    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific baseline-times using pyuvdata. "
        "Combined data along baseline-time "
        "axis using pyuvdata.",
        uv2.history,
    )

    # test freq & pol arrays equal
    assert uv2._freq_array == uv_full._freq_array
    assert uv2._polarization_array == uv_full._polarization_array

    # test Nblt length arrays not equal but same shape
    assert uv2._ant_1_array != uv_full._ant_1_array
    assert uv2.ant_1_array.shape == uv_full.ant_1_array.shape
    assert uv2._ant_2_array != uv_full._ant_2_array
    assert uv2.ant_2_array.shape == uv_full.ant_2_array.shape
    assert uv2._uvw_array != uv_full._uvw_array
    assert uv2.uvw_array.shape == uv_full.uvw_array.shape
    assert uv2._time_array != uv_full._time_array
    assert uv2.time_array.shape == uv_full.time_array.shape
    assert uv2._baseline_array != uv_full._baseline_array
    assert uv2.baseline_array.shape == uv_full.baseline_array.shape
    assert uv2._data_array != uv_full._data_array
    assert uv2.data_array.shape == uv_full.data_array.shape

    # reorder blts to enable comparison
    uv2.reorder_blts()
    assert uv2.blt_order == ("time", "baseline")
    uv2.blt_order = None
    uv2.history = uv_full.history
    assert uv2 == uv_full

    # Add multiple axes
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(
        times=times[0 : len(times) // 2], polarizations=uv1.polarization_array[0:2]
    )
    uv2.select(
        times=times[len(times) // 2 :], polarizations=uv2.polarization_array[2:4]
    )
    with pytest.raises(
        ValueError,
        match="UVParameter polarization_array does not match. Cannot combine objects.",
    ):
        uv1.fast_concat(uv2, "blt", inplace=True)

    # Another combo
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 2], freq_chans=np.arange(0, 32))
    uv2.select(times=times[len(times) // 2 :], freq_chans=np.arange(32, 64))
    with pytest.raises(
        ValueError,
        match="UVParameter freq_array does not match. Cannot combine objects.",
    ):
        uv1.fast_concat(uv2, "blt", inplace=True)

    # Add without inplace
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    times = np.unique(uv_full.time_array)
    uv1.select(times=times[0 : len(times) // 2])
    uv2.select(times=times[len(times) // 2 :])
    uv1 = uv1.fast_concat(uv2, "blt", inplace=False)
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific times using pyuvdata. "
        "Combined data along baseline-time "
        "axis using pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history
    assert uv1 == uv_full

    # Check warnings
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(33, 64))
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "Combined frequencies are not evenly spaced",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv1.fast_concat(uv1, "freq", inplace=True)

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=[0])
    uv2.select(freq_chans=[3])
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "Combined frequencies are separated by more than their channel width",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv1.fast_concat(uv2, "freq")

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=[0])
    uv2.select(freq_chans=[1])
    uv2.freq_array += uv2._channel_width.tols[1] / 2.0
    with uvtest.check_warnings(
        UserWarning,
        "The uvw_array does not match the expected values given the antenna "
        "positions.",
        nwarnings=3,
    ):
        uv1.fast_concat(uv2, "freq")

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[3])
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "Combined polarizations are not evenly spaced",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv1.fast_concat(uv2, "polarization")

    # Combining histories
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(polarizations=uv1.polarization_array[0:2])
    uv2.select(polarizations=uv2.polarization_array[2:4])
    uv2.history += " testing the history. AIPS WTSCAL = 1.0"
    uv_new = uv1.fast_concat(uv2, "polarization")
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to specific polarizations using pyuvdata. "
        "Combined data along polarization axis using pyuvdata. Unique part of next "
        "object history follows. testing the history.",
        uv_new.history,
    )
    uv_new.history = uv_full.history
    assert uv_new == uv_full

    uv_new = uv1.fast_concat(uv2, "polarization", verbose_history=True)
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to specific polarizations using pyuvdata. "
        "Combined data along polarization axis using pyuvdata. Next object history "
        "follows." + uv2.history,
        uv_new.history,
    )

    # test add of autocorr-only and crosscorr-only objects
    uv_full = hera_uvh5_xx
    bls = uv_full.get_antpairs()
    autos = [bl for bl in bls if bl[0] == bl[1]]
    cross = sorted(set(bls) - set(autos))
    uv_auto = uv_full.select(bls=autos, inplace=False)
    uv_cross = uv_full.select(bls=cross, inplace=False)
    uv1 = uv_auto.fast_concat(uv_cross, "blt")
    assert uv1.Nbls == uv_auto.Nbls + uv_cross.Nbls
    uv2 = uv_cross.fast_concat(uv_auto, "blt")
    assert uv2.Nbls == uv_auto.Nbls + uv_cross.Nbls


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_fast_concat_errors(casa_uvfits):
    uv_full = casa_uvfits

    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    with pytest.raises(ValueError, match="Axis must be one of"):
        uv1.fast_concat(uv2, "foo", inplace=True)

    uv2.use_current_array_shapes()
    with pytest.raises(
        ValueError,
        match="All objects must have the same `future_array_shapes` parameter.",
    ):
        uv1.fast_concat(uv2, "freq", inplace=True)

    uv1.use_future_array_shapes()
    uv1._set_flex_spw()
    uv1.flex_spw_id_array = np.full(uv1.Nfreqs, uv1.spw_array[0], dtype=int)

    with pytest.raises(
        ValueError,
        match=re.escape(
            "All objects must have the same `flex_spw` value (True or False)."
        ),
    ):
        uv1.fast_concat(uv2, "freq", inplace=True)

    cal = UVCal()
    with pytest.raises(
        ValueError, match="Only UVData \\(or subclass\\) objects can be added"
    ):
        uv1.fast_concat(cal, "freq", inplace=True)


def test_key2inds(casa_uvfits):
    # Test function to interpret key as antpair, pol
    uv = casa_uvfits

    # Get an antpair/pol combo
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    pol = uv.polarization_array[0]
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    ind1, ind2, indp = uv._key2inds((ant1, ant2, pol))
    assert np.array_equal(bltind, ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal([0], indp[0])
    # Any of these inputs can also be a tuple of a tuple, so need to be checked twice.
    ind1, ind2, indp = uv._key2inds(((ant1, ant2, pol),))
    assert np.array_equal(bltind, ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal([0], indp[0])

    # Combo with pol as string
    ind1, ind2, indp = uv._key2inds((ant1, ant2, uvutils.polnum2str(pol)))
    assert np.array_equal([0], indp[0])
    ind1, ind2, indp = uv._key2inds(((ant1, ant2, uvutils.polnum2str(pol)),))
    assert np.array_equal([0], indp[0])

    # Check conjugation
    ind1, ind2, indp = uv._key2inds((ant2, ant1, pol))
    assert np.array_equal(bltind, ind2)
    assert np.array_equal(np.array([]), ind1)
    assert np.array_equal([0], indp[1])
    # Conjugation with pol as string
    ind1, ind2, indp = uv._key2inds((ant2, ant1, uvutils.polnum2str(pol)))
    assert np.array_equal(bltind, ind2)
    assert np.array_equal(np.array([]), ind1)
    assert np.array_equal([0], indp[1])
    assert np.array_equal([], indp[0])

    # Antpair only
    ind1, ind2, indp = uv._key2inds((ant1, ant2))
    assert np.array_equal(bltind, ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.arange(uv.Npols), indp[0])
    ind1, ind2, indp = uv._key2inds(((ant1, ant2)))
    assert np.array_equal(bltind, ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.arange(uv.Npols), indp[0])

    # Baseline number only
    ind1, ind2, indp = uv._key2inds(uv.antnums_to_baseline(ant1, ant2))
    assert np.array_equal(bltind, ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.arange(uv.Npols), indp[0])
    ind1, ind2, indp = uv._key2inds((uv.antnums_to_baseline(ant1, ant2),))
    assert np.array_equal(bltind, ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.arange(uv.Npols), indp[0])

    # Pol number only
    ind1, ind2, indp = uv._key2inds(pol)
    assert np.array_equal(np.arange(uv.Nblts), ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.array([0]), indp[0])
    ind1, ind2, indp = uv._key2inds((pol))
    assert np.array_equal(np.arange(uv.Nblts), ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.array([0]), indp[0])

    # Pol string only
    ind1, ind2, indp = uv._key2inds("LL")
    assert np.array_equal(np.arange(uv.Nblts), ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.array([1]), indp[0])
    ind1, ind2, indp = uv._key2inds(("LL"))
    assert np.array_equal(np.arange(uv.Nblts), ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.array([1]), indp[0])

    # Test invalid keys
    with pytest.raises(KeyError, match="Polarization I not found in data."):
        uv._key2inds("I")  # pol str not in data
    with pytest.raises(KeyError, match="Polarization -8 not found in data."):
        uv._key2inds(-8)  # pol num not in data
    with pytest.raises(KeyError, match="Baseline 6 not found in data."):
        uv._key2inds(6)  # bl num not in data
    with pytest.raises(KeyError, match=r"Antenna pair \(1, 1\) not found in data"):
        uv._key2inds((1, 1))  # ant pair not in data
    with pytest.raises(KeyError, match=r"Antenna pair \(1, 1\) not found in data"):
        uv._key2inds((1, 1, "rr"))  # ant pair not in data
    with pytest.raises(KeyError, match="Polarization xx not found in data."):
        uv._key2inds((1, 2, "xx"))  # pol not in data

    # Test autos are handled correctly
    uv.ant_2_array[0] = uv.ant_1_array[0]
    ind1, ind2, indp = uv._key2inds((ant1, ant1, pol))
    assert np.array_equal(ind1, [0])
    assert np.array_equal(ind2, [])


def test_key2inds_conj_all_pols(casa_uvfits):
    uv = casa_uvfits

    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    ind1, ind2, indp = uv._key2inds((ant2, ant1))

    # Pols in data are 'rr', 'll', 'rl', 'lr'
    # So conjugated order should be [0, 1, 3, 2]
    assert np.array_equal(bltind, ind2)
    assert np.array_equal(np.array([]), ind1)
    assert np.array_equal(np.array([]), indp[0])
    assert np.array_equal([0, 1, 3, 2], indp[1])


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_key2inds_conj_all_pols_fringe(casa_uvfits):
    uv = casa_uvfits

    uv.select(polarizations=["rl"])
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    # Mix one instance of this baseline.
    uv.ant_1_array[0] = ant2
    uv.ant_2_array[0] = ant1
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    ind1, ind2, indp = uv._key2inds((ant1, ant2))

    assert np.array_equal(bltind, ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.array([0]), indp[0])
    assert np.array_equal(np.array([]), indp[1])


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_key2inds_conj_all_pols_bl_fringe(casa_uvfits):
    uv = casa_uvfits

    uv.select(polarizations=["rl"])
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    # Mix one instance of this baseline.
    uv.ant_1_array[0] = ant2
    uv.ant_2_array[0] = ant1
    uv.baseline_array[0] = uvutils.antnums_to_baseline(ant2, ant1, uv.Nants_telescope)
    bl = uvutils.antnums_to_baseline(ant1, ant2, uv.Nants_telescope)
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    ind1, ind2, indp = uv._key2inds(bl)

    assert np.array_equal(bltind, ind1)
    assert np.array_equal(np.array([]), ind2)
    assert np.array_equal(np.array([0]), indp[0])
    assert np.array_equal(np.array([]), indp[1])


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_key2inds_conj_all_pols_missing_data(casa_uvfits):
    uv = casa_uvfits

    uv.select(polarizations=["rl"])
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]

    with pytest.raises(
        KeyError, match=r"Baseline \(8, 4\) not found for polarization array in data."
    ):
        uv._key2inds((ant2, ant1))


def test_key2inds_conj_all_pols_bls(casa_uvfits):
    uv = casa_uvfits

    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    bl = uvutils.antnums_to_baseline(ant2, ant1, uv.Nants_telescope)
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    ind1, ind2, indp = uv._key2inds(bl)

    # Pols in data are 'rr', 'll', 'rl', 'lr'
    # So conjugated order should be [0, 1, 3, 2]
    assert np.array_equal(bltind, ind2)
    assert np.array_equal(np.array([]), ind1)
    assert np.array_equal(np.array([]), indp[0])
    assert np.array_equal([0, 1, 3, 2], indp[1])


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_key2inds_conj_all_pols_missing_data_bls(casa_uvfits):
    uv = casa_uvfits
    uv.select(polarizations=["rl"])
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    bl = uvutils.antnums_to_baseline(ant2, ant1, uv.Nants_telescope)

    with pytest.raises(
        KeyError, match="Baseline 81924 not found for polarization array in data."
    ):
        uv._key2inds(bl)


def test_smart_slicing_err(casa_uvfits):
    """
    Test that smart_slicing throws an error when using an invald squeeze
    """
    # Test invalid squeeze
    with pytest.raises(
        ValueError,
        match='"notasqueeze" is not a valid option for squeeze.Only "default", "none", '
        'or "full" are allowed.',
    ):
        casa_uvfits._smart_slicing(
            casa_uvfits.data_array, [0, 4, 5], [], ([0, 1], []), squeeze="notasqueeze"
        )


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_smart_slicing(casa_uvfits, future_shapes):
    # Test function to slice data
    uv = casa_uvfits

    if not future_shapes:
        uv.use_current_array_shapes()

    # ind1 reg, ind2 empty, pol reg
    ind1 = 10 * np.arange(9)
    ind2 = []
    indp = [0, 1]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, []))
    dcheck = uv.data_array[ind1]
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)
    assert not d.flags.writeable
    # Ensure a view was returned
    if future_shapes:
        uv.data_array[ind1[1], 0, indp[0]] = 5.43
        assert d[1, 0, 0] == uv.data_array[ind1[1], 0, indp[0]]
    else:
        uv.data_array[ind1[1], 0, 0, indp[0]] = 5.43
        assert d[1, 0, 0] == uv.data_array[ind1[1], 0, 0, indp[0]]

    # force copy
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, []), force_copy=True)
    dcheck = uv.data_array[ind1]
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)
    assert d.flags.writeable
    # Ensure a copy was returned
    if future_shapes:
        uv.data_array[ind1[1], 0, indp[0]] = 4.3
        assert d[1, 0, 0] != uv.data_array[ind1[1], 0, indp[0]]
    else:
        uv.data_array[ind1[1], 0, 0, indp[0]] = 4.3
        assert d[1, 0, 0] != uv.data_array[ind1[1], 0, 0, indp[0]]

    # ind1 reg, ind2 empty, pol not reg
    ind1 = 10 * np.arange(9)
    ind2 = []
    indp = [0, 1, 3]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, []))
    dcheck = uv.data_array[ind1]
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)
    assert not d.flags.writeable
    # Ensure a copy was returned
    if future_shapes:
        uv.data_array[ind1[1], 0, indp[0]] = 1.2
        assert d[1, 0, 0] != uv.data_array[ind1[1], 0, indp[0]]
    else:
        uv.data_array[ind1[1], 0, 0, indp[0]] = 1.2
        assert d[1, 0, 0] != uv.data_array[ind1[1], 0, 0, indp[0]]

    # ind1 not reg, ind2 empty, pol reg
    ind1 = [0, 4, 5]
    ind2 = []
    indp = [0, 1]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, []))
    dcheck = uv.data_array[ind1]
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)
    assert not d.flags.writeable
    # Ensure a copy was returned
    if future_shapes:
        uv.data_array[ind1[1], 0, indp[0]] = 8.2
        assert d[1, 0, 0] != uv.data_array[ind1[1], 0, indp[0]]
    else:
        uv.data_array[ind1[1], 0, 0, indp[0]] = 8.2
        assert d[1, 0, 0] != uv.data_array[ind1[1], 0, 0, indp[0]]

    # ind1 not reg, ind2 empty, pol not reg
    ind1 = [0, 4, 5]
    ind2 = []
    indp = [0, 1, 3]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, []))
    dcheck = uv.data_array[ind1]
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)
    assert not d.flags.writeable
    # Ensure a copy was returned
    if future_shapes:
        uv.data_array[ind1[1], 0, indp[0]] = 3.4
        assert d[1, 0, 0] != uv.data_array[ind1[1], 0, indp[0]]
    else:
        uv.data_array[ind1[1], 0, 0, indp[0]] = 3.4
        assert d[1, 0, 0] != uv.data_array[ind1[1], 0, 0, indp[0]]

    # ind1 empty, ind2 reg, pol reg
    # Note conjugation test ensures the result is a copy, not a view.
    ind1 = []
    ind2 = 10 * np.arange(9)
    indp = [0, 1]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, ([], indp))
    dcheck = uv.data_array[ind2]
    if future_shapes:
        dcheck = np.squeeze(np.conj(dcheck[:, :, indp]))
    else:
        dcheck = np.squeeze(np.conj(dcheck[:, :, :, indp]))
    assert np.all(d == dcheck)

    # ind1 empty, ind2 reg, pol not reg
    ind1 = []
    ind2 = 10 * np.arange(9)
    indp = [0, 1, 3]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, ([], indp))
    dcheck = uv.data_array[ind2]
    if future_shapes:
        dcheck = np.squeeze(np.conj(dcheck[:, :, indp]))
    else:
        dcheck = np.squeeze(np.conj(dcheck[:, :, :, indp]))
    assert np.all(d == dcheck)

    # ind1 empty, ind2 not reg, pol reg
    ind1 = []
    ind2 = [1, 4, 5, 10]
    indp = [0, 1]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, ([], indp))
    dcheck = uv.data_array[ind2]
    if future_shapes:
        dcheck = np.squeeze(np.conj(dcheck[:, :, indp]))
    else:
        dcheck = np.squeeze(np.conj(dcheck[:, :, :, indp]))
    assert np.all(d == dcheck)

    # ind1 empty, ind2 not reg, pol not reg
    ind1 = []
    ind2 = [1, 4, 5, 10]
    indp = [0, 1, 3]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, ([], indp))
    dcheck = uv.data_array[ind2]
    if future_shapes:
        dcheck = np.squeeze(np.conj(dcheck[:, :, indp]))
    else:
        dcheck = np.squeeze(np.conj(dcheck[:, :, :, indp]))
    assert np.all(d == dcheck)

    # ind1, ind2 not empty, pol reg
    ind1 = np.arange(20)
    ind2 = np.arange(30, 40)
    indp = [0, 1]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, indp))
    dcheck = np.append(uv.data_array[ind1], np.conj(uv.data_array[ind2]), axis=0)
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)

    # ind1, ind2 not empty, pol not reg
    ind1 = np.arange(20)
    ind2 = np.arange(30, 40)
    indp = [0, 1, 3]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, indp))
    dcheck = np.append(uv.data_array[ind1], np.conj(uv.data_array[ind2]), axis=0)
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)

    # test single element
    ind1 = [45]
    ind2 = []
    indp = [0, 1]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, []))
    dcheck = uv.data_array[ind1]
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)

    # test single element
    ind1 = []
    ind2 = [45]
    indp = [0, 1]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, ([], indp))
    assert np.all(d == np.conj(dcheck))

    # Full squeeze
    ind1 = [45]
    ind2 = []
    indp = [0, 1]
    d = uv._smart_slicing(uv.data_array, ind1, ind2, (indp, []), squeeze="full")
    dcheck = uv.data_array[ind1]
    if future_shapes:
        dcheck = np.squeeze(dcheck[:, :, indp])
    else:
        dcheck = np.squeeze(dcheck[:, :, :, indp])
    assert np.all(d == dcheck)


def test_get_data(casa_uvfits):
    # Test get_data function for easy access to data
    uv = casa_uvfits

    # Get an antpair/pol combo
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    pol = uv.polarization_array[0]
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    dcheck = np.squeeze(uv.data_array[bltind, :, 0])
    d = uv.get_data(ant1, ant2, pol)
    assert np.all(dcheck == d)

    d = uv.get_data(ant1, ant2, uvutils.polnum2str(pol))
    assert np.all(dcheck == d)

    d = uv.get_data((ant1, ant2, pol))
    assert np.all(dcheck == d)

    with pytest.raises(ValueError, match="no more than 3 key values can be passed"):
        uv.get_data((ant1, ant2, pol), (ant1, ant2, pol))

    # Check conjugation
    d = uv.get_data(ant2, ant1, pol)
    assert np.all(dcheck == np.conj(d))

    # Check cross pol conjugation
    d = uv.get_data(ant2, ant1, uv.polarization_array[2])
    d1 = uv.get_data(ant1, ant2, uv.polarization_array[3])
    assert np.all(d == np.conj(d1))

    # Antpair only
    dcheck = np.squeeze(uv.data_array[bltind])
    d = uv.get_data(ant1, ant2)
    assert np.all(dcheck == d)

    # Pol number only
    dcheck = np.squeeze(uv.data_array[..., 0])
    d = uv.get_data(pol)
    assert np.all(dcheck == d)


def test_get_flags(casa_uvfits):
    # Test function for easy access to flags
    uv = casa_uvfits

    # Get an antpair/pol combo
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    pol = uv.polarization_array[0]
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    dcheck = np.squeeze(uv.flag_array[bltind, :, 0])
    d = uv.get_flags(ant1, ant2, pol)
    assert np.all(dcheck == d)

    d = uv.get_flags(ant1, ant2, uvutils.polnum2str(pol))
    assert np.all(dcheck == d)

    d = uv.get_flags((ant1, ant2, pol))
    assert np.all(dcheck == d)

    with pytest.raises(ValueError, match="no more than 3 key values can be passed"):
        uv.get_flags((ant1, ant2, pol), (ant1, ant2, pol))

    # Check conjugation
    d = uv.get_flags(ant2, ant1, pol)
    assert np.all(dcheck == d)
    assert d.dtype == np.bool_

    # Antpair only
    dcheck = np.squeeze(uv.flag_array[bltind])
    d = uv.get_flags(ant1, ant2)
    assert np.all(dcheck == d)

    # Pol number only
    dcheck = np.squeeze(uv.flag_array[..., 0])
    d = uv.get_flags(pol)
    assert np.all(dcheck == d)


def test_get_nsamples(casa_uvfits):
    # Test function for easy access to nsample array
    uv = casa_uvfits

    # Get an antpair/pol combo
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    pol = uv.polarization_array[0]
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    dcheck = np.squeeze(uv.nsample_array[bltind, :, 0])
    d = uv.get_nsamples(ant1, ant2, pol)
    assert np.all(dcheck == d)

    d = uv.get_nsamples(ant1, ant2, uvutils.polnum2str(pol))
    assert np.all(dcheck == d)

    d = uv.get_nsamples((ant1, ant2, pol))
    assert np.all(dcheck == d)

    with pytest.raises(ValueError, match="no more than 3 key values can be passed"):
        uv.get_nsamples((ant1, ant2, pol), (ant1, ant2, pol))

    # Check conjugation
    d = uv.get_nsamples(ant2, ant1, pol)
    assert np.all(dcheck == d)

    # Antpair only
    dcheck = np.squeeze(uv.nsample_array[bltind])
    d = uv.get_nsamples(ant1, ant2)
    assert np.all(dcheck == d)

    # Pol number only
    dcheck = np.squeeze(uv.nsample_array[..., 0])
    d = uv.get_nsamples(pol)
    assert np.all(dcheck == d)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_antpair2ind(paper_uvh5):
    # Test for baseline-time axis indexer
    uv = paper_uvh5

    # get indices
    inds = uv.antpair2ind(0, 1, ordered=False)
    # fmt: off
    np.testing.assert_array_equal(
        inds,
        np.array(
            [
                1, 22, 43, 64, 85, 106, 127, 148, 169,
                190, 211, 232, 253, 274, 295, 316, 337,
                358, 379
            ]
        )
    )
    # fmt: on
    assert np.issubdtype(inds.dtype, np.integer)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_antpair2ind_conj(paper_uvh5):
    # conjugate (and use key rather than arg expansion)
    uv = paper_uvh5
    inds = uv.antpair2ind(0, 1, ordered=False)
    inds2 = uv.antpair2ind((1, 0), ordered=False)
    np.testing.assert_array_equal(inds, inds2)
    assert np.issubdtype(inds2.dtype, np.integer)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_antpair2ind_ordered(paper_uvh5):
    # test ordered
    uv = paper_uvh5
    inds = uv.antpair2ind(0, 1, ordered=False)

    # make sure conjugated baseline returns nothing
    inds2 = uv.antpair2ind(1, 0, ordered=True)
    assert inds2.size == 0

    # now use baseline actually in data
    inds2 = uv.antpair2ind(0, 1, ordered=True)
    np.testing.assert_array_equal(inds, inds2)
    assert np.issubdtype(inds2.dtype, np.integer)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_antpair2ind_autos(paper_uvh5):
    # test autos w/ and w/o ordered
    uv = paper_uvh5

    inds = uv.antpair2ind(0, 0, ordered=True)
    inds2 = uv.antpair2ind(0, 0, ordered=False)
    np.testing.assert_array_equal(inds, inds2)
    assert np.issubdtype(inds.dtype, np.integer)
    assert np.issubdtype(inds2.dtype, np.integer)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_antpair2ind_exceptions(paper_uvh5):
    # test exceptions
    uv = paper_uvh5

    with pytest.raises(ValueError, match="antpair2ind must be fed an antpair tuple"):
        uv.antpair2ind(1)
    with pytest.raises(ValueError, match="antpair2ind must be fed an antpair tuple"):
        uv.antpair2ind("bar", "foo")
    with pytest.raises(ValueError, match="ordered must be a boolean"):
        uv.antpair2ind(0, 1, "foo")

    return


def test_get_times(casa_uvfits):
    # Test function for easy access to times, to work in conjunction with get_data
    uv = casa_uvfits
    # Get an antpair/pol combo (pol shouldn't actually effect result)
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    pol = uv.polarization_array[0]
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    dcheck = uv.time_array[bltind]
    d = uv.get_times(ant1, ant2, pol)
    assert np.all(dcheck == d)

    d = uv.get_times(ant1, ant2, uvutils.polnum2str(pol))
    assert np.all(dcheck == d)

    d = uv.get_times((ant1, ant2, pol))
    assert np.all(dcheck == d)

    with pytest.raises(ValueError, match="no more than 3 key values can be passed"):
        uv.get_times((ant1, ant2, pol), (ant1, ant2, pol))

    # Check conjugation
    d = uv.get_times(ant2, ant1, pol)
    assert np.all(dcheck == d)

    # Antpair only
    d = uv.get_times(ant1, ant2)
    assert np.all(dcheck == d)

    # Pol number only
    d = uv.get_times(pol)
    assert np.all(d == uv.time_array)


def test_get_lsts(casa_uvfits):
    # Test function for easy access to LSTs, to work in conjunction with get_data
    uv = casa_uvfits
    # Get an antpair/pol combo (pol shouldn't actually effect result)
    ant1 = uv.ant_1_array[0]
    ant2 = uv.ant_2_array[0]
    pol = uv.polarization_array[0]
    bltind = np.where((uv.ant_1_array == ant1) & (uv.ant_2_array == ant2))[0]
    dcheck = uv.lst_array[bltind]
    d = uv.get_lsts(ant1, ant2, pol)
    assert np.all(dcheck == d)

    d = uv.get_lsts(ant1, ant2, uvutils.polnum2str(pol))
    assert np.all(dcheck == d)

    d = uv.get_lsts((ant1, ant2, pol))
    assert np.all(dcheck == d)

    with pytest.raises(ValueError, match="no more than 3 key values can be passed"):
        uv.get_lsts((ant1, ant2, pol), (ant1, ant2, pol))

    # Check conjugation
    d = uv.get_lsts(ant2, ant1, pol)
    assert np.all(dcheck == d)

    # Antpair only
    d = uv.get_lsts(ant1, ant2)
    assert np.all(dcheck == d)

    # Pol number only
    d = uv.get_lsts(pol)
    assert np.all(d == uv.lst_array)


def test_antpairpol_iter(casa_uvfits):
    # Test generator
    uv = casa_uvfits
    pol_dict = {
        uvutils.polnum2str(uv.polarization_array[i]): i for i in range(uv.Npols)
    }
    keys = []
    pols = set()
    bls = set()
    for key, d in uv.antpairpol_iter():
        keys += key
        bl = uv.antnums_to_baseline(key[0], key[1])
        blind = np.where(uv.baseline_array == bl)[0]
        bls.add(bl)
        pols.add(key[2])
        dcheck = np.squeeze(uv.data_array[blind, :, pol_dict[key[2]]])
        assert np.all(dcheck == d)
    assert len(bls) == len(uv.get_baseline_nums())
    assert len(pols) == uv.Npols


def test_get_ants(casa_uvfits):
    # Test function to get unique antennas in data
    uv = casa_uvfits

    ants = uv.get_ants()
    for ant in ants:
        assert (ant in uv.ant_1_array) or (ant in uv.ant_2_array)
    for ant in uv.ant_1_array:
        assert ant in ants
    for ant in uv.ant_2_array:
        assert ant in ants


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_get_enu_antpos(hera_uvh5_xx):
    uvd = hera_uvh5_xx
    # no center, no pick data ants
    antpos, ants = uvd.get_ENU_antpos(center=False, pick_data_ants=False)
    assert len(ants) == 113
    assert np.isclose(antpos[0, 0], 19.340211050751535)
    assert ants[0] == 0
    # test default behavior
    antpos2, ants = uvd.get_ENU_antpos()

    assert np.all(antpos == antpos2)
    # center
    antpos, ants = uvd.get_ENU_antpos(center=True, pick_data_ants=False)
    assert np.isclose(antpos[0, 0], 22.472442651767714)
    # pick data ants
    antpos, ants = uvd.get_ENU_antpos(center=True, pick_data_ants=True)
    assert ants[0] == 9
    assert np.isclose(antpos[0, 0], -0.0026981323386223721)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_telescope_loc_xyz_check(paper_uvh5, tmp_path):
    # test that improper telescope locations can still be read
    uv = paper_uvh5
    uv.telescope_location = uvutils.XYZ_from_LatLonAlt(*uv.telescope_location)
    # fix LST values
    uv.set_lsts_from_time_array()
    fname = str(tmp_path / "test.uvh5")
    uv.write_uvh5(fname, run_check=False, check_extra=False, clobber=True)

    # try to read file without checks (passing is implicit)
    uv.read(fname, run_check=False, use_future_array_shapes=True)

    # try to read without checks: assert it fails
    with pytest.raises(
        ValueError, match="UVParameter _telescope_location has unacceptable values."
    ):
        uv.read(fname, use_future_array_shapes=True)


def test_get_pols(casa_uvfits):
    # Test function to get unique polarizations in string format
    uv = casa_uvfits
    pols = uv.get_pols()
    pols_data = ["rr", "ll", "lr", "rl"]
    assert sorted(pols) == sorted(pols_data)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_get_pols_x_orientation(paper_uvh5):
    uv_in = paper_uvh5

    uv_in.x_orientation = "east"

    pols = uv_in.get_pols()
    pols_data = ["en"]
    assert pols == pols_data

    uv_in.x_orientation = "north"

    pols = uv_in.get_pols()
    pols_data = ["ne"]
    assert pols == pols_data


def test_get_feedpols(casa_uvfits):
    # Test function to get unique antenna feed polarizations in data. String format.
    uv = casa_uvfits
    pols = uv.get_feedpols()
    pols_data = ["r", "l"]
    assert sorted(pols) == sorted(pols_data)

    # Test break when pseudo-Stokes visibilities are present
    uv.polarization_array[0] = 1  # pseudo-Stokes I
    with pytest.raises(
        ValueError,
        match="Pseudo-Stokes visibilities cannot be interpreted as feed polarizations",
    ):
        uv.get_feedpols()


def test_parse_ants(casa_uvfits, hera_uvh5_xx):
    # Test function to get correct antenna pairs and polarizations
    uv = casa_uvfits

    # All baselines
    ant_str = "all"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    assert isinstance(ant_pairs_nums, type(None))
    assert isinstance(polarizations, type(None))

    # Auto correlations
    ant_str = "auto"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    assert Counter(ant_pairs_nums) == Counter([])
    assert isinstance(polarizations, type(None))

    # Cross correlations
    ant_str = "cross"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    assert Counter(uv.get_antpairs()) == Counter(ant_pairs_nums)
    assert isinstance(polarizations, type(None))

    # pseudo-Stokes params
    ant_str = "pI,pq,pU,pv"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    pols_expected = [4, 3, 2, 1]
    assert isinstance(ant_pairs_nums, type(None))
    assert Counter(polarizations) == Counter(pols_expected)

    # Unparsible string
    ant_str = "none"
    with pytest.raises(ValueError, match="Unparsible argument none"):
        uv.parse_ants(ant_str)

    # Single antenna number
    ant_str = "1"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    # fmt: off
    ant_pairs_expected = [(1, 2), (1, 3), (1, 4), (1, 7), (1, 8), (1, 9),
                          (1, 12), (1, 15), (1, 19), (1, 20), (1, 21),
                          (1, 22), (1, 23), (1, 24), (1, 25), (1, 27),
                          (1, 28)]
    # fmt: on
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Single antenna number not in the data
    ant_str = "10"
    with uvtest.check_warnings(
        UserWarning,
        "Warning: Antenna number 10 passed, but not present in the ant_1_array "
        "or ant_2_array",
    ):
        ant_pairs_nums, polarizations = uv.parse_ants(ant_str)

    assert isinstance(ant_pairs_nums, type(None))
    assert isinstance(polarizations, type(None))

    # Single antenna number with polarization, both not in the data
    ant_str = "10x"
    with uvtest.check_warnings(
        UserWarning,
        [
            "Warning: Antenna number 10 passed, but not present in the ant_1_array or "
            "ant_2_array",
            "Warning: Polarization XX,XY is not present in the polarization_array",
        ],
    ):
        ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    assert isinstance(ant_pairs_nums, type(None))
    assert isinstance(polarizations, type(None))

    # Multiple antenna numbers as list
    ant_str = "22,27"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    # fmt: off
    ant_pairs_expected = [(1, 22), (2, 22), (3, 22), (4, 22), (7, 22), (8, 22),
                          (9, 22), (12, 22), (15, 22), (19, 22), (20, 22),
                          (21, 22), (22, 23), (22, 24), (22, 25), (22, 27),
                          (22, 28), (1, 27), (2, 27), (3, 27), (4, 27),
                          (7, 27), (8, 27), (9, 27), (12, 27), (15, 27),
                          (19, 27), (20, 27), (21, 27), (23, 27),
                          (24, 27), (25, 27), (27, 28)]
    # fmt: on
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Single baseline
    ant_str = "1_3"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 3)]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Single baseline with polarization
    ant_str = "1l_3r"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 3)]
    pols_expected = [-4]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Single baseline with single polarization in first entry
    ant_str = "1l_3,2x_3"
    with uvtest.check_warnings(
        UserWarning,
        "Warning: Polarization XX,XY is not present in the polarization_array",
    ):
        ant_pairs_nums, polarizations = uv.parse_ants(ant_str)

    ant_pairs_expected = [(1, 3), (2, 3)]
    pols_expected = [-2, -4]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Single baseline with single polarization in last entry
    ant_str = "1_3l,2_3x"
    with uvtest.check_warnings(
        UserWarning,
        "Warning: Polarization XX,YX is not present in the polarization_array",
    ):
        ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 3), (2, 3)]
    pols_expected = [-2, -3]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Multiple baselines as list
    ant_str = "1_2,1_3,1_12"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 2), (1, 3), (1, 12)]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Multiples baselines with polarizations as list
    ant_str = "1r_2l,1l_3l,1r_12r"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 2), (1, 3), (1, 12)]
    pols_expected = [-1, -2, -3]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Specific baselines with parenthesis
    ant_str = "(1,3)_12"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 12), (3, 12)]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Specific baselines with parenthesis
    ant_str = "1_(3,12)"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 3), (1, 12)]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Antenna numbers with polarizations
    ant_str = "(1l,2r)_(3l,7r)"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 3), (1, 7), (2, 3), (2, 7)]
    pols_expected = [-1, -2, -3, -4]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Antenna numbers with - for avoidance
    ant_str = "1_(-3,12)"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 12)]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Remove specific antenna number
    ant_str = "1,-3"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [
        (1, 2),
        (1, 4),
        (1, 7),
        (1, 8),
        (1, 9),
        (1, 12),
        (1, 15),
        (1, 19),
        (1, 20),
        (1, 21),
        (1, 22),
        (1, 23),
        (1, 24),
        (1, 25),
        (1, 27),
        (1, 28),
    ]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Remove specific baseline (same expected antenna pairs as above example)
    ant_str = "1,-1_3"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Antenna numbers with polarizations and - for avoidance
    ant_str = "1l_(-3r,12l)"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 12)]
    pols_expected = [-2]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Antenna numbers and pseudo-Stokes parameters
    ant_str = "(1l,2r)_(3l,7r),pI,pq"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 3), (1, 7), (2, 3), (2, 7)]
    pols_expected = [2, 1, -1, -2, -3, -4]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Multiple baselines with multiple polarizations, one pol to be removed
    ant_str = "1l_2,1l_3,-1l_3r"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 2), (1, 3)]
    pols_expected = [-2]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Multiple baselines with multiple polarizations, one pol (not in data)
    # to be removed
    ant_str = "1l_2,1l_3,-1x_3y"
    with uvtest.check_warnings(
        UserWarning, "Warning: Polarization XY is not present in the polarization_array"
    ):
        ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = [(1, 2), (1, 3)]
    pols_expected = [-2, -4]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Test print toggle on single baseline with polarization
    ant_str = "1l_2l"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str, print_toggle=True)
    ant_pairs_expected = [(1, 2)]
    pols_expected = [-2]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert Counter(polarizations) == Counter(pols_expected)

    # Test ant_str='auto' on file with auto correlations
    uv = hera_uvh5_xx

    ant_str = "auto"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_nums = [
        9,
        10,
        20,
        22,
        31,
        43,
        53,
        64,
        65,
        72,
        80,
        81,
        88,
        89,
        96,
        97,
        104,
        105,
        112,
    ]
    ant_pairs_autos = [(ant_i, ant_i) for ant_i in ant_nums]
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_autos)
    assert isinstance(polarizations, type(None))

    # Test cross correlation extraction on data with auto + cross
    ant_str = "cross"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_cross = list(itertools.combinations(ant_nums, 2))
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_cross)
    assert isinstance(polarizations, type(None))

    # Remove only polarization of single baseline
    ant_str = "all,-9x_10x"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = ant_pairs_autos + ant_pairs_cross
    ant_pairs_expected.remove((9, 10))
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))

    # Test appending all to beginning of strings that start with -
    ant_str = "-9"
    ant_pairs_nums, polarizations = uv.parse_ants(ant_str)
    ant_pairs_expected = ant_pairs_autos + ant_pairs_cross
    for ant_i in ant_nums:
        ant_pairs_expected.remove((9, ant_i))
    assert Counter(ant_pairs_nums) == Counter(ant_pairs_expected)
    assert isinstance(polarizations, type(None))


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_with_ant_str(casa_uvfits, hera_uvh5_xx):
    # Test select function with ant_str argument
    uv = casa_uvfits
    inplace = False

    # All baselines
    ant_str = "all"
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(uv.get_antpairs())
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Cross correlations
    ant_str = "cross"
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(uv.get_antpairs())
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())
    # All baselines in data are cross correlations

    # Single antenna number
    ant_str = "1"
    ant_pairs = [
        (1, 2),
        (1, 3),
        (1, 4),
        (1, 7),
        (1, 8),
        (1, 9),
        (1, 12),
        (1, 15),
        (1, 19),
        (1, 20),
        (1, 21),
        (1, 22),
        (1, 23),
        (1, 24),
        (1, 25),
        (1, 27),
        (1, 28),
    ]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Single antenna number not present in data
    ant_str = "10"
    with uvtest.check_warnings(
        UserWarning,
        [
            "Warning: Antenna number 10 passed, but not present in the "
            "ant_1_array or ant_2_array",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv.select(ant_str=ant_str, inplace=inplace)

    # Multiple antenna numbers as list
    ant_str = "23,27"
    ant_pairs = [
        (1, 23),
        (1, 27),
        (2, 23),
        (2, 27),
        (3, 23),
        (3, 27),
        (4, 23),
        (4, 27),
        (7, 23),
        (7, 27),
        (8, 23),
        (8, 27),
        (9, 23),
        (9, 27),
        (12, 23),
        (12, 27),
        (15, 23),
        (15, 27),
        (19, 23),
        (19, 27),
        (20, 23),
        (20, 27),
        (21, 23),
        (21, 27),
        (22, 23),
        (22, 27),
        (23, 24),
        (23, 25),
        (23, 27),
        (23, 28),
        (24, 27),
        (25, 27),
        (27, 28),
    ]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Single baseline
    ant_str = "1_3"
    ant_pairs = [(1, 3)]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Single baseline with polarization
    ant_str = "1l_3r"
    ant_pairs = [(1, 3)]
    pols = ["lr"]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(pols)

    # Single baseline with single polarization in first entry
    ant_str = "1l_3,2x_3"
    # x,y pols not present in data
    with uvtest.check_warnings(
        UserWarning,
        [
            "Warning: Polarization XX,XY is not present in the polarization_array",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv.select(ant_str=ant_str, inplace=inplace)
    # with polarizations in data
    ant_str = "1l_3,2_3"
    ant_pairs = [(1, 3), (2, 3)]
    pols = ["ll", "lr"]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(pols)

    # Single baseline with single polarization in last entry
    ant_str = "1_3l,2_3x"
    # x,y pols not present in data
    with uvtest.check_warnings(
        UserWarning,
        [
            "Warning: Polarization XX,YX is not present in the polarization_array",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    # with polarizations in data
    ant_str = "1_3l,2_3"
    ant_pairs = [(1, 3), (2, 3)]
    pols = ["ll", "rl"]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(pols)

    # Multiple baselines as list
    ant_str = "1_2,1_3,1_10"
    # Antenna number 10 not in data
    with uvtest.check_warnings(
        UserWarning,
        [
            "Warning: Antenna number 10 passed, but not present in the "
            "ant_1_array or ant_2_array",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv2 = uv.select(ant_str=ant_str, inplace=inplace)

    ant_pairs = [(1, 2), (1, 3)]
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Multiples baselines with polarizations as list
    ant_str = "1r_2l,1l_3l,1r_12r"
    ant_pairs = [(1, 2), (1, 3), (1, 12)]
    pols = ["rr", "ll", "rl"]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(pols)

    # Specific baselines with parenthesis
    ant_str = "(1,3)_12"
    ant_pairs = [(1, 12), (3, 12)]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Specific baselines with parenthesis
    ant_str = "1_(3,12)"
    ant_pairs = [(1, 3), (1, 12)]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Antenna numbers with polarizations
    ant_str = "(1l,2r)_(3l,7r)"
    ant_pairs = [(1, 3), (1, 7), (2, 3), (2, 7)]
    pols = ["rr", "ll", "rl", "lr"]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(pols)

    # Antenna numbers with - for avoidance
    ant_str = "1_(-3,12)"
    ant_pairs = [(1, 12)]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    ant_str = "(-1,3)_12"
    ant_pairs = [(3, 12)]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Remove specific antenna number
    ant_str = "1,-3"
    ant_pairs = [
        (1, 2),
        (1, 4),
        (1, 7),
        (1, 8),
        (1, 9),
        (1, 12),
        (1, 15),
        (1, 19),
        (1, 20),
        (1, 21),
        (1, 22),
        (1, 23),
        (1, 24),
        (1, 25),
        (1, 27),
        (1, 28),
    ]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Remove specific baseline
    ant_str = "1,-1_3"
    ant_pairs = [
        (1, 2),
        (1, 4),
        (1, 7),
        (1, 8),
        (1, 9),
        (1, 12),
        (1, 15),
        (1, 19),
        (1, 20),
        (1, 21),
        (1, 22),
        (1, 23),
        (1, 24),
        (1, 25),
        (1, 27),
        (1, 28),
    ]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Antenna numbers with polarizations and - for avoidance
    ant_str = "1l_(-3r,12l)"
    ant_pairs = [(1, 12)]
    pols = ["ll"]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(pols)

    # Test pseudo-Stokes params with select
    ant_str = "pi,pQ"
    pols = ["pQ", "pI"]
    uv.polarization_array = np.array([4, 3, 2, 1])
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(uv.get_antpairs())
    assert Counter(uv2.get_pols()) == Counter(pols)

    # Test ant_str = 'auto' on file with auto correlations
    uv = hera_uvh5_xx

    ant_str = "auto"
    ant_nums = [
        9,
        10,
        20,
        22,
        31,
        43,
        53,
        64,
        65,
        72,
        80,
        81,
        88,
        89,
        96,
        97,
        104,
        105,
        112,
    ]
    ant_pairs_autos = [(ant_i, ant_i) for ant_i in ant_nums]
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs_autos)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Test cross correlation extraction on data with auto + cross
    ant_str = "cross"
    ant_pairs_cross = list(itertools.combinations(ant_nums, 2))
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs_cross)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Remove only polarization of single baseline
    ant_str = "all,-9x_10x"
    ant_pairs = ant_pairs_autos + ant_pairs_cross
    ant_pairs.remove((9, 10))
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())

    # Test appending all to beginning of strings that start with -
    ant_str = "-9"
    ant_pairs = ant_pairs_autos + ant_pairs_cross
    for ant_i in ant_nums:
        ant_pairs.remove((9, ant_i))
    uv2 = uv.select(ant_str=ant_str, inplace=inplace)
    assert Counter(uv2.get_antpairs()) == Counter(ant_pairs)
    assert Counter(uv2.get_pols()) == Counter(uv.get_pols())


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "kwargs,message",
    [
        (
            {"ant_str": "", "antenna_nums": []},
            "Cannot provide ant_str with antenna_nums, antenna_names, bls, or "
            "polarizations.",
        ),
        (
            {"ant_str": "", "antenna_names": []},
            "Cannot provide ant_str with antenna_nums, antenna_names, bls, or "
            "polarizations.",
        ),
        (
            {"ant_str": "", "bls": []},
            "Cannot provide ant_str with antenna_nums, antenna_names, bls, or "
            "polarizations.",
        ),
        (
            {"ant_str": "", "polarizations": []},
            "Cannot provide ant_str with antenna_nums, antenna_names, bls, or "
            "polarizations.",
        ),
        ({"ant_str": "auto"}, "There is no data matching ant_str=auto in this object."),
        (
            {"ant_str": "pI,pq,pU,pv"},
            "Polarization 4 is not present in the polarization_array",
        ),
        ({"ant_str": "none"}, "Unparsible argument none"),
    ],
)
def test_select_with_ant_str_errors(casa_uvfits, kwargs, message):
    uv = casa_uvfits

    with pytest.raises(ValueError, match=message):
        uv.select(**kwargs)


@pytest.mark.filterwarnings("ignore:The `use_old_proj` option is deprecated")
@pytest.mark.filterwarnings("ignore:Data will be unphased")
@pytest.mark.parametrize(
    "arg_dict,msg",
    [
        [
            {"out": "icrs", "oldproj": True},
            "UVW calculation with `use_old_proj` requires unprojected data",
        ],
        [
            {"allow": True, "orig": "gcrs", "out": "xyz", "oldproj": True},
            "Invalid parameter output_phase_frame.",
        ],
        [
            {"allow": True, "orig": "xyz", "out": "hcrs", "oldproj": True},
            "Invalid parameter orig_phase_frame.",
        ],
    ],
)
def test_set_uvws_from_antenna_pos_errs(casa_uvfits, arg_dict, msg):
    """
    Verify that set_uvws_from_antenna_pos throws appropriate errors when being
    provided with bad combinations are arguments.
    """
    if arg_dict.get("oldproj"):
        casa_uvfits.phase_center_catalog[0]["cat_frame"] = "icrs"
    with pytest.raises(ValueError, match=msg):
        casa_uvfits.set_uvws_from_antenna_positions(
            allow_phasing=arg_dict.get("allow"),
            orig_phase_frame=arg_dict.get("orig"),
            output_phase_frame=arg_dict.get("out"),
            use_old_proj=arg_dict.get("oldproj"),
        )


def test_set_uvws_from_antenna_pos_old(uv_phase_comp):
    uv_object, _ = uv_phase_comp
    orig_uvw_array = uv_object.uvw_array.copy()

    uv_object.phase_center_catalog[0]["cat_frame"] = "icrs"
    with uvtest.check_warnings(
        [UserWarning, DeprecationWarning],
        [
            "Data will be unphased and rephased to calculate UVWs",
            "The `use_old_proj` option is deprecated",
        ],
    ):
        uv_object.set_uvws_from_antenna_positions(allow_phasing=True, use_old_proj=True)
    max_diff = np.amax(np.absolute(np.subtract(orig_uvw_array, uv_object.uvw_array)))
    assert np.isclose(max_diff, 0.0, atol=2)


def test_set_uvws_multi_phase_error(uv_phase_comp):
    """
    Verify that we can't use the old proj method with multi-phase-ctr objects when
    calling set_uvws_from_antenna_positions.
    """
    uv_object, _ = uv_phase_comp

    with pytest.raises(
        ValueError, match="Objects with fk5 phase frames are not compatible"
    ):
        uv_object.set_uvws_from_antenna_positions(use_old_proj=True)

    uv_object.phase_center_catalog[0]["cat_type"] = "ephem"
    with pytest.raises(
        ValueError, match="Objects with ephem phase centers are not compatible"
    ):
        uv_object.set_uvws_from_antenna_positions(use_old_proj=True)

    mask = np.full((uv_object.Nblts), False, dtype=bool)
    mask[: uv_object.Nblts // 2] = True
    uv_object.phase(ra=0, dec=0, phase_frame="icrs", cat_name="npol", select_mask=mask)

    with pytest.raises(
        ValueError, match="Objects with multiple phase centers are not compatible"
    ):
        uv_object.set_uvws_from_antenna_positions(use_old_proj=True)


@pytest.mark.parametrize(
    "rephase,warn,msg",
    [
        [
            False,
            [DeprecationWarning, UserWarning],
            [
                "The `require_phasing` option is deprecated",
                "Recalculating uvw_array without adjusting visibility phases",
            ],
        ],
        [True, DeprecationWarning, "The `require_phasing` option is deprecated"],
    ],
)
def test_set_uvws_from_antenna_pos(sma_mir, sma_mir_main, rephase, warn, msg):
    # Now do this operation w/ SMA data, whose uvws are known good.
    with uvtest.check_warnings(warn, match=msg):
        sma_mir.set_uvws_from_antenna_positions(
            allow_phasing=rephase, require_phasing=rephase
        )
    max_diff = np.amax(np.absolute(sma_mir_main.uvw_array - sma_mir.uvw_array))
    assert np.isclose(max_diff, 0.0, atol=1e-5)
    # Verify that the data array is untouched if not rephased, otherwise that
    # the data are in fact different
    assert np.all(sma_mir_main.data_array == sma_mir.data_array) != rephase


def test_get_antenna_redundancies(pyuvsim_redundant):
    uv0 = pyuvsim_redundant

    old_bl_array = np.copy(uv0.baseline_array)
    red_gps, centers, lengths = uv0.get_redundancies(
        use_antpos=True, include_autos=False, conjugate_bls=True
    )
    # new and old baseline Numbers are not the same (different conjugation)
    assert not np.allclose(uv0.baseline_array, old_bl_array)

    # assert all baselines are in the data (because it's conjugated to match)
    for gp in red_gps:
        for bl in gp:
            assert bl in uv0.baseline_array

    # conjugate data differently
    uv0.conjugate_bls(convention="ant1<ant2")
    new_red_gps, new_centers, new_lengths, conjs = uv0.get_redundancies(
        use_antpos=True, include_autos=False, include_conjugates=True
    )

    assert conjs is None

    apos, anums = uv0.get_ENU_antpos()
    new_red_gps, new_centers, new_lengths = uvutils.get_antenna_redundancies(
        anums, apos, include_autos=False
    )

    # all redundancy info is the same
    assert red_gps == new_red_gps
    assert np.allclose(centers, new_centers)
    assert np.allclose(lengths, new_lengths)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("method", ("select", "average"))
@pytest.mark.parametrize("reconjugate", (True, False))
@pytest.mark.parametrize("flagging_level", ("none", "some", "all"))
@pytest.mark.parametrize("future_shapes", [True, False])
def test_redundancy_contract_expand(
    method, reconjugate, flagging_level, future_shapes, pyuvsim_redundant
):
    # Test that a UVData object can be reduced to one baseline from each redundant group
    # and restored to its original form.

    uv0 = pyuvsim_redundant

    if not future_shapes:
        uv0.use_current_array_shapes()
        # Need to test with multi phase center, but don't need a separate parameterize

    # Fails at lower precision because some baselines fall into multiple
    # redundant groups
    tol = 0.02

    if reconjugate:
        # the test file has groups that are either all not conjugated or all conjugated.
        # need to conjugate some so we have mixed groups to properly test the average
        # method.
        (
            orig_red_gps,
            orig_centers,
            orig_lengths,
            orig_conjugates,
        ) = uv0.get_redundancies(tol, include_conjugates=True)
        blt_inds_to_conj = []
        for gp in orig_red_gps:
            if len(gp) > 1:
                blt_inds_to_conj.extend(
                    list(np.nonzero(uv0.baseline_array == gp[0])[0])
                )
        uv0.conjugate_bls(convention=np.array(blt_inds_to_conj))

    # Assign identical data to each redundant group, set up flagging.
    # This must be done after reconjugation because reconjugation can alter the index
    # baseline
    red_gps, centers, lengths, conjugates = uv0.get_redundancies(
        tol, include_conjugates=True
    )
    index_bls = []
    for gp_ind, gp in enumerate(red_gps):
        for bl in gp:
            inds = np.where(bl == uv0.baseline_array)
            uv0.data_array[inds] *= 0
            # Make this data complex so that we can track phase issues
            uv0.data_array[inds] += complex(1 + gp_ind, 1 - gp_ind)
        index_bls.append(gp[0])

    # Data in the conjugated list, make sure that we manually conjugate here
    conj_mask = np.isin(uv0.baseline_array, conjugates)
    uv0.data_array[conj_mask] = np.conj(uv0.data_array[conj_mask])

    if flagging_level == "none":
        assert np.all(~uv0.flag_array)
    elif flagging_level == "some":
        # flag all the index baselines in a redundant group
        for bl in index_bls:
            bl_locs = np.where(uv0.baseline_array == bl)
            uv0.flag_array[bl_locs] = True
    elif flagging_level == "all":
        uv0.flag_array[:] = True
        uv0.check()
        assert np.all(uv0.flag_array)

    uv3 = uv0.copy()
    if reconjugate:
        # undo the conjugations to make uv3 have different conjugations than uv0 to test
        # that we still get the same answer
        uv3.conjugate_bls(convention=np.array(blt_inds_to_conj))

    uv2 = uv0.compress_by_redundancy(method=method, tol=tol, inplace=False)
    uv2.check()

    if method == "average":
        gp_bl_use = []
        nbls_group = []
        for gp in red_gps:
            bls_init = [bl for bl in gp if bl in uv0.baseline_array]
            nbls_group.append(len(bls_init))
            bl_use = [bl for bl in gp if bl in uv2.baseline_array]
            assert len(bl_use) == 1
            gp_bl_use.append(bl_use[0])

        for gp_ind, bl in enumerate(gp_bl_use):
            if flagging_level == "none" or flagging_level == "all":
                assert np.all(uv2.get_nsamples(bl) == nbls_group[gp_ind])
            else:
                assert np.all(uv2.get_nsamples(bl) == max((nbls_group[gp_ind] - 1), 1))
        if flagging_level == "all":
            assert np.all(uv2.flag_array)
        else:
            for gp_ind, bl in enumerate(gp_bl_use):
                if nbls_group[gp_ind] > 1:
                    assert np.all(~uv2.get_flags(bl))
    else:
        assert np.all(uv2.nsample_array == 1)
        if flagging_level == "some" or flagging_level == "all":
            assert np.all(uv2.flag_array)
        else:
            assert np.all(~uv2.flag_array)

    # Compare in-place to separated compression without the conjugation.
    uv3.compress_by_redundancy(method=method, tol=tol)
    if reconjugate:
        assert len(orig_red_gps) == len(red_gps)
        match_ind_list = []
        for gp in red_gps:
            for bl in gp:
                match_ind = [
                    ind for ind, orig_gp in enumerate(orig_red_gps) if bl in orig_gp
                ]
                if len(match_ind) > 0:
                    break
            assert len(match_ind) == 1
            match_ind_list.append(match_ind[0])

        # the reconjugation of select baselines causes the set of baselines on the
        # two objects to differ. Need to match up baselines again
        unique_bls_2 = np.unique(uv2.baseline_array)
        unique_bls_3 = np.unique(uv3.baseline_array)
        unmatched_bls = list(
            set(unique_bls_2) - set(unique_bls_2).intersection(unique_bls_3)
        )

        # first find the ones that will be fixed by simple conjugation
        ant1, ant2 = uv2.baseline_to_antnums(unmatched_bls)
        conj_bls = uv2.antnums_to_baseline(ant2, ant1)
        bls_to_conj = list(set(unique_bls_3).intersection(conj_bls))
        if len(bls_to_conj) > 0:
            blts_to_conj = []
            for bl in bls_to_conj:
                blts_to_conj.extend(list(np.nonzero(uv3.baseline_array == bl)[0]))
            uv3.conjugate_bls(convention=blts_to_conj)

        # now check for ones that are still not matching
        unique_bls_3 = np.unique(uv3.baseline_array)
        unmatched_bls = list(
            set(unique_bls_2) - set(unique_bls_2).intersection(unique_bls_3)
        )
        for bl in unmatched_bls:
            assert bl in uv0.baseline_array
            for gp_ind, gp in enumerate(red_gps):
                if bl in gp:
                    bl_match = [
                        bl3
                        for bl3 in orig_red_gps[match_ind_list[gp_ind]]
                        if bl3 in unique_bls_3
                    ]
                    assert len(bl_match) == 1
                    blts = np.nonzero(uv3.baseline_array == bl_match[0])[0]
                    uv3.baseline_array[blts] = bl
                    # use the uvw values from the original for this baseline
                    orig_blts = np.nonzero(uv0.baseline_array == bl)[0]
                    uv3.uvw_array[blts, :] = uv0.uvw_array[orig_blts, :]
                    if method == "select":
                        # use the data values from the original for this baseline
                        # TODO: Spw axis to be collapsed in future release
                        uv2_blts = np.nonzero(uv2.baseline_array == bl)[0]
                        assert np.allclose(
                            uv2.data_array[uv2_blts], uv0.data_array[orig_blts]
                        )
                        uv3.data_array[blts] = uv2.data_array[uv2_blts]
                        if flagging_level == "some":
                            uv3.flag_array[:] = True

        uv3.ant_1_array, uv3.ant_2_array = uv3.baseline_to_antnums(uv3.baseline_array)
        uv3.Nants_data = uv3._calc_nants_data()
        unique_bls_3 = np.unique(uv3.baseline_array)
        unmatched_bls = list(
            set(unique_bls_2) - set(unique_bls_2).intersection(unique_bls_3)
        )
        assert set(unique_bls_2) == set(unique_bls_3)

    uv4 = uv2.copy()
    uv4.reorder_blts()
    uv3.reorder_blts()
    assert uv4 == uv3

    # check inflating gets back to the original
    with uvtest.check_warnings(
        UserWarning, match="Missing some redundant groups. Filling in available data."
    ):
        uv2.inflate_by_redundancy(tol=tol)

    # Confirm that we get the same result looping inflate -> compress -> inflate.
    uv3 = uv2.compress_by_redundancy(method=method, tol=tol, inplace=False)
    with uvtest.check_warnings(
        UserWarning, match="Missing some redundant groups. Filling in available data."
    ):
        uv3.inflate_by_redundancy(tol=tol)

    if method == "average":
        # with average, the nsample_array goes up by the number of baselines
        # averaged together.
        assert not np.allclose(uv3.nsample_array, uv2.nsample_array)
        # reset it to test other parameters
        uv3.nsample_array = uv2.nsample_array
    uv3.history = uv2.history
    assert uv2 == uv3

    uv2.history = uv0.history
    # Inflation changes the baseline ordering into the order of the redundant groups.
    # reorder bls for comparison
    uv0.reorder_blts(conj_convention="u>0")
    uv2.reorder_blts(conj_convention="u>0")
    uv2._uvw_array.tols = [0, tol]

    if method == "average":
        # with average, the nsample_array goes up by the number of baselines
        # averaged together.
        assert not np.allclose(uv2.nsample_array, uv0.nsample_array)
        # reset it to test other parameters
        uv2.nsample_array = uv0.nsample_array
    if flagging_level == "some":
        if method == "select":
            # inflated array will be entirely flagged
            assert np.all(uv2.flag_array)
            assert not np.allclose(uv0.flag_array, uv2.flag_array)
            uv2.flag_array = uv0.flag_array
        else:
            # flag arrays will not match -- inflated array will mostly be unflagged
            # it will only be flagged if only one in group
            assert not np.allclose(uv0.flag_array, uv2.flag_array)
            uv2.flag_array = uv0.flag_array

    assert uv2 == uv0


@pytest.mark.parametrize("method", ("select", "average"))
@pytest.mark.parametrize("flagging_level", ("none", "some", "all"))
def test_redundancy_contract_expand_variable_data(
    method, flagging_level, pyuvsim_redundant
):
    # Test that a UVData object can be reduced to one baseline from each redundant group
    # and restored to its original form.

    uv0 = pyuvsim_redundant

    # Fails at lower precision because some baselines fall into multiple
    # redundant groups
    tol = 0.02
    # Assign identical data to each redundant group in comparison object
    # Assign data to the index baseline and zeros elsewhere in the one to compress
    red_gps, centers, lengths = uv0.get_redundancies(
        tol=tol, use_antpos=True, conjugate_bls=True
    )
    index_bls = [gp[0] for gp in red_gps]
    uv0.data_array *= 0
    uv1 = uv0.copy()
    for gp_ind, gp in enumerate(red_gps):
        for bl in gp:
            inds = np.where(bl == uv0.baseline_array)
            uv1.data_array[inds] += complex(gp_ind)
            if bl in index_bls:
                uv0.data_array[inds] += complex(gp_ind)

    if flagging_level == "none":
        assert np.all(~uv0.flag_array)
    elif flagging_level == "some":
        # flag all the non index baselines in a redundant group
        uv0.flag_array[:, :, :] = True
        for bl in index_bls:
            bl_locs = np.where(uv0.baseline_array == bl)
            uv0.flag_array[bl_locs, :, :] = False
    elif flagging_level == "all":
        uv0.flag_array[:] = True
        uv0.check()
        assert np.all(uv0.flag_array)

    uv2 = uv0.compress_by_redundancy(method=method, tol=tol, inplace=False)

    # inflate to get back to the original size
    with uvtest.check_warnings(
        UserWarning, match="Missing some redundant groups. Filling in available data."
    ):
        uv2.inflate_by_redundancy(tol=tol)

    uv2.history = uv1.history
    # Inflation changes the baseline ordering into the order of the redundant groups.
    # reorder bls for comparison
    uv1.reorder_blts(conj_convention="u>0")
    uv2.reorder_blts(conj_convention="u>0")
    uv2._uvw_array.tols = [0, tol]

    if method == "select":
        if flagging_level == "all":
            assert uv2._flag_array != uv1._flag_array
            uv2.flag_array = uv1.flag_array
        assert uv2 == uv1
    else:
        if flagging_level == "some":
            for gp in red_gps:
                bls_init = [bl for bl in gp if bl in uv1.baseline_array]
                for bl in bls_init:
                    assert np.all(uv2.get_data(bl) == uv1.get_data(bl))
                    assert np.all(uv2.get_nsamples(bl) == uv1.get_nsamples(bl))
        else:
            assert uv2.data_array.min() < uv1.data_array.min()
            assert np.all(uv2.data_array <= uv1.data_array)
            for gp in red_gps:
                bls_init = [bl for bl in gp if bl in uv1.baseline_array]
                for bl in bls_init:
                    assert np.all(
                        uv2.get_data(bl) == (uv1.get_data(bl) / len(bls_init))
                    )
                    assert np.all(uv2.get_nsamples(bl) == len(bls_init))


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("method", ("select", "average"))
def test_redundancy_contract_expand_nblts_not_nbls_times_ntimes(method, casa_uvfits):
    uv0 = casa_uvfits

    # check that Nblts != Nbls * Ntimes
    assert uv0.Nblts != uv0.Nbls * uv0.Ntimes

    tol = 1.0

    # Assign identical data to each redundant group:
    red_gps, centers, lengths = uv0.get_redundancies(
        tol=tol, use_antpos=True, conjugate_bls=True
    )
    for i, gp in enumerate(red_gps):
        for bl in gp:
            inds = np.where(bl == uv0.baseline_array)
            uv0.data_array[inds, ...] *= 0
            uv0.data_array[inds, ...] += complex(i)

    if method == "average":
        with uvtest.check_warnings(
            UserWarning,
            [
                "Index baseline in the redundant group does not have all the "
                "times, compressed object will be missing those times."
            ]
            * 4
            + [
                "The uvw_array does not match the expected values given the antenna "
                "positions."
            ]
            * 2,
        ):
            uv2 = uv0.compress_by_redundancy(method=method, tol=tol, inplace=False)
    else:
        uv2 = uv0.compress_by_redundancy(method=method, tol=tol, inplace=False)

    # check inflating gets back to the original
    with uvtest.check_warnings(
        UserWarning,
        [
            "Missing some redundant groups. Filling in available data.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv2.inflate_by_redundancy(tol=tol)

    uv2.history = uv0.history
    # Inflation changes the baseline ordering into the order of the redundant groups.
    # reorder bls for comparison
    uv0.reorder_blts()
    uv2.reorder_blts()
    uv2._uvw_array.tols = [0, tol]

    blt_inds = []
    missing_inds = []
    for bl, t in zip(uv0.baseline_array, uv0.time_array):
        if (bl, t) in zip(uv2.baseline_array, uv2.time_array):
            this_ind = np.where((uv2.baseline_array == bl) & (uv2.time_array == t))[0]
            blt_inds.append(this_ind[0])
        else:
            # this is missing because of the compress_by_redundancy step
            missing_inds.append(
                np.where((uv0.baseline_array == bl) & (uv0.time_array == t))[0]
            )

    uv3 = uv2.select(blt_inds=blt_inds, inplace=False)

    orig_inds_keep = list(np.arange(uv0.Nblts))
    for ind in missing_inds:
        orig_inds_keep.remove(ind)
    uv1 = uv0.select(blt_inds=orig_inds_keep, inplace=False)

    if method == "average":
        # the nsample array in the original object varies, so they
        # don't come out the same
        assert not np.allclose(uv3.nsample_array, uv1.nsample_array)
        uv3.nsample_array = uv1.nsample_array

    assert uv3 == uv1


def test_compress_redundancy_variable_inttime():
    uv0 = UVData()
    uv0.read_uvfits(
        os.path.join(DATA_PATH, "fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits"),
        use_future_array_shapes=True,
    )

    tol = 0.05
    ntimes_in = uv0.Ntimes

    # Assign identical data to each redundant group:
    red_gps, centers, lengths = uv0.get_redundancies(
        tol=tol, use_antpos=True, conjugate_bls=True
    )
    index_bls = [gp[0] for gp in red_gps]
    uv0.data_array *= 0
    # set different int time for index baseline in object to compress
    uv1 = uv0.copy()
    ave_int_time = np.average(uv0.integration_time)
    nbls_group = np.zeros(len(red_gps))
    for gp_ind, gp in enumerate(red_gps):
        for bl in gp:
            inds = np.where(bl == uv0.baseline_array)
            if inds[0].size > 0:
                nbls_group[gp_ind] += 1
            uv1.data_array[inds] += complex(gp_ind)
            uv0.data_array[inds] += complex(gp_ind)
            if bl not in index_bls:
                uv0.integration_time[inds] = ave_int_time / 2

    assert uv0._integration_time != uv1._integration_time

    with uvtest.check_warnings(
        UserWarning,
        "Integrations times are not identical in a redundant "
        "group. Averaging anyway but this may cause unexpected "
        "behavior.",
        nwarnings=56,
    ) as warn_record:
        uv0.compress_by_redundancy(method="average", tol=tol)
    assert len(warn_record) == np.sum(nbls_group > 1) * ntimes_in

    uv1.compress_by_redundancy(method="average", tol=tol)

    assert uv0 == uv1


@pytest.mark.parametrize("method", ("select", "average"))
def test_compress_redundancy_metadata_only(method, pyuvsim_redundant):
    uv0 = pyuvsim_redundant

    tol = 0.05

    # Assign identical data to each redundant group
    red_gps, centers, lengths = uv0.get_redundancies(
        tol=tol, use_antpos=True, conjugate_bls=True
    )
    for i, gp in enumerate(red_gps):
        for bl in gp:
            inds = np.where(bl == uv0.baseline_array)
            uv0.data_array[inds] *= 0
            uv0.data_array[inds] += complex(i)

    uv2 = uv0.copy(metadata_only=True)
    uv2.compress_by_redundancy(method=method, tol=tol, inplace=True)

    uv0.compress_by_redundancy(method=method, tol=tol)
    uv0.data_array = None
    uv0.flag_array = None
    uv0.nsample_array = None
    assert uv0 == uv2


def test_compress_redundancy_wrong_method(pyuvsim_redundant):
    uv0 = pyuvsim_redundant

    tol = 0.05
    with pytest.raises(ValueError, match="method must be one of"):
        uv0.compress_by_redundancy(method="foo", tol=tol, inplace=True)


@pytest.mark.parametrize("method", ("select", "average"))
def test_redundancy_missing_groups(method, pyuvsim_redundant, tmp_path):
    # Check that if I try to inflate a compressed UVData that is missing
    # redundant groups, it will raise the right warnings and fill only what
    # data are available.

    uv0 = pyuvsim_redundant

    tol = 0.02
    num_select = 19

    uv0.compress_by_redundancy(method=method, tol=tol)
    fname = str(tmp_path / "temp_hera19_missingreds.uvfits")

    bls = np.unique(uv0.baseline_array)[:num_select]  # First twenty baseline groups
    uv0.select(bls=[uv0.baseline_to_antnums(bl) for bl in bls])
    uv0.write_uvfits(fname)
    uv1 = UVData()
    uv1.read_uvfits(fname, use_future_array_shapes=True)

    # check that filenames are what we expect
    assert uv0.filename == ["fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits"]
    assert uv1.filename == ["temp_hera19_missingreds.uvfits"]

    # update phase center catalog to make objects match
    uv1._consolidate_phase_center_catalogs(
        reference_catalog=uv0.phase_center_catalog, ignore_name=True
    )
    assert uv0 == uv1  # Check that writing compressed files causes no issues.

    with uvtest.check_warnings(
        UserWarning, match="Missing some redundant groups. Filling in available data."
    ):
        uv1.inflate_by_redundancy(tol=tol)

    uv2 = uv1.compress_by_redundancy(method=method, tol=tol, inplace=False)

    assert np.unique(uv2.baseline_array).size == num_select


def test_quick_redundant_vs_redundant_test_array(pyuvsim_redundant):
    """Verify the quick redundancy calc returns the same groups as a known array."""
    uv = pyuvsim_redundant

    uv.select(times=uv.time_array[0])
    uv.unproject_phase()
    uv.conjugate_bls(convention="u>0", use_enu=True)
    tol = 0.05
    # a quick and dirty redundancy calculation
    unique_bls, baseline_inds = np.unique(uv.baseline_array, return_index=True)
    uvw_vectors = np.take(uv.uvw_array, baseline_inds, axis=0)
    uvw_diffs = np.expand_dims(uvw_vectors, axis=0) - np.expand_dims(
        uvw_vectors, axis=1
    )
    uvw_diffs = np.linalg.norm(uvw_diffs, axis=2)

    reds = np.where(uvw_diffs < tol, unique_bls, 0)
    reds = np.ma.masked_where(reds == 0, reds)
    groups = []
    for bl in reds:
        grp = []
        grp.extend(bl.compressed())
        for other_bls in reds:
            if set(reds.compressed()).issubset(other_bls.compressed()):
                grp.extend(other_bls.compressed())
        grp = np.unique(grp).tolist()
        groups.append(grp)

    pad = len(max(groups, key=len))
    groups = np.array([i + [-1] * (pad - len(i)) for i in groups])
    groups = np.unique(groups, axis=0)
    groups = [sorted(bl for bl in grp if bl != -1) for grp in groups]
    groups.sort()

    redundant_groups, centers, lengths, conj_inds = uv.get_redundancies(
        tol=tol, include_conjugates=True
    )
    redundant_groups.sort()
    assert groups == redundant_groups


def test_redundancy_finder_when_nblts_not_nbls_times_ntimes(casa_uvfits):
    """Test the redundancy finder functions when Nblts != Nbls * Ntimes."""
    tol = 1  # meter
    uv = casa_uvfits
    uv.conjugate_bls(convention="u>0", use_enu=True)
    # check that Nblts != Nbls * Ntimes
    assert uv.Nblts != uv.Nbls * uv.Ntimes

    # a quick and dirty redundancy calculation
    unique_bls, baseline_inds = np.unique(uv.baseline_array, return_index=True)
    uvw_vectors = np.take(uv.uvw_array, baseline_inds, axis=0)
    uvw_diffs = np.expand_dims(uvw_vectors, axis=0) - np.expand_dims(
        uvw_vectors, axis=1
    )
    uvw_diffs = np.linalg.norm(uvw_diffs, axis=2)

    reds = np.where(uvw_diffs < tol, unique_bls, 0)
    reds = np.ma.masked_where(reds == 0, reds)
    groups = []
    for bl in reds:
        grp = []
        grp.extend(bl.compressed())
        for other_bls in reds:
            if set(reds.compressed()).issubset(other_bls.compressed()):
                grp.extend(other_bls.compressed())
        grp = np.unique(grp).tolist()
        groups.append(grp)

    pad = len(max(groups, key=len))
    groups = np.array([i + [-1] * (pad - len(i)) for i in groups])
    groups = np.unique(groups, axis=0)
    groups = [sorted(bl for bl in grp if bl != -1) for grp in groups]
    groups.sort()

    redundant_groups, centers, lengths, conj_inds = uv.get_redundancies(
        tol=tol, include_conjugates=True
    )
    redundant_groups.sort()
    assert groups == redundant_groups


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_overlapping_data_add(casa_uvfits, tmp_path, future_shapes):
    # read in test data
    uv = casa_uvfits

    if not future_shapes:
        uv.use_current_array_shapes()

    # slice into four objects
    blts1 = np.arange(500)
    blts2 = np.arange(500, 1360)
    uv1 = uv.select(polarizations=[-1, -2], blt_inds=blts1, inplace=False)
    uv2 = uv.select(polarizations=[-3, -4], blt_inds=blts1, inplace=False)
    uv3 = uv.select(polarizations=[-1, -2], blt_inds=blts2, inplace=False)
    uv4 = uv.select(polarizations=[-3, -4], blt_inds=blts2, inplace=False)

    # combine and check for equality
    uvfull = uv1 + uv2
    uvfull += uv3
    uvfull += uv4
    extra_history = (
        "Downselected to specific baseline-times, polarizations using pyuvdata. "
        "Combined data along polarization axis using pyuvdata. Combined data along "
        "baseline-time axis using pyuvdata. Overwrote invalid data using pyuvdata."
    )
    assert uvutils._check_histories(uvfull.history, uv.history + extra_history)
    uvfull.history = uv.history  # make histories match
    assert uv == uvfull

    # combine in a different order and check for equality
    uvfull = uv1.copy()
    uvfull += uv2
    uvfull2 = uv3.copy()
    uvfull2 += uv4
    uvfull += uvfull2
    extra_history2 = (
        "Downselected to specific baseline-times, polarizations using pyuvdata. "
        "Combined data along polarization axis using pyuvdata. Combined data along "
        "baseline-time axis using pyuvdata."
    )
    assert uvutils._check_histories(uvfull.history, uv.history + extra_history2)
    uvfull.history = uv.history  # make histories match
    assert uv == uvfull

    # check combination not-in-place
    uvfull = uv1 + uv2
    uvfull += uv3
    uvfull = uvfull + uv4
    uvfull.history = uv.history  # make histories match
    assert uv == uvfull

    # test raising error for adding objects incorrectly (i.e., having the object
    # with data to be overwritten come second)
    uvfull = uv1 + uv2
    uvfull += uv3
    with pytest.raises(
        ValueError,
        match="To combine these data, please run the add operation again, but with "
        "the object whose data is to be overwritten as the first object",
    ):
        uv4.__iadd__(uvfull)
    with pytest.raises(
        ValueError, match="These objects have overlapping data and cannot be combined."
    ):
        uv4.__add__(uv4, uvfull)

    # write individual objects out, and make sure that we can read in the list
    uv1_out = str(tmp_path / "uv1.uvfits")
    uv1.write_uvfits(uv1_out)
    uv2_out = str(tmp_path / "uv2.uvfits")
    uv2.write_uvfits(uv2_out)
    uv3_out = str(tmp_path / "uv3.uvfits")
    uv3.write_uvfits(uv3_out)
    uv4_out = str(tmp_path / "uv4.uvfits")
    uv4.write_uvfits(uv4_out)

    uvfull = UVData()
    uvfull.read(
        np.array([uv1_out, uv2_out, uv3_out, uv4_out]),
        allow_rephase=False,
        use_future_array_shapes=future_shapes,
    )
    uvfull.reorder_blts()
    uv.reorder_blts()
    assert uvutils._check_histories(uvfull.history, uv.history + extra_history2)
    uvfull.history = uv.history  # make histories match

    # make sure filenames are what we expect
    assert set(uvfull.filename) == {
        "uv1.uvfits",
        "uv2.uvfits",
        "uv3.uvfits",
        "uv4.uvfits",
    }
    assert uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uvfull.filename = uv.filename
    uvfull._filename.form = (1,)

    uvfull._update_phase_center_id(1, new_id=0)
    uvfull.phase_center_catalog[0]["info_source"] = uv.phase_center_catalog[0][
        "info_source"
    ]

    assert uvfull == uv


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_lsts_from_time_with_only_unique(paper_uvh5):
    """
    Test `set_lsts_from_time_array` with only unique values is identical to full array.
    """
    uv = paper_uvh5
    lat, lon, alt = uv.telescope_location_lat_lon_alt_degrees
    # calculate the lsts for all elements in time array
    full_lsts = uvutils.get_lst_for_time(uv.time_array, lat, lon, alt)
    # use `set_lst_from_time_array` to set the uv.lst_array using only unique values
    uv.set_lsts_from_time_array()
    assert np.array_equal(full_lsts, uv.lst_array)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_lsts_from_time_with_only_unique_background(paper_uvh5):
    """
    Test `set_lsts_from_time_array` with only unique values is identical to full array.
    """
    uv = paper_uvh5
    lat, lon, alt = uv.telescope_location_lat_lon_alt_degrees
    # calculate the lsts for all elements in time array
    full_lsts = uvutils.get_lst_for_time(uv.time_array, lat, lon, alt)
    # use `set_lst_from_time_array` to set the uv.lst_array using only unique values
    proc = uv.set_lsts_from_time_array(background=True)
    proc.join()
    assert np.array_equal(full_lsts, uv.lst_array)


def test_copy(casa_uvfits):
    """Test the copy method"""
    uv_object = casa_uvfits

    uv_object_copy = uv_object.copy()
    assert uv_object_copy == uv_object

    uv_object_copy = uv_object.copy(metadata_only=True)
    assert uv_object_copy.metadata_only

    for name in uv_object._data_params:
        setattr(uv_object, name, None)
    assert uv_object_copy == uv_object

    uv_object_copy = uv_object.copy()
    assert uv_object_copy == uv_object

    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_upsample_in_time(hera_uvh5, future_shapes):
    """Test the upsample_in_time method"""
    uv_object = hera_uvh5

    if not future_shapes:
        uv_object.use_current_array_shapes()

    init_phase_dict = uv_object.phase_center_catalog[0]
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # test that setting the app coords works with extra unused phase centers
    assert 0 not in uv_object.phase_center_catalog.keys()
    uv_object.phase_center_catalog[0] = init_phase_dict

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    max_integration_time = np.amin(uv_object.integration_time) / 2.0
    uv_object.upsample_in_time(max_integration_time, blt_order="baseline")

    assert np.allclose(uv_object.integration_time, max_integration_time)
    # we should double the size of the data arrays
    assert uv_object.data_array.size == 2 * init_data_size
    # output data should be the same
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[0, 0, 0], out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(init_ns[0, 0, 0], out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_in_time_with_flags(hera_uvh5):
    """Test the upsample_in_time method with flags"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    max_integration_time = np.amin(uv_object.integration_time) / 2.0

    # add flags and upsample again
    inds01 = uv_object.antpair2ind(0, 1)
    uv_object.flag_array[inds01[0], 0, 0] = True
    uv_object.upsample_in_time(max_integration_time, blt_order="baseline")

    # data and nsamples should be changed as normal, but flagged
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[0, 0, 0], out_wf[0, 0, 0])
    out_flags = uv_object.get_flags(0, 1)
    assert np.all(out_flags[:2, 0, 0])
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(init_ns[0, 0, 0], out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_in_time_noninteger_resampling(hera_uvh5):
    """Test the upsample_in_time method with a non-integer resampling factor"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    max_integration_time = np.amin(uv_object.integration_time) * 0.75
    uv_object.upsample_in_time(max_integration_time, blt_order="baseline")

    assert np.allclose(uv_object.integration_time, max_integration_time * 0.5 / 0.75)
    # we should double the size of the data arrays
    assert uv_object.data_array.size == 2 * init_data_size
    # output data should be different by a factor of 2
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[0, 0, 0], out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(init_ns[0, 0, 0], out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_in_time_errors(hera_uvh5):
    """Test errors and warnings raised by upsample_in_time"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # test using a too-small integration time
    max_integration_time = 1e-3 * np.amin(uv_object.integration_time)
    with pytest.raises(
        ValueError, match="Decreasing the integration time by more than"
    ):
        uv_object.upsample_in_time(max_integration_time)

    # catch a warning for doing no work
    uv_object2 = uv_object.copy()
    max_integration_time = 2 * np.amax(uv_object.integration_time)
    with uvtest.check_warnings(
        UserWarning, "All values in the integration_time array are already longer"
    ):
        uv_object.upsample_in_time(max_integration_time)
    assert uv_object == uv_object2

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_in_time_summing_correlator_mode(hera_uvh5):
    """Test the upsample_in_time method with summing correlator mode"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    max_integration_time = np.amin(uv_object.integration_time) / 2.0
    uv_object.upsample_in_time(
        max_integration_time, blt_order="baseline", summing_correlator_mode=True
    )

    assert np.allclose(uv_object.integration_time, max_integration_time)
    # we should double the size of the data arrays
    assert uv_object.data_array.size == 2 * init_data_size
    # output data should be the half the input
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[0, 0, 0] / 2, out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(init_ns[0, 0, 0], out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_in_time_summing_correlator_mode_with_flags(hera_uvh5):
    """Test the upsample_in_time method with summing correlator mode and flags"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # add flags and upsample again
    inds01 = uv_object.antpair2ind(0, 1)
    uv_object.flag_array[inds01[0], 0, 0] = True
    max_integration_time = np.amin(uv_object.integration_time) / 2.0
    uv_object.upsample_in_time(
        max_integration_time, blt_order="baseline", summing_correlator_mode=True
    )

    # data and nsamples should be changed as normal, but flagged
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[0, 0, 0] / 2, out_wf[0, 0, 0])
    out_flags = uv_object.get_flags(0, 1)
    assert np.all(out_flags[:2, 0, 0])
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(init_ns[0, 0, 0], out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_in_time_summing_correlator_mode_nonint_resampling(hera_uvh5):
    """Test the upsample_in_time method with summing correlator mode
    and non-integer resampling
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # try again with a non-integer resampling factor
    # change the target integration time
    max_integration_time = np.amin(uv_object.integration_time) * 0.75
    uv_object.upsample_in_time(
        max_integration_time, blt_order="baseline", summing_correlator_mode=True
    )

    assert np.allclose(uv_object.integration_time, max_integration_time * 0.5 / 0.75)
    # we should double the size of the data arrays
    assert uv_object.data_array.size == 2 * init_data_size
    # output data should be half the input
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[0, 0, 0] / 2, out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(init_ns[0, 0, 0], out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_partial_upsample_in_time(hera_uvh5):
    """Test the upsample_in_time method with non-uniform upsampling"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # change a whole baseline's integration time
    bl_inds = uv_object.antpair2ind(0, 1)
    uv_object.integration_time[bl_inds] = uv_object.integration_time[0] / 2.0

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_wf_01 = uv_object.get_data(0, 1)
    init_wf_02 = uv_object.get_data(0, 2)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns_01 = uv_object.get_nsamples(0, 1)
    init_ns_02 = uv_object.get_nsamples(0, 2)

    # change the target integration time
    max_integration_time = np.amin(uv_object.integration_time)
    uv_object.upsample_in_time(max_integration_time, blt_order="baseline")

    assert np.allclose(uv_object.integration_time, max_integration_time)
    # output data should be the same
    out_wf_01 = uv_object.get_data(0, 1)
    out_wf_02 = uv_object.get_data(0, 2)
    assert np.all(init_wf_01 == out_wf_01)
    assert np.isclose(init_wf_02[0, 0, 0], out_wf_02[0, 0, 0])
    assert init_wf_02.size * 2 == out_wf_02.size

    # this should be true because there are no flags
    out_ns_01 = uv_object.get_nsamples(0, 1)
    out_ns_02 = uv_object.get_nsamples(0, 2)
    assert np.allclose(out_ns_01, init_ns_01)
    assert np.isclose(init_ns_02[0, 0, 0], out_ns_02[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_in_time_drift(hera_uvh5):
    """Test the upsample_in_time method on drift mode data"""
    uv_object = hera_uvh5

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    max_integration_time = np.amin(uv_object.integration_time) / 2.0
    uv_object.upsample_in_time(
        max_integration_time, blt_order="baseline", allow_drift=True
    )

    assert np.allclose(uv_object.integration_time, max_integration_time)
    # we should double the size of the data arrays
    assert uv_object.data_array.size == 2 * init_data_size
    # output data should be the same
    out_wf = uv_object.get_data(0, 1)
    # we need a "large" tolerance given the "large" data
    new_tol = 1e-2 * np.amax(np.abs(uv_object.data_array))
    assert np.isclose(init_wf[0, 0, 0], out_wf[0, 0, 0], atol=new_tol)

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(init_ns[0, 0, 0], out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("driftscan", [True, False])
@pytest.mark.parametrize("partial_phase", [True, False])
def test_upsample_in_time_drift_no_phasing(hera_uvh5, driftscan, partial_phase):
    """Test the upsample_in_time method on drift mode data without phasing"""
    uv_object = hera_uvh5

    if driftscan:
        uv_object.phase(
            lon=0,
            lat=np.pi / 2,
            phase_frame="altaz",
            cat_type="driftscan",
            cat_name="foo",
        )

    if partial_phase:
        mask = np.full(uv_object.Nblts, False)
        mask[: uv_object.Nblts // 2] = True
        uv_object.phase(
            ra=0, dec=0, phase_frame="icrs", select_mask=mask, cat_name="bar"
        )

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    max_integration_time = np.amin(uv_object.integration_time) / 2.0
    # upsample with allow_drift=False
    uv_object.upsample_in_time(
        max_integration_time, blt_order="baseline", allow_drift=False
    )

    assert np.allclose(uv_object.integration_time, max_integration_time)
    # we should double the size of the data arrays
    assert uv_object.data_array.size == 2 * init_data_size
    # output data should be similar, but somewhat different because of the phasing
    out_wf = uv_object.get_data(0, 1)
    # we need a "large" tolerance given the "large" data
    new_tol = 1e-2 * np.amax(np.abs(uv_object.data_array))
    assert np.isclose(init_wf[0, 0, 0], out_wf[0, 0, 0], atol=new_tol)

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(init_ns[0, 0, 0], out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_downsample_in_time(hera_uvh5, future_shapes):
    """Test the downsample_in_time method"""
    uv_object = hera_uvh5

    if not future_shapes:
        uv_object.use_current_array_shapes()

    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0
    uv_object.downsample_in_time(
        min_int_time=min_integration_time, blt_order="baseline", minor_order="time"
    )

    # Should have half the size of the data array and all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))
    assert uv_object.data_array.size * 2 == init_data_size

    # output data should be the average
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]) / 2.0, out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    # Compare doing it with n_times_to_avg
    uv_object2.downsample_in_time(
        n_times_to_avg=2, blt_order="baseline", minor_order="time"
    )
    # histories are different when n_times_to_avg is set vs min_int_time
    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history
    assert uv_object == uv_object2

    assert not isinstance(uv_object.data_array, np.ma.MaskedArray)
    assert not isinstance(uv_object.nsample_array, np.ma.MaskedArray)


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_partial_flags(hera_uvh5):
    """Test the downsample_in_time method with partial flagging"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0

    # add flags and try again. With one of the 2 inputs flagged, the data should
    # just be the unflagged value and nsample should be half the unflagged one
    # and the output should not be flagged.
    inds01 = uv_object.antpair2ind(0, 1)
    uv_object.flag_array[inds01[0], 0, 0] = True
    uv_object2 = uv_object.copy()

    uv_object.downsample_in_time(
        min_int_time=min_integration_time, blt_order="baseline", minor_order="time"
    )
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[1, 0, 0], out_wf[0, 0, 0])

    # make sure nsamples is correct
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    # check that there are still no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0

    # Compare doing it with n_times_to_avg
    uv_object2.downsample_in_time(
        n_times_to_avg=2, blt_order="baseline", minor_order="time"
    )
    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history
    assert uv_object == uv_object2


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_downsample_in_time_totally_flagged(hera_uvh5, future_shapes):
    """Test the downsample_in_time method with totally flagged integrations"""
    uv_object = hera_uvh5

    if not future_shapes:
        uv_object.use_current_array_shapes()

    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0

    # add more flags and try again. When all the input points are flagged,
    # data and nsample should have the same results as no flags but the output
    # should be flagged
    inds01 = uv_object.antpair2ind(0, 1)
    if future_shapes:
        uv_object.flag_array[inds01[:2], 0, 0] = True
    else:
        uv_object.flag_array[inds01[:2], 0, 0, 0] = True
    uv_object2 = uv_object.copy()

    uv_object.downsample_in_time(
        min_int_time=min_integration_time, blt_order="baseline", minor_order="time"
    )
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]) / 2.0, out_wf[0, 0, 0])

    # make sure nsamples is correct
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    # check that the new sample is flagged
    out_flag = uv_object.get_flags(0, 1)
    assert out_flag[0, 0, 0]

    # Compare doing it with n_times_to_avg
    uv_object2.downsample_in_time(
        n_times_to_avg=2, blt_order="baseline", minor_order="time"
    )
    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history
    assert uv_object == uv_object2


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_uneven_samples(hera_uvh5):
    """Test the downsample_in_time method with uneven downsampling"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0

    # test again with a downsample factor that doesn't go evenly into the
    # number of samples
    min_integration_time = original_int_time * 3.0
    uv_object.downsample_in_time(
        min_int_time=min_integration_time,
        blt_order="baseline",
        minor_order="time",
        keep_ragged=False,
    )

    # Only some baselines have an even number of times, so the output integration time
    # is not uniformly the same. For the test case, we'll have *either* the original
    # integration time or twice that.
    assert np.all(
        np.logical_or(
            np.isclose(uv_object.integration_time, original_int_time),
            np.isclose(uv_object.integration_time, min_integration_time),
        )
    )

    # make sure integration time is correct
    # in this case, all integration times should be the target one
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))

    # as usual, the new data should be the average of the input data (3 points now)
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(np.mean(init_wf[0:3, 0, 0]), out_wf[0, 0, 0])

    # Compare doing it with n_times_to_avg
    uv_object2.downsample_in_time(
        n_times_to_avg=3, blt_order="baseline", minor_order="time", keep_ragged=False
    )
    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history
    assert uv_object == uv_object2


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_uneven_samples_keep_ragged(hera_uvh5):
    """Test downsample_in_time with uneven downsampling and keep_ragged=True."""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0

    # test again with a downsample factor that doesn't go evenly into the
    # number of samples
    min_integration_time = original_int_time * 3.0

    # test again with keep_ragged=False
    uv_object.downsample_in_time(
        min_int_time=min_integration_time,
        blt_order="baseline",
        minor_order="time",
        keep_ragged=True,
    )

    # as usual, the new data should be the average of the input data
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(np.mean(init_wf[0:3, 0, 0]), out_wf[0, 0, 0])

    # Compare doing it with n_times_to_avg
    uv_object2.downsample_in_time(
        n_times_to_avg=3, blt_order="baseline", minor_order="time", keep_ragged=True
    )
    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history
    assert uv_object == uv_object2


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_summing_correlator_mode(hera_uvh5):
    """Test the downsample_in_time method with summing correlator mode"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0
    uv_object.downsample_in_time(
        min_int_time=min_integration_time,
        blt_order="baseline",
        minor_order="time",
        summing_correlator_mode=True,
    )

    # Should have half the size of the data array and all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))
    assert uv_object.data_array.size * 2 == init_data_size

    # output data should be the sum
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]), out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_summing_correlator_mode_partial_flags(hera_uvh5):
    """Test the downsample_in_time method with summing correlator mode and
    partial flags
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0

    # add flags and try again. With one of the 2 inputs flagged, the data should
    # just be the unflagged value and nsample should be half the unflagged one
    # and the output should not be flagged.
    inds01 = uv_object.antpair2ind(0, 1)
    uv_object.flag_array[inds01[0], 0, 0] = True
    uv_object.downsample_in_time(
        min_int_time=min_integration_time,
        blt_order="baseline",
        minor_order="time",
        summing_correlator_mode=True,
    )
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[1, 0, 0], out_wf[0, 0, 0])

    # make sure nsamples is correct
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    # check that there are still no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_summing_correlator_mode_totally_flagged(hera_uvh5):
    """Test the downsample_in_time method with summing correlator mode and
    totally flagged integrations.
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0

    # add more flags and try again. When all the input points are flagged,
    # data and nsample should have the same results as no flags but the output
    # should be flagged
    inds01 = uv_object.antpair2ind(0, 1)
    uv_object.flag_array[inds01[:2], 0, 0] = True
    uv_object.downsample_in_time(
        min_int_time=min_integration_time,
        blt_order="baseline",
        minor_order="time",
        summing_correlator_mode=True,
    )
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]), out_wf[0, 0, 0])

    # make sure nsamples is correct
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    # check that the new sample is flagged
    out_flag = uv_object.get_flags(0, 1)
    assert out_flag[0, 0, 0]

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_summing_correlator_mode_uneven_samples(hera_uvh5):
    """Test the downsample_in_time method with summing correlator mode and
    uneven samples.
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # test again with a downsample factor that doesn't go evenly into the
    # number of samples
    min_integration_time = original_int_time * 3.0
    uv_object.downsample_in_time(
        min_int_time=min_integration_time,
        blt_order="baseline",
        minor_order="time",
        keep_ragged=False,
        summing_correlator_mode=True,
    )

    # Only some baselines have an even number of times, so the output integration time
    # is not uniformly the same. For the test case, we'll have *either* the original
    # integration time or twice that.
    assert np.all(
        np.logical_or(
            np.isclose(uv_object.integration_time, original_int_time),
            np.isclose(uv_object.integration_time, min_integration_time),
        )
    )

    # as usual, the new data should be the average of the input data (3 points now)
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(np.sum(init_wf[0:3, 0, 0]), out_wf[0, 0, 0])

    # make sure nsamples is correct
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(np.mean(init_ns[0:3, 0, 0]), out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_summing_correlator_mode_uneven_samples_drop_ragged(
    hera_uvh5,
):
    """Test the downsample_in_time method with summing correlator mode and
    uneven samples, dropping ragged ones.
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # test again with keep_ragged=False
    min_integration_time = original_int_time * 3.0
    uv_object.downsample_in_time(
        min_int_time=min_integration_time,
        blt_order="baseline",
        minor_order="time",
        keep_ragged=False,
        summing_correlator_mode=True,
    )

    # make sure integration time is correct
    # in this case, all integration times should be the target one
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))

    # as usual, the new data should be the average of the input data
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(np.sum(init_wf[0:3, 0, 0]), out_wf[0, 0, 0])

    # make sure nsamples is correct
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose(np.mean(init_ns[0:3, 0, 0]), out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_partial_downsample_in_time(hera_uvh5):
    """Test the downsample_in_time method without uniform downsampling"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # change a whole baseline's integration time
    bl_inds = uv_object.antpair2ind(0, 1)
    uv_object.integration_time[bl_inds] = uv_object.integration_time[0] * 2.0

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline")

    # save some values for later
    init_wf_01 = uv_object.get_data(0, 1)
    init_wf_02 = uv_object.get_data(0, 2)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns_01 = uv_object.get_nsamples(0, 1)
    init_ns_02 = uv_object.get_nsamples(0, 2)

    # change the target integration time
    min_integration_time = np.amax(uv_object.integration_time)
    uv_object.downsample_in_time(
        min_int_time=min_integration_time, blt_order="baseline"
    )

    # Should have all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))

    # output data should be the same
    out_wf_01 = uv_object.get_data(0, 1)
    out_wf_02 = uv_object.get_data(0, 2)
    assert np.all(init_wf_01 == out_wf_01)
    assert np.isclose(
        (init_wf_02[0, 0, 0] + init_wf_02[1, 0, 0]) / 2.0, out_wf_02[0, 0, 0]
    )

    # this should be true because there are no flags
    out_ns_01 = uv_object.get_nsamples(0, 1)
    out_ns_02 = uv_object.get_nsamples(0, 2)
    assert np.allclose(out_ns_01, init_ns_01)
    assert np.isclose(
        (init_ns_02[0, 0, 0] + init_ns_02[1, 0, 0]) / 2.0, out_ns_02[0, 0, 0]
    )

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_drift(hera_uvh5):
    """Test the downsample_in_time method on drift mode data"""
    uv_object = hera_uvh5

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0
    uv_object.downsample_in_time(
        min_int_time=min_integration_time, blt_order="baseline", allow_drift=True
    )

    # Should have half the size of the data array and all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))
    assert uv_object.data_array.size * 2 == init_data_size

    # output data should be the average
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]) / 2.0, out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0

    # Compare doing it with n_times_to_avg
    uv_object2.downsample_in_time(
        n_times_to_avg=2, blt_order="baseline", allow_drift=True
    )
    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history
    assert uv_object == uv_object2


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("driftscan", [True, False])
@pytest.mark.parametrize("partial_phase", [True, False])
def test_downsample_in_time_drift_no_phasing(hera_uvh5, driftscan, partial_phase):
    """Test the downsample_in_time method on drift mode data without phasing"""
    uv_object = hera_uvh5

    if driftscan:
        uv_object.phase(
            lon=0,
            lat=np.pi / 2,
            phase_frame="altaz",
            cat_type="driftscan",
            cat_name="foo",
        )

    if partial_phase:
        mask = np.full(uv_object.Nblts, False)
        mask[uv_object.Nblts // 2 :] = True
        uv_object.phase(
            ra=0, dec=0, phase_frame="icrs", select_mask=mask, cat_name="bar"
        )

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0

    # try again with allow_drift=False
    uv_object.downsample_in_time(
        min_int_time=min_integration_time, blt_order="baseline", allow_drift=False
    )

    # Should have half the size of the data array and all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))
    assert uv_object.data_array.size * 2 == init_data_size

    # output data should be similar to the average, but somewhat different
    # because of the phasing
    out_wf = uv_object.get_data(0, 1)
    new_tol = 5e-2 * np.amax(np.abs(uv_object.data_array))
    assert np.isclose(
        (init_wf[0, 0, 0] + init_wf[1, 0, 0]) / 2.0, out_wf[0, 0, 0], atol=new_tol
    )

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0

    # Compare doing it with n_times_to_avg
    uv_object2.downsample_in_time(
        n_times_to_avg=2, blt_order="baseline", minor_order="time"
    )
    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history
    assert uv_object == uv_object2


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_nsample_precision(hera_uvh5):
    """Test the downsample_in_time method with a half-precision nsample_array"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    original_int_time = np.amax(uv_object.integration_time)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the target integration time
    min_integration_time = original_int_time * 2.0

    # add flags and try again. With one of the 2 inputs flagged, the data should
    # just be the unflagged value and nsample should be half the unflagged one
    # and the output should not be flagged.
    inds01 = uv_object.antpair2ind(0, 1)
    uv_object.flag_array[inds01[0], 0, 0] = True
    uv_object2 = uv_object.copy()

    # change precision of nsample array
    uv_object.nsample_array = uv_object.nsample_array.astype(np.float16)
    uv_object.downsample_in_time(
        min_int_time=min_integration_time, blt_order="baseline", minor_order="time"
    )
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose(init_wf[1, 0, 0], out_wf[0, 0, 0])

    # make sure nsamples is correct
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    # make sure nsamples has the right dtype
    assert uv_object.nsample_array.dtype.type is np.float16

    # check that there are still no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0

    # Compare doing it with n_times_to_avg
    uv_object2.nsample_array = uv_object2.nsample_array.astype(np.float16)
    uv_object2.downsample_in_time(
        n_times_to_avg=2, blt_order="baseline", minor_order="time"
    )
    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history
    assert uv_object == uv_object2

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_errors(hera_uvh5):
    """Test various errors and warnings are raised"""
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # raise an error if set neither min_int_time and n_times_to_avg
    with pytest.raises(
        ValueError, match="Either min_int_time or n_times_to_avg must be set."
    ):
        uv_object.downsample_in_time()

    # raise an error if set both min_int_time and n_times_to_avg
    with pytest.raises(
        ValueError, match="Only one of min_int_time or n_times_to_avg can be set."
    ):
        uv_object.downsample_in_time(
            min_int_time=2 * np.amin(uv_object.integration_time), n_times_to_avg=2
        )
    # raise an error if only one time
    uv_object2 = uv_object.copy()
    uv_object2.select(times=uv_object2.time_array[0])
    with pytest.raises(
        ValueError, match="Only one time in this object, cannot downsample."
    ):
        uv_object2.downsample_in_time(n_times_to_avg=2)

    # raise an error for a too-large integration time
    max_integration_time = 1e3 * np.amax(uv_object.integration_time)
    with pytest.raises(
        ValueError, match="Increasing the integration time by more than"
    ):
        uv_object.downsample_in_time(min_int_time=max_integration_time)

    # raise an error if phase centers change within an downsampling window
    uv_object2 = uv_object.copy()
    uv_object2.reorder_blts(order="time")
    mask = np.full(uv_object2.Nblts, False)
    mask[: uv_object2.Nblts // 3] = True
    uv_object2.phase(ra=0, dec=0, phase_frame="icrs", select_mask=mask, cat_name="foo")
    with pytest.raises(
        ValueError,
        match="Multiple phase centers included in a downsampling window. Use `phase` "
        "to phase to a single phase center or decrease the `min_int_time` or "
        "`n_times_to_avg` parameter to avoid multiple phase centers being included in "
        "a downsampling window.",
    ):
        uv_object2.downsample_in_time(n_times_to_avg=2)

    # catch a warning for doing no work
    uv_object2 = uv_object.copy()
    max_integration_time = 0.5 * np.amin(uv_object.integration_time)
    with uvtest.check_warnings(
        UserWarning, match="All values in the integration_time array are already longer"
    ):
        uv_object.downsample_in_time(min_int_time=max_integration_time)

    assert uv_object == uv_object2
    del uv_object2

    # raise an error if n_times_to_avg is not an integer
    with pytest.raises(ValueError, match="n_times_to_avg must be an integer."):
        uv_object.downsample_in_time(n_times_to_avg=2.5)

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # make a gap in the times to check a warning about that
    inds01 = uv_object.antpair2ind(0, 1)
    initial_int_time = uv_object.integration_time[inds01[0]]
    # time array is in jd, integration time is in sec
    uv_object.time_array[inds01[-1]] += initial_int_time / (24 * 3600)
    uv_object.Ntimes += 1
    min_integration_time = 2 * np.amin(uv_object.integration_time)
    times_01 = uv_object.get_times(0, 1)
    assert np.unique(np.diff(times_01)).size > 1
    with uvtest.check_warnings(UserWarning, "There is a gap in the times of baseline"):
        uv_object.downsample_in_time(min_int_time=min_integration_time)

    # Should have half the size of the data array and all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))
    assert uv_object.data_array.size * 2 == init_data_size

    # output data should be the average
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]) / 2.0, out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_int_time_mismatch_warning(hera_uvh5):
    """Test warning in downsample_in_time about mismatch between integration
    times and the time between integrations.
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # save some values for later
    init_data_size = uv_object.data_array.size
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # change the integration times to catch a warning about integration times
    # not matching the time delta between integrations
    uv_object.integration_time *= 0.5
    min_integration_time = 2 * np.amin(uv_object.integration_time)
    with uvtest.check_warnings(
        UserWarning,
        match="The time difference between integrations is not the same",
        nwarnings=10,
    ):
        uv_object.downsample_in_time(min_int_time=min_integration_time)

    # Should have half the size of the data array and all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))
    assert uv_object.data_array.size * 2 == init_data_size

    # output data should be the average
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]) / 2.0, out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_varying_integration_time(hera_uvh5):
    """Test downsample_in_time handling of file with integration time changing
    within a baseline
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # test handling (& warnings) with varying integration time in a baseline
    # First, change both integration time & time array to match
    inds01 = uv_object.antpair2ind(0, 1)
    initial_int_time = uv_object.integration_time[inds01[0]]
    # time array is in jd, integration time is in sec
    uv_object.time_array[inds01[-2]] += (initial_int_time / 2) / (24 * 3600)
    uv_object.time_array[inds01[-1]] += (3 * initial_int_time / 2) / (24 * 3600)
    uv_object.integration_time[inds01[-2:]] += initial_int_time
    uv_object.Ntimes = np.unique(uv_object.time_array).size
    min_integration_time = 2 * np.amin(uv_object.integration_time)
    # check that there are no warnings about inconsistencies between
    # integration_time & time_array
    with uvtest.check_warnings(None):
        uv_object.downsample_in_time(min_int_time=min_integration_time)

    # Should have all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))

    out_wf = uv_object.get_data(0, 1)

    n_times_in = init_wf.shape[0]
    n_times_out = out_wf.shape[0]
    assert n_times_out == (n_times_in - 2) / 2 + 2

    # output data should be the average for the first set
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]) / 2.0, out_wf[0, 0, 0])
    # last 2 time samples should be identical to initial ones
    assert np.isclose(init_wf[-1, 0, 0], out_wf[-1, 0, 0])
    assert np.isclose(init_wf[-2, 0, 0], out_wf[-2, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])
    assert np.isclose(init_ns[-1, 0, 0], out_ns[-1, 0, 0])
    assert np.isclose(init_ns[-2, 0, 0], out_ns[2, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_varying_int_time_partial_flags(hera_uvh5):
    """Test downsample_in_time handling of file with integration time changing
    within a baseline and partial flagging.
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # downselect to 14 times and one baseline
    uv_object.select(times=np.unique(uv_object.time_array)[:14])

    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0

    # change last 2 integrations to be twice as long
    # (so 12 normal length, 2 double length)
    # change integration time & time array to match
    inds01 = uv_object.antpair2ind(0, 1)
    initial_int_time = uv_object.integration_time[inds01[0]]
    # time array is in jd, integration time is in sec
    uv_object.time_array[inds01[-2]] += (initial_int_time / 2) / (24 * 3600)
    uv_object.time_array[inds01[-1]] += (3 * initial_int_time / 2) / (24 * 3600)
    uv_object.integration_time[inds01[-2:]] += initial_int_time
    uv_object.Ntimes = np.unique(uv_object.time_array).size

    # add a flag on last time
    uv_object.flag_array[inds01[-1]] = True
    # add a flag on thrid to last time
    uv_object.flag_array[inds01[-3]] = True

    uv_object2 = uv_object.copy()

    with uvtest.check_warnings(None):
        uv_object.downsample_in_time(min_int_time=4 * initial_int_time)
    with uvtest.check_warnings(None):
        uv_object.downsample_in_time(min_int_time=8 * initial_int_time)
    with uvtest.check_warnings(None):
        uv_object2.downsample_in_time(min_int_time=8 * initial_int_time)

    assert uv_object.history != uv_object2.history
    uv_object2.history = uv_object.history

    assert uv_object == uv_object2
    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_downsample_in_time_varying_integration_time_warning(hera_uvh5):
    """Test downsample_in_time handling of file with integration time changing
    within a baseline, but without adjusting the time_array so there is a mismatch.
    """
    uv_object = hera_uvh5
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))
    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")

    # save some values for later
    init_wf = uv_object.get_data(0, 1)
    # check that there are no flags
    assert np.nonzero(uv_object.flag_array)[0].size == 0
    init_ns = uv_object.get_nsamples(0, 1)

    # Next, change just integration time, so time array doesn't match
    inds01 = uv_object.antpair2ind(0, 1)
    initial_int_time = uv_object.integration_time[inds01[0]]
    uv_object.integration_time[inds01[-2:]] += initial_int_time
    min_integration_time = 2 * np.amin(uv_object.integration_time)
    with uvtest.check_warnings(
        UserWarning, "The time difference between integrations is different than"
    ):
        uv_object.downsample_in_time(min_int_time=min_integration_time)

    # Should have all the new integration time
    # (for this file with 20 integrations and a factor of 2 downsampling)
    assert np.all(np.isclose(uv_object.integration_time, min_integration_time))

    # output data should be the average
    out_wf = uv_object.get_data(0, 1)
    assert np.isclose((init_wf[0, 0, 0] + init_wf[1, 0, 0]) / 2.0, out_wf[0, 0, 0])

    # this should be true because there are no flags
    out_ns = uv_object.get_nsamples(0, 1)
    assert np.isclose((init_ns[0, 0, 0] + init_ns[1, 0, 0]) / 2.0, out_ns[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:Data will be unphased and rephased")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_downsample_in_time(hera_uvh5):
    """Test round trip works"""
    uv_object = hera_uvh5

    # Using astropy here (and elsewhere) to match previously calculated values
    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    max_integration_time = np.amin(uv_object.integration_time) / 2.0
    uv_object.upsample_in_time(max_integration_time, blt_order="baseline")
    assert np.amax(uv_object.integration_time) <= max_integration_time
    new_Nblts = uv_object.Nblts

    # check that calling upsample again with the same max_integration_time
    # gives warning and does nothing
    with uvtest.check_warnings(
        UserWarning, "All values in the integration_time array are already longer"
    ):
        uv_object.upsample_in_time(max_integration_time, blt_order="baseline")
    assert uv_object.Nblts == new_Nblts

    # check that calling upsample again with the almost the same max_integration_time
    # gives warning and does nothing
    small_number = 0.9 * uv_object._integration_time.tols[1]
    with uvtest.check_warnings(
        UserWarning, "All values in the integration_time array are already longer"
    ):
        uv_object.upsample_in_time(
            max_integration_time - small_number, blt_order="baseline"
        )
    assert uv_object.Nblts == new_Nblts

    uv_object.downsample_in_time(
        min_int_time=np.amin(uv_object2.integration_time), blt_order="baseline"
    )

    # increase tolerance on LST if iers.conf.auto_max_age is set to None, as we
    # do in testing if the iers url is down. See conftest.py for more info.
    if iers.conf.auto_max_age is None:
        uv_object._lst_array.tols = (0, 1e-4)

    # make sure that history is correct
    assert (
        "Upsampled data to 0.939524 second integration time using pyuvdata."
        in uv_object.history
    )
    assert (
        "Downsampled data to 1.879048 second integration time using pyuvdata."
        in uv_object.history
    )

    # overwrite history and check for equality
    uv_object.history = uv_object2.history
    assert uv_object == uv_object2

    # check that calling downsample again with the same min_integration_time
    # gives warning and does nothing
    with uvtest.check_warnings(
        UserWarning, match="All values in the integration_time array are already longer"
    ):
        uv_object.downsample_in_time(
            min_int_time=np.amin(uv_object2.integration_time), blt_order="baseline"
        )
    assert uv_object.Nblts == uv_object2.Nblts

    # check that calling upsample again with the almost the same min_integration_time
    # gives warning and does nothing
    with uvtest.check_warnings(
        UserWarning, match="All values in the integration_time array are already longer"
    ):
        uv_object.upsample_in_time(
            np.amin(uv_object2.integration_time) + small_number, blt_order="baseline"
        )

    assert uv_object.Nblts == uv_object2.Nblts

    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:Data will be unphased and rephased")
@pytest.mark.filterwarnings("ignore:There is a gap in the times of baseline")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_upsample_downsample_in_time_odd_resample(hera_uvh5, future_shapes):
    """Test round trip works with odd resampling"""
    uv_object = hera_uvh5

    if not future_shapes:
        uv_object.use_current_array_shapes()

    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    # try again with a resampling factor of 3 (test odd numbers)
    max_integration_time = np.amin(uv_object.integration_time) / 3.0
    uv_object.upsample_in_time(max_integration_time, blt_order="baseline")
    assert np.amax(uv_object.integration_time) <= max_integration_time

    uv_object.downsample_in_time(
        np.amin(uv_object2.integration_time), blt_order="baseline"
    )

    # increase tolerance on LST if iers.conf.auto_max_age is set to None, as we
    # do in testing if the iers url is down. See conftest.py for more info.
    if iers.conf.auto_max_age is None:
        uv_object._lst_array.tols = (0, 1e-4)

    # make sure that history is correct
    assert (
        "Upsampled data to 0.626349 second integration time using pyuvdata."
        in uv_object.history
    )
    assert (
        "Downsampled data to 1.879048 second integration time using pyuvdata."
        in uv_object.history
    )

    # overwrite history and check for equality
    uv_object.history = uv_object2.history
    assert uv_object == uv_object2


@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_upsample_downsample_in_time_metadata_only(hera_uvh5):
    """Test round trip works with metadata-only objects"""
    uv_object = hera_uvh5

    # drop the data arrays
    uv_object.data_array = None
    uv_object.flag_array = None
    uv_object.nsample_array = None

    uv_object.phase_to_time(Time(uv_object.time_array[0], format="jd"))

    # reorder to make sure we get the right value later
    uv_object.reorder_blts(order="baseline", minor_order="time")
    uv_object2 = uv_object.copy()

    max_integration_time = np.amin(uv_object.integration_time) / 2.0
    uv_object.upsample_in_time(max_integration_time, blt_order="baseline")
    assert np.amax(uv_object.integration_time) <= max_integration_time

    uv_object.downsample_in_time(
        np.amin(uv_object2.integration_time), blt_order="baseline"
    )

    # increase tolerance on LST if iers.conf.auto_max_age is set to None, as we
    # do in testing if the iers url is down. See conftest.py for more info.
    if iers.conf.auto_max_age is None:
        uv_object._lst_array.tols = (0, 1e-4)

    # make sure that history is correct
    assert (
        "Upsampled data to 0.939524 second integration time using pyuvdata."
        in uv_object.history
    )
    assert (
        "Downsampled data to 1.879048 second integration time using pyuvdata."
        in uv_object.history
    )

    # overwrite history and check for equality
    uv_object.history = uv_object2.history
    assert uv_object == uv_object2


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Unknown phase types are no longer supported,")
@pytest.mark.filterwarnings("ignore:Telescope mock-HERA is not in known_telescopes")
@pytest.mark.filterwarnings("ignore:There is a gap in the times of baseline")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize("driftscan", [True, False])
@pytest.mark.parametrize("partial_phase", [True, False])
def test_resample_in_time(bda_test_file, future_shapes, driftscan, partial_phase):
    """Test the resample_in_time method"""
    # Note this file has slight variations in the delta t between integrations
    # that causes our gap test to issue a warning, but the variations are small
    # We aren't worried about them, so we filter those warnings
    uv_object = bda_test_file

    if not future_shapes:
        uv_object.use_current_array_shapes()

    if driftscan:
        uv_object.phase(
            lon=0,
            lat=np.pi / 2,
            phase_frame="altaz",
            cat_type="driftscan",
            cat_name="zenith",
        )

    if partial_phase:
        mask = np.full(uv_object.Nblts, False)
        mask[: uv_object.Nblts // 2] = True
        uv_object.phase(
            ra=0, dec=0, phase_frame="icrs", select_mask=mask, cat_name="foo"
        )

    # save some initial info
    # 2s integration time
    init_data_1_136 = uv_object.get_data((1, 136))
    # 4s integration time
    init_data_1_137 = uv_object.get_data((1, 137))
    # 8s integration time
    init_data_1_138 = uv_object.get_data((1, 138))
    # 16s integration time
    init_data_136_137 = uv_object.get_data((136, 137))

    uv_object.resample_in_time(8, allow_drift=True)
    # Should have all the target integration time
    assert np.all(np.isclose(uv_object.integration_time, 8))

    # 2s integration time
    out_data_1_136 = uv_object.get_data((1, 136))
    # 4s integration time
    out_data_1_137 = uv_object.get_data((1, 137))
    # 8s integration time
    out_data_1_138 = uv_object.get_data((1, 138))
    # 16s integration time
    out_data_136_137 = uv_object.get_data((136, 137))

    # check array sizes make sense
    assert out_data_1_136.size * 4 == init_data_1_136.size
    assert out_data_1_137.size * 2 == init_data_1_137.size
    assert out_data_1_138.size == init_data_1_138.size
    assert out_data_136_137.size / 2 == init_data_136_137.size

    # check some values
    assert np.isclose(np.mean(init_data_1_136[0:4, 0, 0]), out_data_1_136[0, 0, 0])
    assert np.isclose(np.mean(init_data_1_137[0:2, 0, 0]), out_data_1_137[0, 0, 0])
    assert np.isclose(init_data_1_138[0, 0, 0], out_data_1_138[0, 0, 0])
    assert np.isclose(init_data_136_137[0, 0, 0], out_data_136_137[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:Telescope mock-HERA is not in known_telescopes")
@pytest.mark.filterwarnings("ignore:There is a gap in the times of baseline")
def test_resample_in_time_downsample_only(bda_test_file):
    """Test resample_in_time with downsampling only"""
    # Note this file has slight variations in the delta t between integrations
    # that causes our gap test to issue a warning, but the variations are small
    # We aren't worried about them, so we filter those warnings
    uv_object = bda_test_file

    # save some initial info
    # 2s integration time
    init_data_1_136 = uv_object.get_data((1, 136))
    # 4s integration time
    init_data_1_137 = uv_object.get_data((1, 137))
    # 8s integration time
    init_data_1_138 = uv_object.get_data((1, 138))
    # 16s integration time
    init_data_136_137 = uv_object.get_data((136, 137))

    # resample again, with only_downsample set
    uv_object.resample_in_time(8, only_downsample=True, allow_drift=True)
    # Should have all less than or equal to the target integration time
    assert np.all(
        np.logical_or(
            np.isclose(uv_object.integration_time, 8),
            np.isclose(uv_object.integration_time, 16),
        )
    )

    # 2s integration time
    out_data_1_136 = uv_object.get_data((1, 136))
    # 4s integration time
    out_data_1_137 = uv_object.get_data((1, 137))
    # 8s integration time
    out_data_1_138 = uv_object.get_data((1, 138))
    # 16s integration time
    out_data_136_137 = uv_object.get_data((136, 137))

    # check array sizes make sense
    assert out_data_1_136.size * 4 == init_data_1_136.size
    assert out_data_1_137.size * 2 == init_data_1_137.size
    assert out_data_1_138.size == init_data_1_138.size
    assert out_data_136_137.size == init_data_136_137.size

    # check some values
    assert np.isclose(np.mean(init_data_1_136[0:4, 0, 0]), out_data_1_136[0, 0, 0])
    assert np.isclose(np.mean(init_data_1_137[0:2, 0, 0]), out_data_1_137[0, 0, 0])
    assert np.isclose(init_data_1_138[0, 0, 0], out_data_1_138[0, 0, 0])
    assert np.isclose(init_data_136_137[0, 0, 0], out_data_136_137[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:Telescope mock-HERA is not in known_telescopes")
@pytest.mark.filterwarnings("ignore:There is a gap in the times of baseline")
def test_resample_in_time_only_upsample(bda_test_file):
    """Test resample_in_time with only upsampling"""
    # Note this file has slight variations in the delta t between integrations
    # that causes our gap test to issue a warning, but the variations are small
    # We aren't worried about them, so we filter those warnings
    uv_object = bda_test_file

    # save some initial info
    # 2s integration time
    init_data_1_136 = uv_object.get_data((1, 136))
    # 4s integration time
    init_data_1_137 = uv_object.get_data((1, 137))
    # 8s integration time
    init_data_1_138 = uv_object.get_data((1, 138))
    # 16s integration time
    init_data_136_137 = uv_object.get_data((136, 137))

    # again, with only_upsample set
    uv_object.resample_in_time(8, only_upsample=True, allow_drift=True)
    # Should have all greater than or equal to the target integration time
    assert np.all(
        np.logical_or(
            np.logical_or(
                np.isclose(uv_object.integration_time, 2.0),
                np.isclose(uv_object.integration_time, 4.0),
            ),
            np.isclose(uv_object.integration_time, 8.0),
        )
    )

    # 2s integration time
    out_data_1_136 = uv_object.get_data((1, 136))
    # 4s integration time
    out_data_1_137 = uv_object.get_data((1, 137))
    # 8s integration time
    out_data_1_138 = uv_object.get_data((1, 138))
    # 16s integration time
    out_data_136_137 = uv_object.get_data((136, 137))

    # check array sizes make sense
    assert out_data_1_136.size == init_data_1_136.size
    assert out_data_1_137.size == init_data_1_137.size
    assert out_data_1_138.size == init_data_1_138.size
    assert out_data_136_137.size / 2 == init_data_136_137.size

    # check some values
    assert np.isclose(init_data_1_136[0, 0, 0], out_data_1_136[0, 0, 0])
    assert np.isclose(init_data_1_137[0, 0, 0], out_data_1_137[0, 0, 0])
    assert np.isclose(init_data_1_138[0, 0, 0], out_data_1_138[0, 0, 0])
    assert np.isclose(init_data_136_137[0, 0, 0], out_data_136_137[0, 0, 0])

    return


@pytest.mark.filterwarnings("ignore:Telescope mock-HERA is not in known_telescopes")
@pytest.mark.filterwarnings("ignore:There is a gap in the times of baseline")
def test_resample_in_time_partial_flags(bda_test_file):
    """Test resample_in_time with partial flags"""
    # Note this file has slight variations in the delta t between integrations
    # that causes our gap test to issue a warning, but the variations are small
    # We aren't worried about them, so we filter those warnings
    uv = bda_test_file
    # For ease, select a single baseline
    uv.select(bls=[(1, 136)])
    # Flag one time
    uv.flag_array[0] = True
    uv2 = uv.copy()

    # Downsample in two stages
    uv.resample_in_time(4.0, only_downsample=True)
    uv.resample_in_time(8.0, only_downsample=True)
    # Downsample in a single stage
    uv2.resample_in_time(8.0, only_downsample=True)

    assert uv.history != uv2.history
    uv2.history = uv.history
    assert uv == uv2
    return


@pytest.mark.filterwarnings("ignore:There is a gap in the times of baseline")
def test_downsample_in_time_mwa():
    """
    Test resample in time works with numerical weirdnesses.

    In particular, when min_int_time is not quite an integer mulitple of
    integration_time. This text broke with a prior bug (see issue 773).
    """
    filename = os.path.join(DATA_PATH, "mwa_integration_time.uvh5")
    uv = UVData()
    uv.read(filename, use_future_array_shapes=True)
    uv.phase_to_time(np.mean(uv.time_array))
    uv_object2 = uv.copy()

    # all data within 5 milliseconds of 2 second integrations
    assert np.allclose(uv.integration_time, 2, atol=5e-3)
    min_int_time = 4.0
    uv.resample_in_time(min_int_time, only_downsample=True, keep_ragged=False)

    assert np.all(uv.integration_time > (min_int_time - 5e-3))

    # Now do the human expected thing:
    init_data = uv_object2.get_data((61, 58))
    uv_object2.downsample_in_time(n_times_to_avg=2, keep_ragged=False)

    assert uv_object2.Ntimes == 5

    out_data = uv_object2.get_data((61, 58))

    assert np.isclose(np.mean(init_data[0:2, 0, 0]), out_data[0, 0, 0])


@pytest.mark.filterwarnings("ignore:There is a gap in the times of baseline")
def test_resample_in_time_warning():
    filename = os.path.join(DATA_PATH, "mwa_integration_time.uvh5")
    uv = UVData()
    uv.read(filename, use_future_array_shapes=True)

    uv2 = uv.copy()

    with uvtest.check_warnings(
        UserWarning, match="No resampling will be done because target time"
    ):
        uv.resample_in_time(3, keep_ragged=False)

    assert uv2 == uv


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_frequency_average(casa_uvfits, future_shapes):
    """Test averaging in frequency."""
    uvobj = casa_uvfits

    if not future_shapes:
        uvobj.use_current_array_shapes()
    uvobj2 = uvobj.copy()

    eq_coeffs = np.tile(
        np.arange(uvobj.Nfreqs, dtype=np.float64), (uvobj.Nants_telescope, 1)
    )
    uvobj.eq_coeffs = eq_coeffs
    uvobj.check()

    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0

    with uvtest.check_warnings(UserWarning, "eq_coeffs vary by frequency"):
        uvobj.frequency_average(2),

    assert uvobj.Nfreqs == (uvobj2.Nfreqs / 2)

    if future_shapes:
        expected_freqs = uvobj2.freq_array.reshape(int(uvobj2.Nfreqs / 2), 2).mean(
            axis=1
        )
    else:
        expected_freqs = uvobj2.freq_array.reshape(1, int(uvobj2.Nfreqs / 2), 2).mean(
            axis=2
        )
    assert np.max(np.abs(uvobj.freq_array - expected_freqs)) == 0

    expected_coeffs = eq_coeffs.reshape(
        uvobj2.Nants_telescope, int(uvobj2.Nfreqs / 2), 2
    ).mean(axis=2)
    assert np.max(np.abs(uvobj.eq_coeffs - expected_coeffs)) == 0

    # no flagging, so the following is true
    expected_data = uvobj2.get_data(1, 2, squeeze="none")
    if future_shapes:
        reshape_tuple = (
            expected_data.shape[0],
            int(uvobj2.Nfreqs / 2),
            2,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).mean(axis=2)

    else:
        reshape_tuple = (
            expected_data.shape[0],
            1,
            int(uvobj2.Nfreqs / 2),
            2,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).mean(axis=3)
    assert np.allclose(uvobj.get_data(1, 2, squeeze="none"), expected_data)

    assert np.nonzero(uvobj.flag_array)[0].size == 0

    assert not isinstance(uvobj.data_array, np.ma.MaskedArray)
    assert not isinstance(uvobj.nsample_array, np.ma.MaskedArray)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_frequency_average_uneven(casa_uvfits, future_shapes):
    """Test averaging in frequency with a number that is not a factor of Nfreqs."""
    uvobj = casa_uvfits

    if not future_shapes:
        uvobj.use_current_array_shapes()
    uvobj2 = uvobj.copy()

    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0

    with uvtest.check_warnings(
        UserWarning,
        [
            "Nfreqs does not divide by `n_chan_to_avg` evenly. The final 1 "
            "frequencies will be excluded, to control which frequencies to exclude, "
            "use a select to control.",
            "The uvw_array does not match the expected values",
        ],
    ):
        uvobj.frequency_average(7)

    assert uvobj2.Nfreqs % 7 != 0

    assert uvobj.Nfreqs == (uvobj2.Nfreqs // 7)

    if future_shapes:
        expected_freqs = uvobj2.freq_array[np.arange((uvobj2.Nfreqs // 7) * 7)]
        expected_freqs = expected_freqs.reshape(int(uvobj2.Nfreqs // 7), 7).mean(axis=1)
    else:
        expected_freqs = uvobj2.freq_array[:, np.arange((uvobj2.Nfreqs // 7) * 7)]
        expected_freqs = expected_freqs.reshape(1, int(uvobj2.Nfreqs // 7), 7).mean(
            axis=2
        )
    assert np.max(np.abs(uvobj.freq_array - expected_freqs)) == 0

    # no flagging, so the following is true
    expected_data = uvobj2.get_data(1, 2, squeeze="none")
    if future_shapes:
        expected_data = expected_data[:, 0 : ((uvobj2.Nfreqs // 7) * 7), :]
        reshape_tuple = (
            expected_data.shape[0],
            int(uvobj2.Nfreqs // 7),
            7,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).mean(axis=2)
    else:
        expected_data = expected_data[:, :, 0 : ((uvobj2.Nfreqs // 7) * 7), :]
        reshape_tuple = (
            expected_data.shape[0],
            1,
            int(uvobj2.Nfreqs // 7),
            7,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).mean(axis=3)
    assert np.allclose(uvobj.get_data(1, 2, squeeze="none"), expected_data)

    assert np.nonzero(uvobj.flag_array)[0].size == 0


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_frequency_average_flagging(casa_uvfits, future_shapes):
    """Test averaging in frequency with flagging all samples averaged."""
    uvobj = casa_uvfits

    if not future_shapes:
        uvobj.use_current_array_shapes()
    uvobj2 = uvobj.copy()

    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0

    # apply some flagging for testing
    inds01 = uvobj.antpair2ind(1, 2)
    if future_shapes:
        uvobj.flag_array[inds01[0], 0:2, :] = True
    else:
        uvobj.flag_array[inds01[0], :, 0:2, :] = True
    assert np.nonzero(uvobj.flag_array)[0].size == uvobj.Npols * 2

    uvobj.frequency_average(2)

    assert uvobj.Nfreqs == (uvobj2.Nfreqs / 2)

    if future_shapes:
        expected_freqs = uvobj2.freq_array.reshape(int(uvobj2.Nfreqs / 2), 2).mean(
            axis=1
        )
    else:
        expected_freqs = uvobj2.freq_array.reshape(1, int(uvobj2.Nfreqs / 2), 2).mean(
            axis=2
        )
    assert np.max(np.abs(uvobj.freq_array - expected_freqs)) == 0

    expected_data = uvobj2.get_data(1, 2, squeeze="none")
    if future_shapes:
        reshape_tuple = (
            expected_data.shape[0],
            int(uvobj2.Nfreqs / 2),
            2,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).mean(axis=2)
    else:
        reshape_tuple = (
            expected_data.shape[0],
            1,
            int(uvobj2.Nfreqs / 2),
            2,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).mean(axis=3)
    assert np.allclose(uvobj.get_data(1, 2, squeeze="none"), expected_data)

    if future_shapes:
        assert np.sum(uvobj.flag_array[inds01[0], 0, :]) == 4
    else:
        assert np.sum(uvobj.flag_array[inds01[0], :, 0, :]) == 4
    assert np.nonzero(uvobj.flag_array)[0].size == uvobj.Npols
    if future_shapes:
        assert np.nonzero(uvobj.flag_array[inds01[1:], 0, :])[0].size == 0
    else:
        assert np.nonzero(uvobj.flag_array[inds01[1:], :, 0, :])[0].size == 0


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_frequency_average_flagging_partial(casa_uvfits):
    """Test averaging in frequency with flagging only one sample averaged."""
    uvobj = casa_uvfits
    uvobj2 = uvobj.copy()
    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0

    # apply some flagging for testing
    inds01 = uvobj.antpair2ind(1, 2)
    uvobj.flag_array[inds01[0], 0, :] = True
    assert np.nonzero(uvobj.flag_array)[0].size == uvobj.Npols

    uvobj.frequency_average(2)

    assert uvobj.Nfreqs == (uvobj2.Nfreqs / 2)

    # TODO: Spw axis to be collapsed in future release
    expected_freqs = uvobj2.freq_array.reshape(1, int(uvobj2.Nfreqs / 2), 2).mean(
        axis=2
    )
    assert np.max(np.abs(uvobj.freq_array - expected_freqs)) == 0

    expected_data = uvobj2.get_data(1, 2, squeeze="none")
    # TODO: Spw axis to be collapsed in future release
    reshape_tuple = (expected_data.shape[0], int(uvobj2.Nfreqs / 2), 2, uvobj2.Npols)
    expected_data = expected_data.reshape(reshape_tuple).mean(axis=2)
    expected_data[0, 0, :] = uvobj2.data_array[inds01[0], 1, :]
    assert np.allclose(uvobj.get_data(1, 2, squeeze="none"), expected_data)

    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_frequency_average_flagging_full_and_partial(casa_uvfits):
    """
    Test averaging in frequency with flagging all of one and only one of
    another sample averaged.
    """
    uvobj = casa_uvfits
    uvobj2 = uvobj.copy()
    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0

    # apply some flagging for testing
    inds01 = uvobj.antpair2ind(1, 2)
    uvobj.flag_array[inds01[0], 0:3, :] = True
    assert np.nonzero(uvobj.flag_array)[0].size == uvobj.Npols * 3

    uvobj.frequency_average(2)

    assert uvobj.Nfreqs == (uvobj2.Nfreqs / 2)

    # TODO: Spw axis to be collapsed in future release
    expected_freqs = uvobj2.freq_array.reshape(1, int(uvobj2.Nfreqs / 2), 2).mean(
        axis=2
    )
    assert np.max(np.abs(uvobj.freq_array - expected_freqs)) == 0

    expected_data = uvobj2.get_data(1, 2, squeeze="none")
    # TODO: Spw axis to be collapsed in future release
    reshape_tuple = (expected_data.shape[0], int(uvobj2.Nfreqs / 2), 2, uvobj2.Npols)
    expected_data = expected_data.reshape(reshape_tuple).mean(axis=2)

    expected_data[0, 1, :] = uvobj2.data_array[inds01[0], 3, :]

    assert np.allclose(uvobj.get_data(1, 2, squeeze="none"), expected_data)
    assert np.nonzero(uvobj.flag_array)[0].size == uvobj.Npols


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_frequency_average_flagging_partial_twostage(casa_uvfits):
    """
    Test averaging in frequency in two stages with flagging only one sample averaged.
    """
    uvobj = casa_uvfits
    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0

    # apply some flagging for testing
    inds01 = uvobj.antpair2ind(1, 2)
    uvobj.flag_array[inds01[0], 0, :] = True
    assert np.nonzero(uvobj.flag_array)[0].size == uvobj.Npols

    uv_object3 = uvobj.copy()

    uvobj.frequency_average(2)
    uvobj.frequency_average(2)

    uv_object3.frequency_average(4)

    assert uvobj == uv_object3


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_frequency_average_summing_corr_mode(casa_uvfits, future_shapes):
    """Test averaging in frequency."""
    # check that there's no flagging
    uvobj = casa_uvfits

    if not future_shapes:
        uvobj.use_current_array_shapes()
    uvobj2 = uvobj.copy()

    assert np.nonzero(uvobj.flag_array)[0].size == 0

    uvobj.frequency_average(2, summing_correlator_mode=True)

    assert uvobj.Nfreqs == (uvobj2.Nfreqs / 2)

    if future_shapes:
        expected_freqs = uvobj2.freq_array.reshape(int(uvobj2.Nfreqs / 2), 2).mean(
            axis=1
        )
    else:
        expected_freqs = uvobj2.freq_array.reshape(1, int(uvobj2.Nfreqs / 2), 2).mean(
            axis=2
        )
    assert np.max(np.abs(uvobj.freq_array - expected_freqs)) == 0

    # no flagging, so the following is true
    expected_data = uvobj2.get_data(1, 2, squeeze="none")
    if future_shapes:
        reshape_tuple = (
            expected_data.shape[0],
            int(uvobj2.Nfreqs / 2),
            2,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).sum(axis=2)
    else:
        reshape_tuple = (
            expected_data.shape[0],
            1,
            int(uvobj2.Nfreqs / 2),
            2,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).sum(axis=3)
    assert np.allclose(uvobj.get_data(1, 2, squeeze="none"), expected_data)

    assert np.nonzero(uvobj.flag_array)[0].size == 0
    assert not isinstance(uvobj.data_array, np.ma.MaskedArray)
    assert not isinstance(uvobj.nsample_array, np.ma.MaskedArray)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_frequency_average_propagate_flags(casa_uvfits, future_shapes):
    """
    Test averaging in frequency with flagging all of one and only one of
    another sample averaged, and propagating flags. Data should be identical,
    but flags should be slightly different compared to other test of the same
    name.
    """
    uvobj = casa_uvfits

    if not future_shapes:
        uvobj.use_current_array_shapes()
    uvobj2 = uvobj.copy()

    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0

    # apply some flagging for testing
    inds01 = uvobj.antpair2ind(1, 2)
    if future_shapes:
        uvobj.flag_array[inds01[0], 0:3, :] = True
    else:
        uvobj.flag_array[inds01[0], :, 0:3, :] = True

    assert np.nonzero(uvobj.flag_array)[0].size == uvobj.Npols * 3

    uvobj.frequency_average(2, propagate_flags=True)

    assert uvobj.Nfreqs == (uvobj2.Nfreqs / 2)

    if future_shapes:
        expected_freqs = uvobj2.freq_array.reshape(int(uvobj2.Nfreqs / 2), 2).mean(
            axis=1
        )
    else:
        expected_freqs = uvobj2.freq_array.reshape(1, int(uvobj2.Nfreqs / 2), 2).mean(
            axis=2
        )
    assert np.max(np.abs(uvobj.freq_array - expected_freqs)) == 0

    expected_data = uvobj2.get_data(1, 2, squeeze="none")
    if future_shapes:
        reshape_tuple = (
            expected_data.shape[0],
            int(uvobj2.Nfreqs / 2),
            2,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).mean(axis=2)

        expected_data[0, 1, :] = uvobj2.data_array[inds01[0], 3, :]
    else:
        reshape_tuple = (
            expected_data.shape[0],
            1,
            int(uvobj2.Nfreqs / 2),
            2,
            uvobj2.Npols,
        )
        expected_data = expected_data.reshape(reshape_tuple).mean(axis=3)

        expected_data[0, :, 1, :] = uvobj2.data_array[inds01[0], :, 3, :]

    assert np.allclose(uvobj.get_data(1, 2, squeeze="none"), expected_data)
    # Twice as many flags should exist compared to test of previous name.
    assert np.nonzero(uvobj.flag_array)[0].size == 2 * uvobj.Npols


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_frequency_average_nsample_precision(casa_uvfits):
    """Test averaging in frequency with a half-precision nsample_array."""
    uvobj = casa_uvfits
    uvobj2 = uvobj.copy()
    eq_coeffs = np.tile(
        np.arange(uvobj.Nfreqs, dtype=np.float64), (uvobj.Nants_telescope, 1)
    )
    uvobj.eq_coeffs = eq_coeffs
    uvobj.check()

    # check that there's no flagging
    assert np.nonzero(uvobj.flag_array)[0].size == 0

    # change precision of the nsample array
    uvobj.nsample_array = uvobj.nsample_array.astype(np.float16)

    with uvtest.check_warnings(UserWarning, "eq_coeffs vary by frequency"):
        uvobj.frequency_average(2),

    assert uvobj.Nfreqs == (uvobj2.Nfreqs / 2)

    # TODO: Spw axis to be collapsed in future release
    expected_freqs = uvobj2.freq_array.reshape(1, int(uvobj2.Nfreqs / 2), 2).mean(
        axis=2
    )
    assert np.max(np.abs(uvobj.freq_array - expected_freqs)) == 0

    expected_coeffs = eq_coeffs.reshape(
        uvobj2.Nants_telescope, int(uvobj2.Nfreqs / 2), 2
    ).mean(axis=2)
    assert np.max(np.abs(uvobj.eq_coeffs - expected_coeffs)) == 0

    # no flagging, so the following is true
    expected_data = uvobj2.get_data(1, 2, squeeze="none")
    reshape_tuple = (expected_data.shape[0], int(uvobj2.Nfreqs / 2), 2, uvobj2.Npols)
    expected_data = expected_data.reshape(reshape_tuple).mean(axis=2)
    assert np.allclose(uvobj.get_data(1, 2, squeeze="none"), expected_data)

    assert np.nonzero(uvobj.flag_array)[0].size == 0

    assert not isinstance(uvobj.data_array, np.ma.MaskedArray)
    assert not isinstance(uvobj.nsample_array, np.ma.MaskedArray)

    # make sure we still have a half-precision nsample_array
    assert uvobj.nsample_array.dtype.type is np.float16


def test_frequency_average_flex_spw(sma_mir, casa_uvfits):
    """
    Test that freq averaging works correctly when called on a flex_spw data set
    (currently not implented).
    """
    with pytest.raises(
        NotImplementedError,
        match=re.escape(
            "Frequency averaging not (yet) available for multiple spectral windows"
        ),
    ):
        sma_mir.frequency_average(2)

    # also test errors with varying freq spacing but with one spw
    uvd = casa_uvfits.copy()
    uvd.use_future_array_shapes()
    uvd.freq_array[-1] += uvd.channel_width[0]
    with pytest.raises(
        NotImplementedError,
        match=re.escape(
            "Frequency averaging not (yet) available for unevenly spaced "
            "frequencies or varying channel widths."
        ),
    ):
        uvd.frequency_average(2)

    # also test errors with varying channel widths but with one spw
    uvd = casa_uvfits.copy()
    uvd.use_future_array_shapes()
    uvd.channel_width[-1] *= 2
    with pytest.raises(
        NotImplementedError,
        match=re.escape(
            "Frequency averaging not (yet) available for unevenly spaced "
            "frequencies or varying channel widths."
        ),
    ):
        uvd.frequency_average(2)

    # test warning with freq spacing not equal to channel width
    uvd = casa_uvfits.copy()
    uvd.channel_width *= 0.5
    with uvtest.check_warnings(
        UserWarning,
        match=re.escape(
            "The frequency spacing is even but not equal to the channel width, so "
            "after averaging the channel_width will also not match the frequency "
            "spacing."
        ),
    ):
        uvd.frequency_average(2)

    spacing_error, chanwidth_error = uvd._check_freq_spacing(raise_errors=False)
    assert not spacing_error
    assert chanwidth_error


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_remove_eq_coeffs_divide(casa_uvfits, future_shapes):
    """Test using the remove_eq_coeffs method with divide convention."""
    uvobj = casa_uvfits

    if not future_shapes:
        uvobj.use_current_array_shapes()
    uvobj2 = uvobj.copy()

    # give eq_coeffs to the object
    eq_coeffs = np.empty((uvobj.Nants_telescope, uvobj.Nfreqs), dtype=np.float64)
    for i, ant in enumerate(uvobj.antenna_numbers):
        eq_coeffs[i, :] = ant + 1
    uvobj.eq_coeffs = eq_coeffs
    uvobj.eq_coeffs_convention = "divide"
    uvobj.remove_eq_coeffs()

    # make sure the right coefficients were removed
    for key in uvobj.get_antpairs():
        eq1 = key[0] + 1
        eq2 = key[1] + 1
        blt_inds = uvobj.antpair2ind(key)
        norm_data = uvobj.data_array[blt_inds]
        unnorm_data = uvobj2.data_array[blt_inds]
        assert np.allclose(norm_data, unnorm_data / (eq1 * eq2))

    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_remove_eq_coeffs_multiply(casa_uvfits, future_shapes):
    """Test using the remove_eq_coeffs method with multiply convention."""
    uvobj = casa_uvfits

    if not future_shapes:
        uvobj.use_current_array_shapes()
    uvobj2 = uvobj.copy()

    # give eq_coeffs to the object
    eq_coeffs = np.empty((uvobj.Nants_telescope, uvobj.Nfreqs), dtype=np.float64)
    for i, ant in enumerate(uvobj.antenna_numbers):
        eq_coeffs[i, :] = ant + 1
    uvobj.eq_coeffs = eq_coeffs
    uvobj.eq_coeffs_convention = "multiply"
    uvobj.remove_eq_coeffs()

    # make sure the right coefficients were removed
    for key in uvobj.get_antpairs():
        eq1 = key[0] + 1
        eq2 = key[1] + 1
        blt_inds = uvobj.antpair2ind(key)
        norm_data = uvobj.data_array[blt_inds]
        unnorm_data = uvobj2.data_array[blt_inds]
        assert np.allclose(norm_data, unnorm_data * (eq1 * eq2))

    return


def test_remove_eq_coeffs_errors(casa_uvfits):
    """Test errors raised by remove_eq_coeffs method."""
    uvobj = casa_uvfits
    # raise error when eq_coeffs are not defined
    with pytest.raises(ValueError, match="The eq_coeffs attribute must be defined"):
        uvobj.remove_eq_coeffs()

    # raise error when eq_coeffs are defined but not eq_coeffs_convention
    uvobj.eq_coeffs = np.ones((uvobj.Nants_telescope, uvobj.Nfreqs))
    with pytest.raises(
        ValueError, match="The eq_coeffs_convention attribute must be defined"
    ):
        uvobj.remove_eq_coeffs()

    # raise error when convention is not a valid choice
    uvobj.eq_coeffs_convention = "foo"
    with pytest.raises(ValueError, match="Got unknown convention foo. Must be one of"):
        uvobj.remove_eq_coeffs()

    return


@pytest.mark.parametrize(
    "read_func,filelist",
    [
        ("read_miriad", [os.path.join(DATA_PATH, "zen.2457698.40355.xx.HH.uvcA")] * 2),
        (
            "read_mwa_corr_fits",
            [[mwa_corr_files[0:2], [mwa_corr_files[0], mwa_corr_files[2]]]],
        ),
        ("read_uvh5", [os.path.join(DATA_PATH, "zen.2458661.23480.HH.uvh5")] * 2),
        (
            "read_uvfits",
            [os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits")] * 2,
        ),
        (
            "read_ms",
            [
                os.path.join(DATA_PATH, "multi_1.ms"),
                os.path.join(DATA_PATH, "multi_2.ms"),
            ],
        ),
        (
            "read_fhd",
            [
                list(np.array(fhd_files)[[0, 1, 2, 4, 6, 7]]),
                list(np.array(fhd_files)[[0, 2, 3, 5, 6, 7]]),
            ],
        ),
    ],
)
def test_multifile_read_errors(read_func, filelist):
    uv = UVData()
    with pytest.raises(
        ValueError,
        match="Reading multiple files from class specific read functions is no "
        "longer supported.",
    ):
        getattr(uv, read_func)(filelist)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_multifile_read_check(hera_uvh5, tmp_path):
    """Test setting skip_bad_files=True when reading in files"""

    uv_true = hera_uvh5.copy()

    uvh5_file = os.path.join(DATA_PATH, "zen.2458661.23480.HH.uvh5")

    # Create a test file and remove header info to 'corrupt' it
    testfile = os.path.join(tmp_path, "zen.2458661.23480.HH.uvh5")

    uv_true.write_uvh5(testfile)
    with h5py.File(testfile, "r+") as h5f:
        del h5f["Header/ant_1_array"]

    err_msg = "ant_1_array not found in"
    uv = UVData()
    # Test that the expected error arises
    with pytest.raises(KeyError, match=err_msg):
        uv.read(testfile, skip_bad_files=False)

    # Test when the corrupted file is at the beggining, skip_bad_files=False
    fileList = [testfile, uvh5_file]
    with pytest.raises(KeyError, match=err_msg):
        with uvtest.check_warnings(UserWarning, match="Failed to read"):
            uv.read(fileList, skip_bad_files=False)
    assert uv != uv_true

    # Test when the corrupted file is at the beggining, skip_bad_files=True
    fileList = [testfile, uvh5_file]
    with uvtest.check_warnings(UserWarning, match=["Failed to read"]):
        uv.read(
            fileList,
            skip_bad_files=True,
            allow_rephase=False,
            use_future_array_shapes=True,
        )
    assert uv == uv_true

    # Test when the corrupted file is at the end of a list
    fileList = [uvh5_file, testfile]
    with uvtest.check_warnings(UserWarning, match=["Failed to read"]):
        uv.read(
            fileList,
            skip_bad_files=True,
            allow_rephase=False,
            use_future_array_shapes=True,
        )
    # Check that the uncorrupted file was still read in
    assert uv == uv_true

    # Test that selection happens when there's only one good file in a list
    uv_true2 = uv_true.copy()
    uv_true2.select(freq_chans=np.arange(uv_true.Nfreqs // 2))
    with uvtest.check_warnings(UserWarning, match=["Failed to read"]):
        uv.read(
            fileList,
            skip_bad_files=True,
            allow_rephase=False,
            freq_chans=np.arange(uv_true.Nfreqs // 2),
            use_future_array_shapes=True,
        )
    # Check that the uncorrupted file was still read in and selection is applied
    assert uv == uv_true2

    # Test that phasing happens when there's only one good file in a list
    uv_true2 = uv_true.copy()
    uv_true2.phase_to_time(np.median(uv_true2.time_array))
    phase_center_radec = [
        uv_true2.phase_center_catalog[1]["cat_lon"],
        uv_true2.phase_center_catalog[1]["cat_lat"],
    ]
    with uvtest.check_warnings(
        [UserWarning, UserWarning, DeprecationWarning, DeprecationWarning],
        match=[
            "Failed to read",
            "Phasing this UVData object",
            "The `phase_center_radec` parameter is deprecated",
            "The default behavior is to rephase data from the files",
        ],
    ):
        uv.read(
            fileList,
            skip_bad_files=True,
            phase_center_radec=phase_center_radec,
            use_future_array_shapes=True,
        )
    # Check that the uncorrupted file was still read in and phased properly
    uv._consolidate_phase_center_catalogs(
        reference_catalog=uv_true2.phase_center_catalog, ignore_name=True
    )
    assert uv == uv_true2
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("err_type", ["KeyError", "ValueError"])
def test_multifile_read_check_long_list(hera_uvh5, tmp_path, err_type):
    """
    Test KeyError catching by setting skip_bad_files=True when
    reading in files for a list of length >2
    """
    # Create mini files for testing
    uv = hera_uvh5

    fileList = []
    for i in range(0, 4):
        uv2 = uv.select(
            times=np.unique(uv.time_array)[i * 5 : i * 5 + 4], inplace=False
        )
        fname = str(tmp_path / f"minifile_{i}.uvh5")
        fileList.append(fname)
        uv2.write_uvh5(fname)
    if err_type == "KeyError":
        with h5py.File(fileList[-1], "r+") as h5f:
            del h5f["Header/ant_1_array"]
    elif err_type == "ValueError":
        with h5py.File(fileList[-1], "r+") as h5f:
            h5f["Header/antenna_numbers"][3] = 85
            h5f["Header/ant_1_array"][2] = 1024

    # Test with corrupted file as last file in list, skip_bad_files=True
    uv_test = UVData()
    with uvtest.check_warnings(UserWarning, "Failed to read"):
        uv_test.read(fileList[0:4], skip_bad_files=True, use_future_array_shapes=True)
    uv_true = UVData()
    uv_true.read(fileList[0:3], skip_bad_files=True, use_future_array_shapes=True)
    assert uv_test == uv_true

    # Repeat above test, but with corrupted file as first file in list
    os.remove(fileList[3])
    uv2 = uv.select(times=np.unique(uv.time_array)[15:19], inplace=False)
    fname = str(tmp_path / f"minifile_{3}.uvh5")
    uv2.write_uvh5(fname)
    if err_type == "KeyError":
        with h5py.File(fileList[0], "r+") as h5f:
            del h5f["Header/ant_1_array"]
    elif err_type == "ValueError":
        with h5py.File(fileList[0], "r+") as h5f:
            h5f["Header/antenna_numbers"][3] = 85
            h5f["Header/ant_1_array"][2] = 1024
    uv_test = UVData()
    with uvtest.check_warnings(UserWarning, "Failed to read"):
        uv_test.read(fileList[0:4], skip_bad_files=True, use_future_array_shapes=True)
    uv_true = UVData()
    uv_true.read(fileList[1:4], skip_bad_files=True, use_future_array_shapes=True)

    assert uv_test == uv_true

    err_msg = "ant_1_array not found"

    # Test with corrupted file first in list, but with skip_bad_files=False
    uv_test = UVData()
    if err_type == "KeyError":
        with pytest.raises(KeyError, match=err_msg):
            with uvtest.check_warnings(UserWarning, match="Failed to read"):
                uv_test.read(
                    fileList[0:4], skip_bad_files=False, use_future_array_shapes=True
                )
    elif err_type == "ValueError":
        with pytest.raises(ValueError, match="Nants_data must be equal to"):
            with uvtest.check_warnings(UserWarning, match="Failed to read"):
                uv_test.read(
                    fileList[0:4], skip_bad_files=False, use_future_array_shapes=True
                )
    uv_true = UVData()
    uv_true.read(
        [fileList[1], fileList[2], fileList[3]],
        skip_bad_files=False,
        use_future_array_shapes=True,
    )

    assert uv_test != uv_true

    # Repeat above test, but with corrupted file in the middle of the list
    os.remove(fileList[0])
    uv2 = uv.select(times=np.unique(uv.time_array)[0:4], inplace=False)
    fname = str(tmp_path / f"minifile_{0}.uvh5")
    uv2.write_uvh5(fname)
    if err_type == "KeyError":
        with h5py.File(fileList[1], "r+") as h5f:
            del h5f["Header/ant_1_array"]
    elif err_type == "ValueError":
        with h5py.File(fileList[1], "r+") as h5f:
            h5f["Header/antenna_numbers"][3] = 85
            h5f["Header/ant_1_array"][2] = 1024
    uv_test = UVData()
    with uvtest.check_warnings(UserWarning, "Failed to read"):
        uv_test.read(fileList[0:4], skip_bad_files=True, use_future_array_shapes=True)
    uv_true = UVData()
    uv_true.read(
        [fileList[0], fileList[2], fileList[3]],
        skip_bad_files=True,
        use_future_array_shapes=True,
    )

    assert uv_test == uv_true

    # Test with corrupted file in middle of list, but with skip_bad_files=False
    uv_test = UVData()
    if err_type == "KeyError":
        with pytest.raises(KeyError, match=err_msg):
            with uvtest.check_warnings(UserWarning, match="Failed to read"):
                uv_test.read(
                    fileList[0:4], skip_bad_files=False, use_future_array_shapes=True
                )
    elif err_type == "ValueError":
        with pytest.raises(ValueError, match="Nants_data must be equal to"):
            with uvtest.check_warnings(UserWarning, match="Failed to read"):
                uv_test.read(
                    fileList[0:4], skip_bad_files=False, use_future_array_shapes=True
                )
    uv_true = UVData()
    uv_true.read(
        [fileList[0], fileList[2], fileList[3]],
        skip_bad_files=False,
        use_future_array_shapes=True,
    )

    assert uv_test != uv_true

    # Test case where all files in list are corrupted
    os.remove(fileList[1])
    uv2 = uv.select(times=np.unique(uv.time_array)[5:9], inplace=False)
    fname = str(tmp_path / f"minifile_{1}.uvh5")
    uv2.write_uvh5(fname)
    for file in fileList:
        if err_type == "KeyError":
            with h5py.File(file, "r+") as h5f:
                del h5f["Header/ant_1_array"]
        elif err_type == "ValueError":
            with h5py.File(file, "r+") as h5f:
                h5f["Header/antenna_numbers"][3] = 85
                h5f["Header/ant_1_array"][2] = 1024
    uv_test = UVData()
    with uvtest.check_warnings(
        UserWarning,
        match=(
            "########################################################\n"
            "ALL FILES FAILED ON READ - NO READABLE FILES IN FILENAME\n"
            "########################################################"
        ),
    ):
        uv_test.read(fileList[0:4], skip_bad_files=True, use_future_array_shapes=True)
    uv_true = UVData()

    assert uv_test == uv_true

    os.remove(fileList[0])
    os.remove(fileList[1])
    os.remove(fileList[2])
    os.remove(fileList[3])

    return


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_background_lsts():
    """Test reading a file with the lst calc in the background."""
    uvd = UVData()
    uvd2 = UVData()
    testfile = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits")
    uvd.read(testfile, background_lsts=False, use_future_array_shapes=True)
    uvd2.read(testfile, background_lsts=True, use_future_array_shapes=True)
    assert uvd == uvd2


def test_parse_ants_x_orientation_kwarg(hera_uvh5):
    uvd = hera_uvh5
    # call with x_orientation = None to make parse_ants read from the object
    ant_pair, pols = uvutils.parse_ants(uvd, "cross")
    ant_pair2, pols2 = uvd.parse_ants("cross")
    assert np.array_equal(ant_pair, ant_pair2)
    assert np.array_equal(pols, pols2)


def test_getattr_error(casa_uvfits):
    with pytest.raises(
        ValueError,
        match="The older phase attributes, including phase_type, phase_center_ra, "
        "phase_center_dec, phase_center_frame, phase_center_epoch, object_name cannot "
        "be used with data phased with the new method. This was phased with the new "
        "method because it has fk5 phase frames.",
    ):
        casa_uvfits.phase_type


@pytest.mark.parametrize(
    ["old_attr", "msg"],
    [
        (
            "phase_type",
            " The phase_type is now represented as the 'cat_type' in the "
            "phase_center_catalog (the old 'drift' type corresponds to the "
            "new 'unprojected' type, the old 'phased' type corresponds to the "
            "new 'sidereal' type, and there is also now support for an 'ephem' "
            "type to support moving objects and a new 'driftscan' type which "
            "can point at any alt/az (not just zenith) and which always has "
            "w-projection applied).",
        ),
        (
            "phase_center_ra",
            " The phase_center_ra is now represented as the 'cat_lon' in the "
            "phase_center_catalog.",
        ),
        (
            "phase_center_dec",
            " The phase_center_dec is now represented as the 'cat_lat' in the "
            "phase_center_catalog.",
        ),
        (
            "phase_center_frame",
            " The phase_center_frame is now represented as the 'cat_frame' in the "
            "phase_center_catalog.",
        ),
        (
            "phase_center_epoch",
            " The phase_center_epoch is now represented as the 'cat_epoch' in the "
            "phase_center_catalog.",
        ),
        (
            "object_name",
            " The object_name is now represented as the 'cat_name' in the "
            "phase_center_catalog.",
        ),
    ],
)
def test_getattr(casa_uvfits, old_attr, msg):
    casa_uvfits.phase_center_catalog[0]["cat_frame"] = "icrs"
    initial_msg = (
        "The older phase attributes, including phase_type, phase_center_ra, "
        "phase_center_dec, phase_center_frame, phase_center_epoch, object_name, are "
        "deprecated in favor of representing phasing using the phase_center_catalog."
    )
    expected_vals = {
        "phase_type": "phased",
        "phase_center_ra": casa_uvfits.phase_center_catalog[0]["cat_lon"],
        "phase_center_dec": casa_uvfits.phase_center_catalog[0]["cat_lat"],
        "phase_center_frame": casa_uvfits.phase_center_catalog[0]["cat_frame"],
        "phase_center_epoch": casa_uvfits.phase_center_catalog[0]["cat_epoch"],
        "object_name": casa_uvfits.phase_center_catalog[0]["cat_name"],
    }

    with uvtest.check_warnings(DeprecationWarning, match=initial_msg + msg):
        assert getattr(casa_uvfits, old_attr) == expected_vals[old_attr]

    if old_attr == "phase_type":
        # also test for drift identification
        casa_uvfits.unproject_phase()

        with uvtest.check_warnings(DeprecationWarning, match=initial_msg + msg):
            assert getattr(casa_uvfits, old_attr) == "drift"


def test_rephase_to_time():
    uvfits_file = os.path.join(DATA_PATH, "1061316296.uvfits")
    uvd = UVData()

    uvd.read(uvfits_file, use_future_array_shapes=True)
    phase_time = np.unique(uvd.time_array)[1]
    time = Time(phase_time, format="jd")
    # Generate ra/dec of zenith at time in the phase_frame coordinate
    # system to use for phasing
    telescope_location = EarthLocation.from_geocentric(
        *uvd.telescope_location, unit="m"
    )

    zenith_coord = SkyCoord(
        alt=Angle(90 * units.deg),
        az=Angle(0 * units.deg),
        obstime=time,
        frame="altaz",
        location=telescope_location,
    )

    obs_zenith_coord = zenith_coord.transform_to("icrs")
    zenith_ra = obs_zenith_coord.ra.rad
    zenith_dec = obs_zenith_coord.dec.rad

    uvd.phase_to_time(phase_time)

    with uvtest.check_warnings(
        DeprecationWarning, match="The older phase attributes, including phase_type"
    ):
        assert uvd.phase_center_ra == zenith_ra

    with uvtest.check_warnings(
        DeprecationWarning, match="The older phase attributes, including phase_type"
    ):
        assert uvd.phase_center_dec == zenith_dec


@pytest.mark.parametrize(
    ["identifier", "errtype", "msg"],
    [
        (-1, ValueError, "No entry with the ID -1 in the catalog."),
        (5.0, TypeError, "catalog_identifier must be a string, an integer or a list"),
        ("test", ValueError, "No entry by the name test in the catalog."),
    ],
)
def test_print_object_err(sma_mir, identifier, errtype, msg):
    with pytest.raises(errtype, match=msg):
        sma_mir.print_phase_center_info(catalog_identifier=identifier)


@pytest.mark.parametrize(
    "kwargs", [{}, {"catalog_identifier": "3c84"}, {"catalog_identifier": 1}]
)
def test_print_object_standard(sma_mir, kwargs):
    """
    Check that the 'standard' mode of print_object works.
    """
    check_str = (
        "   ID     Cat Entry          Type     Az/Lon/RA    El/Lat/Dec  Frame    Epoch \n"  # noqa
        "    #          Name                       hours           deg                 \n"  # noqa
        "------------------------------------------------------------------------------\n"  # noqa
        "    1          3c84      sidereal    3:19:48.16  +41:30:42.11    fk5  J2000.0 \n"  # noqa
    )

    table_str = sma_mir.print_phase_center_info(
        print_table=False, return_str=True, **kwargs
    )
    assert table_str == check_str

    # Make sure we can specify the object name and get the same result
    table_str = sma_mir.print_phase_center_info(
        print_table=False, return_str=True, catalog_identifier="3c84"
    )
    assert table_str == check_str

    # Make sure that things still work when we force the HMS format
    table_str = sma_mir.print_phase_center_info(
        print_table=False, return_str=True, hms_format=True
    )
    assert table_str == check_str


def test_print_object_dms(sma_mir):
    """
    Test that forcing DMS format works as expected.
    """
    check_str = (
        "   ID     Cat Entry          Type      Az/Lon/RA    El/Lat/Dec  Frame    Epoch \n"  # noqa
        "    #          Name                          deg           deg                 \n"  # noqa
        "-------------------------------------------------------------------------------\n"  # noqa
        "    1          3c84      sidereal    49:57:02.40  +41:30:42.11    fk5  J2000.0 \n"  # noqa
    )

    # And likewise when forcing the degree format
    table_str = sma_mir.print_phase_center_info(
        print_table=False, return_str=True, hms_format=False
    )
    assert table_str == check_str


@pytest.mark.filterwarnings("ignore:The provided name")
@pytest.mark.parametrize(
    ["frame", "epoch"],
    [["fk5", 2000.0], ["fk5", "J2000.0"], ["fk4", "B1950.0"], ["fk4", 1950.0]],
)
def test_print_object_full(sma_mir, frame, epoch):
    """
    Test that print object w/ all optional paramters prints as expected.
    """
    # Now check and see what happens if we add the full suite of phase center parameters
    _ = sma_mir._add_phase_center(
        "3c84",
        cat_type="sidereal",
        cat_lat=-1.0,
        cat_lon=-1.0,
        cat_dist=0.0,
        cat_vrad=0.0,
        cat_pm_ra=0.0,
        cat_pm_dec=0.0,
        cat_frame=frame,
        cat_epoch=epoch,
        cat_id=list(sma_mir.phase_center_catalog)[0],
        force_update=True,
    )
    frame_str = str(frame)
    if frame == "fk5":
        if isinstance(epoch, str):
            epoch_str = epoch
        else:
            epoch_str = "J" + str(epoch)
    elif frame == "fk4":
        if isinstance(epoch, str):
            epoch_str = epoch
        else:
            epoch_str = "B" + str(epoch)
    print(epoch_str)
    check_str = (
        "   ID     Cat Entry          Type     Az/Lon/RA"
        "    El/Lat/Dec  Frame    Epoch   PM-Ra  PM-Dec     Dist   V_rad \n"
        "    #          Name                       hours"
        "           deg                  mas/yr  mas/yr       pc    km/s \n"
        "-----------------------------------------------"
        "----------------------------------------------------------------\n"
        "    1          3c84      sidereal   20:10:49.01"
        f"  -57:17:44.81    {frame_str:5s}{epoch_str:7s}       0       0  0.0e+00"
        "       0 \n"
    )
    table_str = sma_mir.print_phase_center_info(print_table=False, return_str=True)
    assert table_str == check_str


@pytest.mark.filterwarnings("ignore:The provided name")
def test_print_object_ephem(sma_mir):
    """
    Test that printing ephem objects works as expected.
    """
    # Now check and see that printing ephems works well
    _ = sma_mir._add_phase_center(
        "3c84",
        cat_type="ephem",
        cat_lat=0.0,
        cat_lon=0.0,
        cat_frame="icrs",
        cat_times=2456789.0,
        cat_id=list(sma_mir.phase_center_catalog)[0],
        force_update=True,
    )
    check_str = (
        "   ID     Cat Entry          Type     Az/Lon/RA"
        "    El/Lat/Dec  Frame        Ephem Range    \n"
        "    #          Name                       hours"
        "           deg         Start-MJD    End-MJD \n"
        "------------------------------------------------"
        "-------------------------------------------\n"
        "    1          3c84         ephem    0:00:00.00"
        "  + 0:00:00.00   icrs   56788.50   56788.50 \n"
    )
    table_str = sma_mir.print_phase_center_info(print_table=False, return_str=True)
    assert table_str == check_str


@pytest.mark.filterwarnings("ignore:The provided name")
def test_print_object_driftscan(sma_mir):
    """
    Test that printing driftscan objects works as expected.
    """
    # Check and see that if we force this to be a driftscan, we get the output
    # we expect
    _ = sma_mir._add_phase_center(
        "3c84",
        cat_type="driftscan",
        force_update=True,
        cat_id=list(sma_mir.phase_center_catalog)[0],
    )
    check_str = (
        "   ID     Cat Entry          Type      Az/Lon/RA    El/Lat/Dec  Frame \n"
        "    #          Name                          deg           deg        \n"
        "----------------------------------------------------------------------\n"
        "    1          3c84     driftscan     0:00:00.00  +90:00:00.00  altaz \n"
    )
    table_str = sma_mir.print_phase_center_info(print_table=False, return_str=True)
    assert table_str == check_str


@pytest.mark.filterwarnings("ignore:The provided name")
def test_print_object_unprojected(sma_mir):
    _ = sma_mir._add_phase_center(
        "3c84",
        cat_type="unprojected",
        force_update=True,
        cat_id=list(sma_mir.phase_center_catalog)[0],
    )
    check_str = (
        "   ID     Cat Entry          Type      Az/Lon/RA    El/Lat/Dec  Frame \n"
        "    #          Name                          deg           deg        \n"
        "----------------------------------------------------------------------\n"
        "    1          3c84   unprojected     0:00:00.00  +90:00:00.00  altaz \n"
    )
    table_str = sma_mir.print_phase_center_info(print_table=False, return_str=True)
    assert table_str == check_str


@pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad file,")
@pytest.mark.filterwarnings("ignore:The provided name")
def test_print_object_multi(carma_miriad):
    """
    Test the print_phase_center_info function when there are multiple objects stored in
    the internal catalog.
    """
    pytest.importorskip("pyuvdata._miriad")

    _ = carma_miriad._add_phase_center(
        "NOISE", cat_type="unprojected", force_update=True, cat_id=0
    )
    check_str = (
        "   ID     Cat Entry          Type     Az/Lon/RA    El/Lat/Dec  Frame    Epoch \n"  # noqa
        "    #          Name                       hours           deg                 \n"  # noqa
        "------------------------------------------------------------------------------\n"  # noqa
        "    0         NOISE   unprojected    0:00:00.00  +90:00:00.00  altaz          \n"  # noqa
        "    1         3C273      sidereal   12:29:06.70  + 2:03:08.60    fk5  J2000.0 \n"  # noqa
        "    2      1159+292      sidereal   11:59:31.83  +29:14:43.83    fk5  J2000.0 \n"  # noqa
    )
    table_str = carma_miriad.print_phase_center_info(
        print_table=False, return_str=True, hms_format=True
    )
    assert table_str == check_str

    # check that it works if you specify a list of ids
    table_str = carma_miriad.print_phase_center_info(
        catalog_identifier=[0, 1, 2],
        print_table=False,
        return_str=True,
        hms_format=True,
    )
    assert table_str == check_str


@pytest.mark.parametrize(
    "kwargs,err_type,err_msg",
    [
        [{}, ValueError, "Must specify either phase_dict or cat_name"],
        [{"cat_name": "3c84", "target_cat_id": -1}, ValueError, "No phase center with"],
    ],
)
def test_look_in_catalog_err(sma_mir, kwargs, err_type, err_msg):
    with pytest.raises(err_type, match=err_msg):
        sma_mir._look_in_catalog(**kwargs)


@pytest.mark.parametrize(
    "name,stype,arg_dict,exp_id,exp_diffs",
    (
        ["zenith", None, {}, 0, 4],
        ["zenith", "driftscan", {}, 0, 1],
        ["zenith", "unprojected", {}, 0, 0],
        ["unprojected", "unprojected", {}, None, 99999],
        ["unprojected", "unprojected", {"ignore_name": True}, 0, 0],
        ["zenith", "unprojected", {"lat": 1.0}, 0, 1],
        ["zenith", "unprojected", {"lon": 1.0}, 0, 1],
        ["zenith", "unprojected", {"frame": 1.0}, 0, 1],
        ["zenith", "unprojected", {"epoch": 1.0}, 0, 1],
        ["zenith", "unprojected", {"times": 1.0}, 0, 1],
        ["zenith", "unprojected", {"pm_ra": 1.0}, 0, 1],
        ["zenith", "unprojected", {"pm_dec": 1.0}, 0, 1],
        ["zenith", "unprojected", {"dist": 1.0}, 0, 1],
        ["zenith", "unprojected", {"vrad": 1.0}, 0, 1],
    ),
)
def test_look_in_catalog(hera_uvh5, name, stype, arg_dict, exp_id, exp_diffs):
    """
    Test some basic functions of _look_in_catalog and check that it finds the
    appropriate phase center ID and number of differences between the provided
    parameters and that recorded in the UVData object.
    """
    hera_uvh5.print_phase_center_info()
    [cat_id, num_diffs] = hera_uvh5._look_in_catalog(
        cat_name=name,
        cat_type=stype,
        cat_lon=arg_dict.get("lon"),
        cat_lat=arg_dict.get("lat"),
        cat_frame=arg_dict.get("frame"),
        cat_epoch=arg_dict.get("epoch"),
        cat_times=arg_dict.get("times"),
        cat_pm_ra=arg_dict.get("pm_ra"),
        cat_pm_dec=arg_dict.get("pm_dec"),
        cat_dist=arg_dict.get("dist"),
        cat_vrad=arg_dict.get("vrad"),
        ignore_name=arg_dict.get("ignore_name"),
    )
    if cat_id is not None:
        assert cat_id == exp_id
    else:
        assert exp_id is None
    assert num_diffs == exp_diffs


def test_look_in_catalog_phase_dict(sma_mir):
    """
    Use the phase_dict argument for _look_in_catalog and make sure that things
    behave as expected
    """
    # Now try lookup using a dictionary of properties
    assert sma_mir._look_in_catalog("3c84") == (1, 5)
    phase_dict = sma_mir.phase_center_catalog[1]
    assert sma_mir._look_in_catalog("3c84", phase_dict=phase_dict) == (1, 0)

    # Make sure that if we set ignore_name, we still get a match
    assert sma_mir._look_in_catalog(
        "3c84", phase_dict=phase_dict, ignore_name=True
    ) == (1, 0)

    # Match w/ a mis-capitalization
    assert sma_mir._look_in_catalog(
        "3C84", phase_dict=phase_dict, ignore_name=True
    ) == (1, 0)


@pytest.mark.parametrize(
    "name,stype,arg_dict,msg",
    (
        [-1, "drift", {}, "cat_name must be a string."],
        ["zenith", "drift", {}, "Only sidereal, ephem, driftscan or unprojected may"],
        ["zenith", "driftscan", {"pm_ra": 0, "pm_dec": 0}, "Non-zero proper motion"],
        [
            "unprojected",
            "unprojected",
            {"lon": 1},
            "Catalog entries that are unprojected",
        ],
        [
            "unprojected",
            "unprojected",
            {"lat": 1},
            "Catalog entries that are unprojected",
        ],
        [
            "unprojected",
            "unprojected",
            {"frame": "fk5"},
            "cat_frame must be either None",
        ],
        ["test", "ephem", {}, "cat_times cannot be None for ephem object."],
        [
            "test",
            "ephem",
            {"lon": 0, "lat": 0, "frame": "icrs", "times": [0, 1]},
            "Object properties -- lon, lat, pm_ra, pm_dec, dist, vrad",
        ],
        ["test", "sidereal", {"pm_ra": 0}, "Must supply values for either both or"],
        ["test", "sidereal", {"pm_dec": 0}, "Must supply values for either both or"],
        ["test", "sidereal", {"times": 0}, "cat_times cannot be used for non-ephem"],
        [
            "test",
            "sidereal",
            {},
            "cat_lon cannot be None for sidereal or ephem phase centers.",
        ],
        [
            "test",
            "sidereal",
            {"lon": 0},
            "cat_lat cannot be None for sidereal or ephem phase centers.",
        ],
        [
            "test",
            "sidereal",
            {"lon": 0, "lat": 0},
            "cat_frame cannot be None for sidereal or ephem phase centers.",
        ],
        ["unprojected", "unprojected", {"id": 1}, "Provided cat_id belongs to another"],
    ),
)
def test_add_phase_center_arg_errs(sma_mir, name, stype, arg_dict, msg):
    with pytest.raises(ValueError, match=msg):
        sma_mir._add_phase_center(
            name,
            cat_type=stype,
            cat_lon=arg_dict.get("lon"),
            cat_lat=arg_dict.get("lat"),
            cat_frame=arg_dict.get("frame"),
            cat_epoch=arg_dict.get("epoch"),
            cat_times=arg_dict.get("times"),
            cat_pm_ra=arg_dict.get("pm_ra"),
            cat_pm_dec=arg_dict.get("pm_dec"),
            cat_dist=arg_dict.get("dist"),
            cat_vrad=arg_dict.get("vrad"),
            force_update=arg_dict.get("force"),
            cat_id=arg_dict.get("id"),
        )


def test_add_phase_center_known_source(sma_mir):
    """
    Verify that if we attempt to add a source already in the catalog, we don't return
    an error but instead the call completes normally.
    """
    return_id = sma_mir._add_phase_center(
        "3c84",
        cat_type="sidereal",
        cat_lon=0.8718035968995141,
        cat_lat=0.7245157752262148,
        cat_frame="fk5",
        cat_epoch="j2000",
    )

    assert return_id == 1


def test_unphased_deprecation(sma_mir):
    with uvtest.check_warnings(
        DeprecationWarning,
        "The 'unphased' catalog type has been renamed to 'unprojected'. Using "
        "unprojected for now, this warning will become an error in version 2.4",
    ):
        new_id = sma_mir._add_phase_center("unphased", cat_type="unphased")
    assert new_id == 0
    assert sma_mir.phase_center_catalog[new_id]["cat_type"] == "unprojected"

    with uvtest.check_warnings(
        DeprecationWarning,
        "The 'unphased' catalog type has been renamed to 'unprojected'. Using "
        "unprojected for now, this warning will become an error in version 2.4",
    ):
        lookup_id, _ = sma_mir._look_in_catalog("unphased")

    assert new_id == lookup_id


def test_remove_phase_center_arg_errs(sma_mir):
    """
    Verify that _remove_phase_center throws errors appropriately when supplied with
    bad arguments.
    """
    # Only one bad argument to check, so no use parametrizing it
    with pytest.raises(
        IndexError, match="No source by that ID contained in the catalog."
    ):
        sma_mir._remove_phase_center(-1)


def test_clear_unused_phase_centers_no_op(sma_mir):
    """
    Verify that _clear_unused_phase_centers does nothing if no unused phase
    centers exist
    """
    check_dict = sma_mir.phase_center_catalog.copy()
    # Check and see that clearing out the unused objects doesn't actually change the
    # phase_center_catalog (because all objects are being "used").
    sma_mir._clear_unused_phase_centers()
    assert sma_mir.phase_center_catalog == check_dict


@pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad file,")
@pytest.mark.parametrize("cat_id,new_name", [(1, "foo"), ([1, 2], "foo")])
def test_rename_phase_center_ints(carma_miriad, cat_id, new_name):
    uvd = carma_miriad.copy()
    pytest.importorskip("pyuvdata._miriad")
    uvd.rename_phase_center(cat_id, new_name)


@pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad file,")
@pytest.mark.parametrize(
    "args,err_type,msg",
    (
        [["abc", "xyz"], ValueError, "No entry by the name abc in the catalog."],
        [["3C273", -2], TypeError, "Value provided to new_name must be a string"],
        [
            [3.1415, "abc"],
            TypeError,
            "catalog_identifier must be a string, an integer or a list of integers.",
        ],
        [
            [["3C273", "unprojected"], "abc"],
            TypeError,
            "catalog_identifier must be a string, an integer or a list of integers.",
        ],
        [
            [["3C273", 1], "abc"],
            TypeError,
            "catalog_identifier must be a string, an integer or a list of integers.",
        ],
        [[-1, None], ValueError, "No entry with the ID -1 in the catalog."],
    ),
)
def test_rename_phase_center_bad_args(carma_miriad, args, err_type, msg):
    """
    Verify that rename_phase_center will throw appropriate errors when supplying
    bad arguments to the method.
    """
    pytest.importorskip("pyuvdata._miriad")
    with pytest.raises(err_type, match=msg):
        carma_miriad.rename_phase_center(*args)


@pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad file,")
@pytest.mark.parametrize(
    "args,err_type,msg",
    (
        [["abc", "xyz", 1], ValueError, "No catalog entries matching the name abc."],
        [["3C273", -2, 1], TypeError, "Value provided to new_name must be a string"],
        [["3C273", "3c273", 1.5], IndexError, "select_mask must be an array-like,"],
        [["3C273", "3c273", 1], ValueError, "Data selected with select_mask includes"],
        [[-1], ValueError, "No entry with the ID -1 found in the catalog"],
        [[1, None, None, "hi"], TypeError, "Value provided to new_id must be an int"],
        [[1, None, None, 2], ValueError, "The ID 2 is already in the catalog"],
        [[35.5], TypeError, "catalog_identifier must be a string or an integer."],
    ),
)
def test_split_phase_center_bad_args(carma_miriad, args, err_type, msg):
    """
    Verify that split_phase_center will throw an error if supplied with bad args
    """
    with pytest.raises(err_type, match=msg):
        carma_miriad.split_phase_center(*args)


def test_split_phase_center_err_multiname(carma_miriad):
    """
    Verify that split_phase_center will throw an error if multiple fields
    have the same name in the dataset.
    """
    for key in carma_miriad.phase_center_catalog:
        carma_miriad.phase_center_catalog[key]["cat_name"] = "NOISE"
    with pytest.raises(ValueError, match="The cat_name NOISE has multiple entries in"):
        carma_miriad.split_phase_center("NOISE")


@pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad file,")
@pytest.mark.parametrize(
    "cat_iden,err_type,msg",
    (
        ["dummy1", ValueError, "No entry by the name dummy1 in"],
        [[0, 1, 2], ValueError, "Attributes of phase centers differ"],
        [[-1, -2], ValueError, "No entry with the ID -1 in the catalog."],
        [
            [0, 1.5],
            TypeError,
            "catalog_identifier must be a string, an integer or a list of strings or "
            "integers.",
        ],
    ),
)
def test_merge_phase_centers_bad_args(carma_miriad, cat_iden, err_type, msg):
    """
    Verify that merge_phase_centers will throw an error if supplied with bad args
    """
    pytest.importorskip("pyuvdata._miriad")
    with pytest.raises(err_type, match=msg):
        carma_miriad.merge_phase_centers(cat_iden)


def test_merge_phase_centers_bad_warn(sma_mir):
    with uvtest.check_warnings(UserWarning, "Selection matches less than two phase"):
        sma_mir.merge_phase_centers("3c84")


@pytest.mark.parametrize(
    "cat_id,new_id,res_id,err_type,msg",
    (
        [-1, -1, 0, ValueError, "Cannot run _update_phase_center_id: no entry"],
        [1, 1, [1], ValueError, "Catalog ID supplied already taken by another"],
    ),
)
def test_update_id_bad_args(sma_mir, cat_id, new_id, res_id, err_type, msg):
    """
    Verify that _update_phase_center_id throws errors when supplied with bad args
    """
    with pytest.raises(err_type, match=msg):
        sma_mir._update_phase_center_id(cat_id, new_id, reserved_ids=res_id)


def test_add_clear_phase_center(sma_mir):
    """
    Test that we can add a phase center, verify that we can find it correctly in the
    catalog, and then clear it as its unused.
    """
    check_dict = sma_mir.phase_center_catalog.copy()
    check_id = sma_mir._add_phase_center(
        "Mars",
        cat_type="ephem",
        cat_lon=[0.0, 1.0],
        cat_lat=[0, 1],
        cat_dist=(0, 1),
        cat_vrad=np.array([0, 1], dtype=np.float32),
        cat_times=np.array([0.0, 1.0]),
        cat_frame="icrs",
    )

    # Make sure the catalog ID returns as expected, and that the catalog changed
    assert check_id == 0
    # Check to see that the catalog actually changed
    assert sma_mir.phase_center_catalog != check_dict
    # And ake sure we can ID by name, but find diffs if attributes dont match
    assert sma_mir._look_in_catalog("Mars", cat_lon=[0], cat_lat=[0]) == (0, 7)

    # Finally, clear out the unused entries and check for equivalency w/ the old catalog
    sma_mir._clear_unused_phase_centers()
    assert sma_mir.phase_center_catalog == check_dict


def test_rename_object_capitalization(sma_mir, sma_mir_catalog):
    """
    Verify that renaming works, and that its case sensitive
    """
    # Check and see what happens if we attempt to rename the source
    sma_mir.rename_phase_center("3c84", "3C84")
    assert sma_mir.phase_center_catalog != sma_mir_catalog

    sma_mir.rename_phase_center("3C84", "3c84")
    assert sma_mir.phase_center_catalog == sma_mir_catalog


def test_rename_no_ops(sma_mir, sma_mir_catalog):
    """
    Verify that renaming the phase center with the same name results in no changes
    """
    # Check to make sure that setting the same name doesn't harm anything
    sma_mir.rename_phase_center("3c84", "3c84")
    assert sma_mir.phase_center_catalog == sma_mir_catalog


def test_update_id_no_op(sma_mir, sma_mir_catalog):
    """
    Verify that updating the ID of a source without any ID conflicts results in no
    changes to the catalog
    """
    # This should effectively be a no-op, since the catalog ID of the source isn't
    # being taken up by anything else
    sma_mir._update_phase_center_id(1)
    assert sma_mir.phase_center_catalog == sma_mir_catalog


def test_update_id(sma_mir):
    """
    Verify that calling _update_phase_center_id will produce the lowest available
    positive int as the new ID for the source being updated.
    """
    # If all goes well, this operation should assign the lowest possible integer to the
    # catalog ID of 3c84 -- in this case, 4.
    sma_mir._update_phase_center_id(1, reserved_ids=[0, 1, 2, 3])
    assert list(sma_mir.phase_center_catalog)[0] == 4


@pytest.mark.parametrize(
    "name1,name2,select_mask,msg",
    (
        ["3c84", "3C84", False, "No relevant data selected"],
        ["3c84", "3C84", True, "All data for the source selected"],
    ),
)
def test_split_phase_center_warnings(sma_mir, name1, name2, select_mask, msg):
    # Now let's select no data at all
    with uvtest.check_warnings(UserWarning, match=msg):
        sma_mir.split_phase_center(name1, name2, select_mask)


def test_split_phase_center(hera_uvh5):
    # Phase the HERA file so that we can play around with it a bit
    hera_uvh5.phase(
        cat_name="3c84",
        lon=Longitude("3:19:48.16", unit="hourangle").rad,
        lat=Latitude("+41:30:42.11", unit="deg").rad,
        phase_frame="fk5",
        epoch="J2000",
    )
    # Alright, now let's actually try to split the sources -- let's say every other
    # integration?
    select_mask = np.isin(hera_uvh5.time_array, np.unique(hera_uvh5.time_array)[::2])

    hera_uvh5.split_phase_center("3c84", "3c84_2", select_mask)
    cat_id1 = hera_uvh5._look_for_name("3c84")
    cat_id2 = hera_uvh5._look_for_name("3c84_2")
    # Check that the catalog IDs also line up w/ what we expect
    assert np.all(hera_uvh5.phase_center_id_array[~select_mask] == cat_id1)
    assert np.all(hera_uvh5.phase_center_id_array[select_mask] == cat_id2)
    assert hera_uvh5.Nphase == 2

    cat_id_all = hera_uvh5._look_for_name(["3c84", "3c84_2"])
    assert np.all(np.isin(hera_uvh5.phase_center_id_array, cat_id_all))

    # Make sure the catalog makes sense -- entries should be identical sans cat_id
    temp_cat = hera_uvh5.phase_center_catalog.copy()
    temp_cat[cat_id1[0]]["cat_name"] = "3c84_2"
    assert temp_cat[cat_id1[0]] == temp_cat[cat_id2[0]]


def test_split_phase_center_downselect(hera_uvh5):
    # Phase the HERA file so that we can play around with it a bit
    hera_uvh5.phase(
        cat_name="3c84",
        lon=Longitude("3:19:48.16", unit="hourangle").rad,
        lat=Latitude("+41:30:42.11", unit="deg").rad,
        phase_frame="fk5",
        epoch="J2000",
    )
    catalog_copy = hera_uvh5.phase_center_catalog.copy()

    # Again, only select the first half of the data
    select_mask = np.isin(hera_uvh5.time_array, np.unique(hera_uvh5.time_array)[::2])
    hera_uvh5.split_phase_center("3c84", "3c84_2", select_mask)

    # Now effectively rename zenith2 as zenith3 by selecting all data and using
    # the downselect switch
    with uvtest.check_warnings(UserWarning, "All data for the source selected"):
        hera_uvh5.split_phase_center(
            "3c84_2", "3c84_3", np.arange(hera_uvh5.Nblts), downselect=True
        )

    cat_id1 = hera_uvh5._look_for_name("3c84")
    cat_id3 = hera_uvh5._look_for_name("3c84_3")
    assert np.all(hera_uvh5.phase_center_id_array[~select_mask] == cat_id1)
    assert np.all(hera_uvh5.phase_center_id_array[select_mask] == cat_id3)

    # Make sure the dicts make sense
    temp_dict = hera_uvh5.phase_center_catalog[cat_id1[0]].copy()
    temp_dict["cat_name"] = "3c84_3"
    assert temp_dict == hera_uvh5.phase_center_catalog[cat_id3[0]]

    # Finally, force the two objects back to being one, despite the fact that we've
    # contaminated the dict of one (which will be overwritten by the other)
    hera_uvh5.phase_center_catalog[cat_id3[0]]["cat_epoch"] = 2000.0
    with uvtest.check_warnings(UserWarning, "Forcing fields together, even though"):
        hera_uvh5.merge_phase_centers(cat_id1 + cat_id3, force_merge=True)

    # We merged everything back together, so we _should_  get back the same
    # thing that we started with.
    assert hera_uvh5.phase_center_catalog == catalog_copy
    assert np.all(hera_uvh5.phase_center_id_array == hera_uvh5._look_for_name("3c84"))


@pytest.mark.parametrize(
    "val1,val2,val3,err_type,msg",
    [
        [0.0, 0.0, 1.5, IndexError, "select_mask must be an array-like, either of"],
        [[0.0, 0.0], 0.0, [0], IndexError, "The length of new_w_vals is wrong"],
        [0.0, [0.0, 0.0], [0], IndexError, "The length of old_w_vals is wrong"],
    ],
)
def test_apply_w_arg_errs(hera_uvh5, val1, val2, val3, err_type, msg):
    with pytest.raises(err_type, match=msg):
        hera_uvh5._apply_w_proj(val1, val2, val3)


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_apply_w_no_ops(hera_uvh5, future_shapes):
    """
    Test to make sure that the _apply_w method throws  expected errors
    """
    if not future_shapes:
        hera_uvh5.use_current_array_shapes()

    hera_copy = hera_uvh5.copy()

    # Test to make sure that the following gives us back the same results,
    # first without a selection mask
    hera_uvh5._apply_w_proj(0.0, 0.0)
    assert hera_uvh5 == hera_copy

    # And now with a selection mask applied
    hera_uvh5._apply_w_proj([0.0, 1.0], [0.0, 1.0], [0, 1])
    assert hera_uvh5 == hera_copy


@pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad file,")
def test_phase_dict_helper_err_multi_match(carma_miriad):
    """
    Verify that _phase_dict_helper will throw an error if multiple fields
    have the same name in the dataset when using lookup.
    """
    for key in carma_miriad.phase_center_catalog:
        carma_miriad.phase_center_catalog[key]["cat_name"] = "NOISE"
    args = [None] * 13
    args[10:12] = "NOISE", True
    with pytest.raises(ValueError, match="Name of object has multiple matches in "):
        carma_miriad._phase_dict_helper(*args)


def test_phase_dict_helper_simple(hera_uvh5, sma_mir, dummy_phase_dict):
    """
    Verify that _phase_dict_helper behaves appropriately when being handed a "typical"
    set of parameters, for a single-phase-ctr (hera_uvh5) and multi-phase-ctr (sma_mir)
    objects alike.
    """
    # If we could parameterize fixtures, I'd do that, but for now I'll use a simple
    # for loop to move through two different datasets.
    for uv_object in [hera_uvh5, sma_mir]:
        phase_dict = uv_object._phase_dict_helper(
            dummy_phase_dict["cat_lon"],
            dummy_phase_dict["cat_lat"],
            dummy_phase_dict["cat_epoch"],
            dummy_phase_dict["cat_frame"],
            dummy_phase_dict["cat_times"],
            dummy_phase_dict["cat_type"],
            dummy_phase_dict["cat_pm_ra"],
            dummy_phase_dict["cat_pm_dec"],
            dummy_phase_dict["cat_dist"],
            dummy_phase_dict["cat_vrad"],
            dummy_phase_dict["cat_name"],
            False,  # Don't lookup source
            None,  # Don't supply a time_array
        )
        assert phase_dict == dummy_phase_dict


@pytest.mark.parametrize(
    "arg_dict, msg",
    [
        [{"lookup": True}, "Unable to find z1 in among the existing sources recorded"],
        [
            {"cat_type": "ephem", "cat_epoch": None, "cat_times": 1, "time_arr": 0},
            "Ephemeris data does not cover",
        ],
        [
            {"cat_type": "ephem", "time_arr": 2456789, "lookup": True},
            "Target ID is not recognized in either the small or major",
        ],
    ],
)
def test_phase_dict_helper_errs(sma_mir, arg_dict, dummy_phase_dict, msg):
    """
    Test the `_phase_dict_helper` method.

    Test the helper function that the `phase` method uses for looking up astronomical
    source information.
    """
    pytest.importorskip("astroquery")

    from ssl import SSLError

    from requests import RequestException

    for key in dummy_phase_dict.keys():
        if key not in arg_dict.keys():
            arg_dict[key] = dummy_phase_dict[key]

    # We have to handle this piece a bit carefully, since some queries fail due to
    # intermittent failures connecting to the JPL-Horizons service.
    with pytest.raises(Exception) as cm:
        sma_mir._phase_dict_helper(
            arg_dict["cat_lon"],
            arg_dict["cat_lat"],
            arg_dict["cat_epoch"],
            arg_dict["cat_frame"],
            arg_dict["cat_times"],
            arg_dict["cat_type"],
            arg_dict["cat_pm_ra"],
            arg_dict["cat_pm_dec"],
            arg_dict["cat_dist"],
            arg_dict["cat_vrad"],
            arg_dict["cat_name"],
            arg_dict.get("lookup"),
            arg_dict.get("time_arr"),
        )

    if issubclass(cm.type, RequestException) or issubclass(cm.type, SSLError):
        pytest.skip("SSL/Connection error w/ JPL Horizons")

    assert issubclass(cm.type, ValueError)
    assert str(cm.value).startswith(msg)


def test_phase_dict_helper_sidereal_lookup(sma_mir, dummy_phase_dict):
    """
    Check that we can use the lookup option to find a sidereal source properties in
    a multi-phase-ctr dataset.
    """
    phase_dict = sma_mir._phase_dict_helper(
        dummy_phase_dict["cat_lon"],
        dummy_phase_dict["cat_lat"],
        dummy_phase_dict["cat_epoch"],
        dummy_phase_dict["cat_frame"],
        dummy_phase_dict["cat_times"],
        dummy_phase_dict["cat_type"],
        dummy_phase_dict["cat_pm_ra"],
        dummy_phase_dict["cat_pm_dec"],
        dummy_phase_dict["cat_dist"],
        dummy_phase_dict["cat_vrad"],
        "3c84",
        True,  # Do lookup source!
        None,  # Don't supply a time_array
    )
    assert phase_dict.pop("cat_id") == sma_mir._look_for_name("3c84")[0]
    assert phase_dict == sma_mir.phase_center_catalog[sma_mir._look_for_name("3c84")[0]]


def test_phase_dict_helper_jpl_lookup_existing(sma_mir):
    """
    Verify that the _phase_dict_helper function correctly hands back a dict that
    matches that in the catalog, provided the source properties match.
    """
    # Finally, check that we get a good result if feeding the same values, even if not
    # actually performing a lookup
    cat_id = sma_mir._look_for_name("3c84")[0]
    phase_dict = sma_mir._phase_dict_helper(
        sma_mir.phase_center_catalog[cat_id].get("cat_lon"),
        sma_mir.phase_center_catalog[cat_id].get("cat_lat"),
        sma_mir.phase_center_catalog[cat_id].get("cat_epoch"),
        sma_mir.phase_center_catalog[cat_id].get("cat_frame"),
        sma_mir.phase_center_catalog[cat_id].get("cat_times"),
        sma_mir.phase_center_catalog[cat_id].get("cat_type"),
        sma_mir.phase_center_catalog[cat_id].get("cat_pm_ra"),
        sma_mir.phase_center_catalog[cat_id].get("cat_pm_dec"),
        sma_mir.phase_center_catalog[cat_id].get("cat_dist"),
        sma_mir.phase_center_catalog[cat_id].get("cat_vrad"),
        "3c84",
        False,
        sma_mir.time_array,
    )
    assert phase_dict.pop("cat_id") == cat_id
    assert phase_dict == sma_mir.phase_center_catalog[sma_mir._look_for_name("3c84")[0]]


def test_phase_dict_helper_jpl_lookup_append(sma_mir):
    """
    Test _phase_dict_helper to see if it will correctly call the JPL lookup when
    an old ephem does not cover the newly requested time range
    """
    pytest.importorskip("astroquery")

    from ssl import SSLError

    from requests import RequestException

    # Now see what happens if we attempt to lookup something that JPL actually knows
    obs_time = np.array(2456789.0)

    # Handle this part with care, since we don't want the test to fail if we are unable
    # to reach the JPL-Horizons service.
    try:
        phase_dict = sma_mir._phase_dict_helper(
            0, 0, None, None, None, None, 0, 0, 0, 0, "Mars", True, obs_time
        )
    except (SSLError, RequestException) as err:
        pytest.skip("SSL/Connection error w/ JPL Horizons: " + str(err))

    cat_id = sma_mir._add_phase_center(
        phase_dict["cat_name"],
        phase_dict["cat_type"],
        cat_lon=phase_dict["cat_lon"],
        cat_lat=phase_dict["cat_lat"],
        cat_frame=phase_dict["cat_frame"],
        cat_epoch=phase_dict["cat_epoch"],
        cat_times=phase_dict["cat_times"],
        cat_pm_ra=phase_dict["cat_pm_ra"],
        cat_pm_dec=phase_dict["cat_pm_dec"],
        cat_dist=phase_dict["cat_dist"],
        cat_vrad=phase_dict["cat_vrad"],
        info_source=phase_dict["info_source"],
        force_update=True,
    )

    # By default, the catalog ID here should be zero (lowest available ID)
    assert cat_id == 0

    # Tick the obs_time up by a day, see if the software will fetch additional
    # coordinates and expand the existing ephem
    obs_time += 1

    # Again, just skip if we are unable to reach the JPL-Horizons
    try:
        phase_dict = sma_mir._phase_dict_helper(
            0, 0, None, None, None, None, 0, 0, 0, 0, "Mars", True, obs_time
        )
    except (SSLError, RequestException) as err:
        pytest.skip("SSL/Connection error w/ JPL Horizons: " + str(err))

    # Previously, everything else will have had a single point, but the new ephem (which
    # covers 36 hours at 3 hour intervals) should have a lucky total of 13 points.
    keycheck = ["cat_lon", "cat_lat", "cat_vrad", "cat_dist", "cat_times"]
    for key in keycheck:
        assert len(phase_dict[key]) == 13


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The original `phase` method is deprecated")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize("use_ant_pos", [True, False])
def test_fix_phase(hera_uvh5, future_shapes, use_ant_pos):
    """
    Test the phase fixing method fix_phase
    """
    if not future_shapes:
        hera_uvh5.use_current_array_shapes()

    # Make some copies of the data
    uv_in = hera_uvh5.copy()
    uv_in_bad = hera_uvh5

    # These values could be anything -- we're just picking something that we know should
    # be visible from the telescope at the time of obs (ignoring horizon limits).
    phase_ra = uv_in.lst_array[-1]
    phase_dec = uv_in.telescope_location_lat_lon_alt[0] * 0.333

    # Do the improved phasing on the dat set.
    with uvtest.check_warnings(
        DeprecationWarning,
        match="The `fix_old_proj` parameter is deprecated and will be removed",
    ):
        uv_in.phase(phase_ra, phase_dec, fix_old_proj=True, cat_name="foo")

    # First test the case where we are using the old phase method with the uvws
    # calculated from the antenna positions. Using fix phase here should be "perfect",
    # since the uvws are completely recalculated from scratch.
    with uvtest.check_warnings(
        DeprecationWarning,
        match="The `use_old_proj` option is deprecated and will be removed",
    ):
        uv_in_bad.phase(
            phase_ra,
            phase_dec,
            use_old_proj=True,
            use_ant_pos=use_ant_pos,
            cat_name="foo",
        )

    if use_ant_pos:
        warn_msg = "Fixing phases using antenna positions."
    else:
        warn_msg = "Attempting to fix residual phasing errors from the old `phase`"

    with uvtest.check_warnings(UserWarning, warn_msg):
        uv_in_bad.fix_phase(use_ant_pos=use_ant_pos)

    # We have to handle this case a little carefully, because since the old
    # unproject_phase was _mostly_ accurate, although it does seem to intoduce errors
    # on the order of a part in 1e5, which translates to about a tenth of a degree phase
    # error in the test data set used here. Check that first, make sure it's good
    assert np.allclose(uv_in.data_array, uv_in_bad.data_array, rtol=3e-4)

    # Once we know the data are okay, copy over data array and check for equality btw
    # the other attributes of the two objects.
    uv_in_bad.data_array = uv_in.data_array
    assert uv_in == uv_in_bad


def test_fix_phase_error(hera_uvh5):
    uv_in = hera_uvh5
    uv_in.phase(
        lon=0, lat=np.pi / 2, cat_type="driftscan", phase_frame="altaz", cat_name="foo"
    )

    with pytest.raises(
        ValueError,
        match="Objects with driftscan phase centers were not phased with the old "
        "method, so no fixing is required.",
    ):
        uv_in.fix_phase()


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_multi_file_ignore_name(hera_uvh5_split, future_shapes):
    """
    Verify that if phased two objects to the same position with different names, we
    can successfully use the "ignore_name" switch in the add operation to allow
    the two objects to be combined.
    """
    uv1, uv2, uvfull = hera_uvh5_split
    if not future_shapes:
        uv1.use_current_array_shapes()
        uv2.use_current_array_shapes()
        uvfull.use_current_array_shapes()

    # Phase both targets to the same position with different names
    uv1.phase(3.6, -0.5, cat_name="target1")
    uv2.phase(3.6, -0.5, cat_name="target2")
    uvfull.phase(3.6, -0.5, cat_name="target1")

    # Check that you end up with two phase centers if you don't ignore the name
    uv3 = uv1 + uv2
    assert uv3.Nphase == 2

    # Check that you only end up with one phase centers if you ignore the name
    uv3 = uv1.__add__(uv2, ignore_name=True, inplace=False)
    assert uv3.Nphase == 1
    # The reorders here are neccessary after the add to make sure that the baseline
    # ordering is consistent between the objects
    uv3.reorder_blts()
    uvfull.reorder_blts()

    # Make sure that after the add, everything agrees
    assert uvfull.history in uv3.history
    uvfull.history = uv3.history

    uv3._consolidate_phase_center_catalogs(
        reference_catalog=uvfull.phase_center_catalog, ignore_name=True
    )


@pytest.mark.parametrize("test_op", [None, "split", "rename", "merge", "r+m"])
def test_multi_phase_split_merge_rename(hera_uvh5_split, test_op):
    """
    Test the split, merge, and rename operations, and make sure their operations
    are internally consistent.
    """
    uv1, uv2, uvfull = hera_uvh5_split
    half_mask = np.arange(uvfull.Nblts) < (uvfull.Nblts * 0.5)

    uv1.phase(3.6, -0.5, cat_name="target1")
    uv2.phase(3.6, -0.5, cat_name="target1" if (test_op is None) else "target2")
    uv3 = uv1 + uv2
    uv3.reorder_blts()

    uvfull.reorder_blts()
    uvfull.phase(3.6, -0.5, cat_name="target1")
    uvfull._update_phase_center_id(
        list(uvfull.phase_center_catalog)[0], 1 if (test_op is None) else 0
    )

    # Any of these operations should allow for the objects to become equal to the
    # other -- they're basically the inverse action taken on two different objects.
    if test_op is None:
        # Nothing to do here -- this should be an apples-to-apples comparison without
        # any renaming operations.
        pass
    if test_op == "split":
        uvfull.split_phase_center(
            select_mask=~half_mask, catalog_identifier=0, new_id=1, new_name="target2"
        )
    elif test_op == "rename":
        uv3.merge_phase_centers(list(uv3.phase_center_catalog)[::-1], ignore_name=True)
        uvfull.rename_phase_center("target1", "target2")
        uvfull._update_phase_center_id(0, 1)
    elif test_op == "merge":
        uv3.merge_phase_centers(["target1", "target2"], ignore_name=True)
    elif test_op == "r+m":
        uv3.rename_phase_center("target2", "target1")
        uv3.merge_phase_centers("target1")

    assert uvfull.history in uv3.history
    uvfull.history = uv3.history
    assert uvfull == uv3


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize("catid", [0, 1])
def test_multi_phase_add(hera_uvh5_split, catid, future_shapes):
    uv1, uv2, uvfull = hera_uvh5_split
    if not future_shapes:
        uv1.use_current_array_shapes()
        uv2.use_current_array_shapes()
        uvfull.use_current_array_shapes()

    # Give it a new name, and then rephase half of the "full" object
    uv1.phase(3.6, -0.5, cat_name="target1")
    uv2.phase(-0.5, 3.6, cat_name="target2")

    # Test that addition handles cat ID collisions correctly
    for pc_id in list(uv2.phase_center_catalog):
        if uv2.phase_center_catalog[pc_id]["cat_name"] == "target2":
            uv2._update_phase_center_id(pc_id, catid)

    # Add the objects together
    with uvtest.check_warnings(
        DeprecationWarning,
        match="The `make_multi_phase` option is deprecated and has no effect",
    ):
        uv3 = uv1.__add__(uv2, make_multi_phase=True)
    uv3.reorder_blts()

    # Separately phase both halves of the full data set
    half_mask = np.arange(uvfull.Nblts) < (uvfull.Nblts * 0.5)
    uvfull.phase(-0.5, 3.6, cat_name="target2", select_mask=~half_mask)
    uvfull.phase(3.6, -0.5, cat_name="target1", select_mask=half_mask)
    uvfull.reorder_blts()

    # Check that the histories line up
    assert uvfull.history in uv3.history
    uvfull.history = uv3.history

    # By construct, we've made it so that the cat IDs don't line up, but everything
    # else should. Make sure the IDs are different, but contain the same entries/names
    # for the phase centers
    assert np.any(uv3.phase_center_id_array != uvfull.phase_center_id_array)
    assert uv3.phase_center_catalog != uvfull.phase_center_catalog

    # Update the Obs IDs, and make sure that _now_ the objects are equal
    name_map1 = {
        pc_dict["cat_name"]: pc_id
        for pc_id, pc_dict in uv3.phase_center_catalog.items()
    }
    name_map2 = {
        pc_dict["cat_name"]: pc_id
        for pc_id, pc_dict in uvfull.phase_center_catalog.items()
    }

    uv3._update_phase_center_id(name_map1["target1"], 100)
    uv3._update_phase_center_id(name_map1["target2"], 101)
    uv3._update_phase_center_id(100, name_map2["target1"])
    uv3._update_phase_center_id(101, name_map2["target2"])

    assert uv3 == uvfull


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("cat_type", ["sidereal", "ephem", "driftscan"])
@pytest.mark.parametrize("future_shapes", [True, False])
def test_multi_phase_downselect(hera_uvh5_split, cat_type, future_shapes):
    """
    Verify that we can create the same UVData object if we phase then downselect
    vs downselect and phase when working with a multi-phase-ctr object.
    """
    if cat_type == "ephem":
        pytest.importorskip("astroquery")

    uv1, uv2, uvfull = hera_uvh5_split
    if not future_shapes:
        uv1.use_current_array_shapes()
        uv2.use_current_array_shapes()
        uvfull.use_current_array_shapes()

    # get the halves of the full data set
    half_mask = np.arange(uvfull.Nblts) < (uvfull.Nblts * 0.5)
    unique_times = np.unique(uvfull.time_array)

    # Give it a new name, and then rephase half of the "full" object
    if cat_type == "sidereal":
        uv1.phase(3.6, -0.5, cat_name="target1")
        uv2.phase(-0.5, 3.6, cat_name="target2")
        uvfull.phase(-0.5, 3.6, cat_name="target2", select_mask=~half_mask)
        uvfull.phase(3.6, -0.5, cat_name="target1", select_mask=half_mask)
    elif cat_type == "ephem":
        from ssl import SSLError

        from requests import RequestException

        try:
            uv1.phase(ra=0, dec=0, epoch="J2000", lookup_name="Mars", cat_name="Mars")
            uv2.phase(
                ra=0, dec=0, epoch="J2000", lookup_name="Jupiter", cat_name="Jupiter"
            )
            uvfull.phase(
                0, 0, lookup_name="Jupiter", cat_name="Jupiter", select_mask=~half_mask
            )
            uvfull.phase(
                0, 0, lookup_name="Mars", cat_name="Mars", select_mask=half_mask
            )
        except (SSLError, RequestException) as err:
            pytest.skip("SSL/Connection error w/ JPL Horizons: " + str(err))
    elif cat_type == "driftscan":
        uv1.phase(3.6, -0.5, cat_type=cat_type, phase_frame=None, cat_name="drift1")
        uv2.phase(-0.5, 3.6, cat_type=cat_type, phase_frame="altaz", cat_name="drift2")
        uvfull.phase(
            -0.5,
            3.6,
            cat_type=cat_type,
            cat_name="drift2",
            phase_frame=None,
            select_mask=~half_mask,
        )
        uvfull.phase(
            3.6,
            -0.5,
            cat_type=cat_type,
            cat_name="drift1",
            phase_frame="altaz",
            select_mask=half_mask,
        )

    uv1.reorder_blts()
    uv2.reorder_blts()

    for mask, uvdata in zip([np.arange(10), np.arange(10, 20)], [uv1, uv2]):
        uvtemp = uvfull.select(times=unique_times[mask], inplace=False)
        uvtemp.reorder_blts()
        # Select does not clear the catalog, so clear the unused source and
        # update the cat ID so that it matches with the indv datasets
        uvtemp._clear_unused_phase_centers()
        uvtemp._update_phase_center_id(list(uvtemp.phase_center_catalog.keys())[0], 1)
        assert uvtemp.history in uvdata.history
        uvtemp.history = uvdata.history
        assert uvtemp == uvdata


@pytest.mark.filterwarnings("ignore:Unknown phase types are no longer supported")
@pytest.mark.filterwarnings("ignore:Telescope mock-HERA is not in known_telescopes")
def test_eq_allowed_failures(bda_test_file, capsys):
    """
    Test that the allowed_failures keyword on the __eq__ method works as intended.
    """
    uv1 = bda_test_file
    uv2 = uv1.copy()

    # adjust optional parameters to be different
    uv1.x_orientation = "NORTH"
    uv2.x_orientation = "EAST"
    assert uv1.__eq__(uv2, check_extra=True, allowed_failures=["x_orientation"])
    captured = capsys.readouterr()
    assert captured.out == (
        "x_orientation parameter value is a string, values are different\n"
        "parameter _x_orientation does not match, but is not required to for equality. "
        "Left is NORTH, right is EAST.\n"
    )

    # make sure that objects are not equal without specifying allowed_failures
    assert uv1 != uv2

    return


@pytest.mark.filterwarnings("ignore:Unknown phase types are no longer supported")
@pytest.mark.filterwarnings("ignore:Telescope mock-HERA is not in known_telescopes")
def test_eq_allowed_failures_filename(bda_test_file, capsys):
    """
    Test that the `filename` parameter does not trip up the __eq__ method.
    """
    uv1 = bda_test_file
    uv2 = uv1.copy()

    uv1.filename = ["foo.uvh5"]
    uv2.filename = ["bar.uvh5"]
    assert uv1 == uv2
    captured = capsys.readouterr()
    assert captured.out == (
        "filename parameter value is a list of strings, values are different\n"
        "parameter _filename does not match, but is not required to for equality. "
        "Left is ['foo.uvh5'], right is ['bar.uvh5'].\n"
    )

    return


@pytest.mark.filterwarnings("ignore:Unknown phase types are no longer supported")
@pytest.mark.filterwarnings("ignore:Telescope mock-HERA is not in known_telescopes")
def test_eq_allowed_failures_filename_string(bda_test_file, capsys):
    """
    Try passing a string to the __eq__ method instead of an iterable.
    """
    uv1 = bda_test_file
    uv2 = uv1.copy()

    uv1.filename = ["foo.uvh5"]
    uv2.filename = ["bar.uvh5"]
    assert uv1.__eq__(uv2, allowed_failures="filename")
    captured = capsys.readouterr()
    assert captured.out == (
        "filename parameter value is a list of strings, values are different\n"
        "parameter _filename does not match, but is not required to for equality. "
        "Left is ['foo.uvh5'], right is ['bar.uvh5'].\n"
    )


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_set_data(hera_uvh5, future_shapes):
    """
    Test setting data for a given baseline.
    """
    uv = hera_uvh5

    if not future_shapes:
        uv.use_current_array_shapes()

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    data = 2 * uv.get_data(ant1, ant2, squeeze="none", force_copy=True)
    inds1, inds2, indp = uv._key2inds((ant1, ant2))
    uv.set_data(data, ant1, ant2)
    data2 = uv.get_data(ant1, ant2, squeeze="none")

    assert np.allclose(data, data2)
    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_set_data_evla(future_shapes):
    """
    Test setting data for a given baseline on a different test file.
    """
    filename = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits")
    uv = UVData()
    uv.read(filename, use_future_array_shapes=True)

    if not future_shapes:
        uv.use_current_array_shapes()

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    data = 2 * uv.get_data(ant1, ant2, squeeze="none", force_copy=True)
    inds1, inds2, indp = uv._key2inds((ant1, ant2))
    uv.set_data(data, ant1, ant2)
    data2 = uv.get_data(ant1, ant2, squeeze="none")

    assert np.allclose(data, data2)
    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_set_data_polkey(hera_uvh5, future_shapes):
    """
    Test setting data for a given baseline with a specific polarization.
    """
    uv = hera_uvh5

    if not future_shapes:
        uv.use_current_array_shapes()

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    pol = "xx"
    data = 2 * uv.get_data(ant1, ant2, pol, squeeze="none", force_copy=True)
    inds1, inds2, indp = uv._key2inds((ant1, ant2, pol))
    uv.set_data(data, ant1, ant2, pol)
    data2 = uv.get_data(ant1, ant2, pol, squeeze="none")

    assert np.allclose(data, data2)
    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_set_flags(hera_uvh5, future_shapes):
    """
    Test setting flags for a given baseline.
    """
    uv = hera_uvh5
    if not future_shapes:
        uv.use_current_array_shapes()

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    flags = uv.get_flags(ant1, ant2, squeeze="none", force_copy=True)
    if future_shapes:
        flags[:, :, :] = True
    else:
        flags[:, :, :, :] = True
    inds1, inds2, indp = uv._key2inds((ant1, ant2))
    uv.set_flags(flags, ant1, ant2)
    flags2 = uv.get_flags(ant1, ant2, squeeze="none")

    assert np.allclose(flags, flags2)
    assert not np.allclose(uv.flag_array, True)
    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_set_flags_polkey(hera_uvh5, future_shapes):
    """
    Test setting flags for a given baseline with a specific polarization.
    """
    uv = hera_uvh5
    if not future_shapes:
        uv.use_current_array_shapes()

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    pol = "xx"
    flags = uv.get_flags(ant1, ant2, pol, squeeze="none", force_copy=True)
    if future_shapes:
        flags[:, :, :] = True
    else:
        flags[:, :, :, :] = True
    inds1, inds2, indp = uv._key2inds((ant1, ant2, pol))
    uv.set_flags(flags, ant1, ant2, pol)
    flags2 = uv.get_flags(ant1, ant2, pol, squeeze="none")

    assert np.allclose(flags, flags2)
    assert not np.allclose(uv.flag_array, True)
    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_set_nsamples(hera_uvh5, future_shapes):
    """
    Test setting nsamples for a given baseline.
    """
    uv = hera_uvh5
    if not future_shapes:
        uv.use_current_array_shapes()

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    nsamples = uv.get_nsamples(ant1, ant2, squeeze="none", force_copy=True)
    if future_shapes:
        nsamples[:, :, :] = np.pi
    else:
        nsamples[:, :, :, :] = np.pi
    inds1, inds2, indp = uv._key2inds((ant1, ant2))
    uv.set_nsamples(nsamples, ant1, ant2)
    nsamples2 = uv.get_nsamples(ant1, ant2, squeeze="none")

    assert np.allclose(nsamples, nsamples2)
    assert not np.allclose(uv.nsample_array, np.pi)
    return


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_set_nsamples_polkey(hera_uvh5, future_shapes):
    """
    Test setting nsamples for a given baseline with a specific polarization.
    """
    uv = hera_uvh5
    if not future_shapes:
        uv.use_current_array_shapes()

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    pol = "xx"
    nsamples = uv.get_nsamples(ant1, ant2, pol, squeeze="none", force_copy=True)
    if future_shapes:
        nsamples[:, :, :] = np.pi
    else:
        nsamples[:, :, :, :] = np.pi
    inds1, inds2, indp = uv._key2inds((ant1, ant2, pol))
    uv.set_nsamples(nsamples, ant1, ant2, pol)
    nsamples2 = uv.get_nsamples(ant1, ant2, pol, squeeze="none")

    assert np.allclose(nsamples, nsamples2)
    assert not np.allclose(uv.nsample_array, np.pi)
    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_data_bad_key_error(hera_uvh5):
    """
    Test an error is raised when a key has too many values.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    data = uv.get_data(ant1, ant2, squeeze="none", force_copy=True)
    match = "no more than 3 key values can be passed"
    with pytest.raises(ValueError, match=match):
        uv.set_data(data, (ant1, ant2, "xx", "foo"))

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_data_conj_data_error(hera_uvh5):
    """
    Test an error is raised when a conjugated baseline is specified.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    data = uv.get_data(ant1, ant2, squeeze="none", force_copy=True)
    match = "the requested key is present on the object, but conjugated"
    with pytest.raises(ValueError, match=match):
        uv.set_data(data, (ant2, ant1))

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_data_wrong_shape_error(hera_uvh5):
    """
    Test an error is raised when the data are the wrong shape.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    data = uv.get_data(ant1, ant2, squeeze="none", force_copy=True)
    # make data the wrong rank
    data = data[0]
    match = "the input array is not compatible with the shape of the destination"
    with pytest.raises(ValueError, match=match):
        uv.set_data(data, (ant1, ant2))

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_flags_bad_key_error(hera_uvh5):
    """
    Test an error is raised when a key has too many values.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    flags = uv.get_data(ant1, ant2, squeeze="none", force_copy=True)
    match = "no more than 3 key values can be passed"
    with pytest.raises(ValueError, match=match):
        uv.set_flags(flags, (ant1, ant2, "xx", "foo"))

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_flags_conj_data_error(hera_uvh5):
    """
    Test an error is raised when a conjugated baseline is specified.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    flags = uv.get_flags(ant1, ant2, squeeze="none", force_copy=True)
    match = "the requested key is present on the object, but conjugated"
    with pytest.raises(ValueError, match=match):
        uv.set_flags(flags, (ant2, ant1))

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_flags_wrong_shape_error(hera_uvh5):
    """
    Test an error is raised when the flags are the wrong shape.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    flags = uv.get_flags(ant1, ant2, squeeze="none", force_copy=True)
    # make data the wrong rank
    flags = flags[0]
    match = "the input array is not compatible with the shape of the destination"
    with pytest.raises(ValueError, match=match):
        uv.set_flags(flags, (ant1, ant2))

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_nsamples_bad_key_error(hera_uvh5):
    """
    Test an error is raised when a key has too many values.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    nsamples = uv.get_data(ant1, ant2, squeeze="none", force_copy=True)
    match = "no more than 3 key values can be passed"
    with pytest.raises(ValueError, match=match):
        uv.set_nsamples(nsamples, (ant1, ant2, "xx", "foo"))

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_nsamples_conj_data_error(hera_uvh5):
    """
    Test an error is raised when a conjugated baseline is specified.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    nsamples = uv.get_nsamples(ant1, ant2, squeeze="none", force_copy=True)
    match = "the requested key is present on the object, but conjugated"
    with pytest.raises(ValueError, match=match):
        uv.set_nsamples(nsamples, (ant2, ant1))

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_set_nsamples_wrong_shape_error(hera_uvh5):
    """
    Test an error is raised when the nsamples are the wrong shape.
    """
    uv = hera_uvh5

    ant1 = np.unique(uv.antenna_numbers)[0]
    ant2 = np.unique(uv.antenna_numbers)[1]
    nsamples = uv.get_nsamples(ant1, ant2, squeeze="none", force_copy=True)
    # make data the wrong rank
    nsamples = nsamples[0]
    match = "the input array is not compatible with the shape of the destination"
    with pytest.raises(ValueError, match=match):
        uv.set_nsamples(nsamples, (ant1, ant2))

    return


@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only,")
@pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad file,")
@pytest.mark.filterwarnings("ignore:using known location values for SZA.")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected")
@pytest.mark.filterwarnings("ignore:pamatten in extra_keywords is a list, array")
@pytest.mark.filterwarnings("ignore:psys in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:psysattn in extra_keywords is a list, array or")
@pytest.mark.filterwarnings("ignore:ambpsys in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:bfmask in extra_keywords is a list, array or dict")
@pytest.mark.filterwarnings("ignore:Telescope location derived from obs lat/lon/alt")
@pytest.mark.parametrize(
    "filename",
    [
        "zen.2458661.23480.HH.uvh5",
        "sma_test.mir",
        "carma_miriad",
        "1133866760.uvfits",
        fhd_files,
    ],
)
def test_from_file(filename):
    if "miriad" in filename:
        pytest.importorskip("pyuvdata._miriad")
    if isinstance(filename, str):
        testfile = os.path.join(DATA_PATH, filename)
    else:
        testfile = filename
    uvd = UVData()
    uvd.read(testfile, use_future_array_shapes=True)
    uvd2 = UVData.from_file(testfile, use_future_array_shapes=True)
    assert uvd == uvd2


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("add_type", ["blt", "freq", "pol"])
@pytest.mark.parametrize("sort_type", ["blt", "freq", "pol"])
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_add_pol_sorting_bl(casa_uvfits, add_type, sort_type, future_shapes):
    if not future_shapes:
        casa_uvfits.use_current_array_shapes()

    if add_type == "pol":
        uv1 = casa_uvfits.select(polarizations=["ll", "lr"], inplace=False)
        uv2 = casa_uvfits.select(polarizations=["rr", "rl"], inplace=False)
    elif add_type == "blt":
        uv1 = casa_uvfits.select(
            blt_inds=np.arange(0, casa_uvfits.Nblts // 2), inplace=False
        )
        uv2 = casa_uvfits.select(
            blt_inds=np.arange(casa_uvfits.Nblts // 2, casa_uvfits.Nblts), inplace=False
        )
    elif add_type == "freq":
        uv1 = casa_uvfits.select(
            freq_chans=np.arange(0, casa_uvfits.Nfreqs // 2), inplace=False
        )
        uv2 = casa_uvfits.select(
            freq_chans=np.arange(casa_uvfits.Nfreqs // 2, casa_uvfits.Nfreqs),
            inplace=False,
        )

    if sort_type == "blt":
        uv1.reorder_blts("time", "ant1")
        uv2.reorder_blts("time", "ant2")
        casa_uvfits.reorder_blts("bda")
        order_check = uv1.ant_1_array == uv2.ant_1_array
    elif sort_type == "freq":
        uv1.reorder_freqs(channel_order="freq")
        uv2.reorder_freqs(channel_order="-freq")
        casa_uvfits.reorder_freqs("freq")
        order_check = uv1.freq_array == uv2.freq_array
    elif sort_type == "pol":
        uv1.reorder_pols("AIPS")
        uv2.reorder_pols("CASA")
        casa_uvfits.reorder_pols("CASA")
        order_check = uv1.polarization_array == uv2.polarization_array

    # Make sure that the order has actually been scrambled
    assert not np.all(order_check)

    # Combine the objects
    uv3 = uv1 + uv2

    if sort_type == "blt":
        uv3.reorder_blts("bda")
    elif sort_type == "freq":
        uv3.reorder_freqs(channel_order="freq")
    elif sort_type == "pol":
        uv3.reorder_pols("CASA")

    # Deal with the history separately, since it will be different
    assert str.startswith(uv3.history, casa_uvfits.history)
    casa_uvfits.history = ""
    uv3.history = ""

    # Finally, make sure everything else lines up
    assert uv3 == casa_uvfits


@pytest.mark.parametrize(
    "pol_sel,flex_err,err_msg",
    [
        [None, True, "Npols must be equal to 1 if flex_spw_polarization_array is set."],
        [-5, True, "polarization_array must all be equal to 0 if flex_spw_pol"],
        [None, False, "polarization_array may not be equal to 0 if flex_spw_pol"],
    ],
)
def test_flex_pol_check_errs(sma_mir, pol_sel, flex_err, err_msg):
    """
    Test that appropriate errors are thrown when flex_spw_polarization_array is set
    incorrectly.
    """
    sma_mir.select(polarizations=pol_sel)
    if flex_err:
        sma_mir.flex_spw_polarization_array = sma_mir.spw_array
    else:
        sma_mir.polarization_array[0] = 0

    with pytest.raises(ValueError, match=err_msg):
        sma_mir.check()


def test_convert_remove_flex_pol_error(uv_phase_comp):
    uvd, _ = uv_phase_comp

    uvd2 = uvd.copy()
    uvd2.convert_to_flex_pol()

    with pytest.raises(ValueError, match="This is already a flex-pol object"):
        uvd2.convert_to_flex_pol()

    uvd2.flex_spw_polarization_array[1] = uvd2.flex_spw_polarization_array[0]
    with pytest.raises(
        ValueError,
        match="Some spectral windows have identical frequencies, "
        "channel widths and polarizations, so spws cannot be "
        "combined. Set combine_spws=False to avoid this error.",
    ):
        uvd2.remove_flex_pol()


def test_sma_make_remove_flex_pol_no_op(sma_mir):
    """
    Test shortcircuits of _make_flex_pol & remove_flex_pol method
    """
    uvd = sma_mir.copy()

    # remove_flex_pol no op
    uvd.select(polarizations=["xx"])
    sma_copy = uvd.copy()
    uvd.remove_flex_pol()

    assert uvd == sma_copy

    # _make_flex_pol no op
    uvd.flag_array[:, : sma_mir.Nfreqs // 2, 0] = True
    uvd._make_flex_pol()
    sma_copy = uvd.copy()

    uvd._make_flex_pol()

    assert uvd == sma_copy


def test_remove_flex_pol_no_op(uv_phase_comp):
    """
    Test shortcircuits of remove_flex_pol method
    """
    # remove_flex_pol with one spw
    uvd, _ = uv_phase_comp

    uvd2 = uvd.copy()
    uvd2.convert_to_flex_pol()
    uvd2.select(polarizations=["xx"])
    uvd2.remove_flex_pol()

    # also remove flex_spw
    uvd2.flex_spw = False
    uvd2._flex_spw_id_array.required = False

    uvd2.check()
    uvd3 = uvd.copy()
    uvd3.select(polarizations=["xx"])
    assert uvd2 == uvd3


def test_remove_flex_pol_no_op_multiple_spws(uv_phase_comp):
    """
    Test shortcircuits of flex_pol method
    """
    # remove_flex_pol with multiple spws but only one pol
    uvd, _ = uv_phase_comp

    uvd2 = uvd.copy()
    uvd2._set_flex_spw()
    uvd2.channel_width = np.full(uvd2.Nfreqs, uvd2.channel_width)
    uvd2.spw_array = np.array([1, 4, 5])
    uvd2.Nspws = 3
    uvd2.flex_spw_id_array = np.zeros(uvd2.Nfreqs, dtype=int)
    uvd2.flex_spw_id_array[: uvd2.Nfreqs // 3] = 1
    uvd2.flex_spw_id_array[uvd2.Nfreqs // 3 : 2 * (uvd2.Nfreqs // 3)] = 4
    uvd2.flex_spw_id_array[2 * (uvd2.Nfreqs // 3) :] = 5
    uvd2.check()
    uvd3 = uvd2.copy()

    uvd2.convert_to_flex_pol()
    uvd2.select(polarizations=["xx"])
    uvd2.remove_flex_pol()

    uvd3.select(polarizations=["xx"])
    assert uvd2 == uvd3


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize(
    "future_shapes,multispw,sorting",
    [
        [True, False, None],
        [False, True, None],
        [False, False, "channel"],
        [True, True, "channel"],
        [False, True, "spw1"],
        [True, True, "spw2"],
    ],
)
def test_flex_pol_uvh5(future_shapes, multispw, sorting, uv_phase_comp, tmp_path):
    """
    Check that we can write out uvh5 files with flex pol data sets.

    This exercises `convert_to_flex_pol` and `remove_flex_pol` and the check_autos for
    flex_pol.
    """

    uvd, _ = uv_phase_comp

    assert uvd.Npols > 1

    if not future_shapes:
        uvd.use_current_array_shapes()

    if multispw:
        # split data into multiple spws
        uvd._set_flex_spw()
        if not future_shapes:
            uvd.channel_width = np.full(uvd.Nfreqs, uvd.channel_width)
        uvd.spw_array = np.array([1, 4, 5])
        uvd.Nspws = 3
        uvd.flex_spw_id_array = np.zeros(uvd.Nfreqs, dtype=int)
        uvd.flex_spw_id_array[: uvd.Nfreqs // 3] = 1
        uvd.flex_spw_id_array[uvd.Nfreqs // 3 : 2 * (uvd.Nfreqs // 3)] = 4
        with pytest.raises(
            ValueError,
            match="All values in the flex_spw_id_array must exist in the spw_array.",
        ):
            uvd.check()
        uvd.flex_spw_id_array[2 * (uvd.Nfreqs // 3) :] = 5

    uvd_orig = uvd.copy()

    # make a copy and reshape improperly to trigger the check_autos code
    uvd2 = uvd.copy(metadata_only=True)
    uvd2.convert_to_flex_pol()
    uvd2.check(check_autos=True)
    if future_shapes:
        uvd2.data_array = uvd.data_array.reshape(uvd.Nblts, uvd.Nfreqs * uvd.Npols, 1)
        uvd2.flag_array = uvd.flag_array.reshape(uvd.Nblts, uvd.Nfreqs * uvd.Npols, 1)
        uvd2.nsample_array = uvd.nsample_array.reshape(
            uvd.Nblts, uvd.Nfreqs * uvd.Npols, 1
        )
    else:
        uvd2.data_array = uvd.data_array.reshape(
            uvd.Nblts, 1, uvd.Nfreqs * uvd.Npols, 1
        )
        uvd2.flag_array = uvd.flag_array.reshape(
            uvd.Nblts, 1, uvd.Nfreqs * uvd.Npols, 1
        )
        uvd2.nsample_array = uvd.nsample_array.reshape(
            uvd.Nblts, 1, uvd.Nfreqs * uvd.Npols, 1
        )
    with pytest.raises(ValueError, match="Some auto-correlations have non-real values"):
        uvd2.check(check_autos=True)

    uvd.convert_to_flex_pol()

    if sorting == "channel":
        spw_reorder = 2
        uvd.reorder_freqs(select_spw=spw_reorder, channel_order="-freq")
    elif sorting == "spw1":
        uvd.reorder_freqs(spw_order="number")
    elif sorting == "spw2":
        if multispw:
            spw_final_order = [1, 4, 5, 0, 3, 2, 6, 7, 8, 9, 10, 11]
            spw_order = np.zeros_like(uvd.spw_array)
            for idx, spw in enumerate(spw_final_order):
                spw_order[idx] = np.nonzero(uvd.spw_array == spw)[0]
            uvd.reorder_freqs(spw_order=spw_order)

    uvd.check(check_autos=True)

    outfile = os.path.join(tmp_path, "test.uvh5")
    uvd.write_uvh5(outfile)
    uvd2 = UVData.from_file(
        outfile, remove_flex_pol=False, use_future_array_shapes=future_shapes
    )

    assert uvd2 == uvd

    uvd3 = UVData.from_file(outfile, use_future_array_shapes=future_shapes)
    uvd2.remove_flex_pol()

    assert uvd3 == uvd2

    if not multispw:
        uvd2.flex_spw = False
        uvd2._flex_spw_id_array.required = False
        if not future_shapes:
            uvd2.channel_width = uvd2.channel_width[0]
            uvd2._channel_width.form = ()

    uvd2.check()

    if multispw and sorting == "spw1":
        # This changes which spw numbers are kept, so need to renumber for equality
        spw_renumber_dict = {0: 1, 2: 4, 3: 5}
        new_spw_array = np.zeros_like(uvd2.spw_array)
        for idx, spw in enumerate(uvd2.spw_array):
            new_spw = spw_renumber_dict[spw]
            new_spw_array[idx] = new_spw
            uvd2.flex_spw_id_array[
                np.nonzero(uvd2.flex_spw_id_array == spw)[0]
            ] = new_spw
        uvd2.spw_array = new_spw_array
        uvd2.check()

    assert uvd_orig == uvd2


@pytest.mark.parametrize(
    "err_msg,param,param_val",
    [
        ["Cannot make a flex-pol UVData object if flex_spw=False.", "flex_spw", False],
        ["Cannot make a flex-pol UVData object, as some windows have", None, None],
    ],
)
def test_make_flex_pol_errs(sma_mir, err_msg, param, param_val):
    """Check to make sure that _make_flex_pol throws correct errors"""

    if param is not None:
        setattr(sma_mir, param, param_val)

    sma_copy = sma_mir.copy()

    with pytest.raises(ValueError, match=err_msg):
        sma_mir._make_flex_pol(True, True)

    with uvtest.check_warnings(UserWarning, err_msg):
        sma_mir._make_flex_pol(False, True)
    assert sma_copy == sma_mir

    with uvtest.check_warnings(None):
        sma_mir._make_flex_pol(False, False)
    assert sma_copy == sma_mir


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("dataset", ["hera", "mwa"])
@pytest.mark.parametrize("future_shapes", [True, False])
def test_auto_check(hera_uvh5, uv_phase_comp, future_shapes, dataset, tmp_path):
    """
    Checks that checking/fixing the autos works correctly, both with dual-pol data
    (supplied by hera_uvh5) and full-pol data (supplied by casa_uvfits).
    """
    if dataset == "hera":
        uv = hera_uvh5
    elif dataset == "mwa":
        uv, _ = uv_phase_comp

    if not future_shapes:
        uv.use_current_array_shapes()

    out_file = os.path.join(tmp_path, "auto_check.uvh5")

    # Corrupt the auto data
    auto_screen = uv.ant_1_array == uv.ant_2_array
    uv.data_array[auto_screen] *= 1 + 0.5j

    with pytest.raises(
        ValueError, match="Some auto-correlations have non-real values in data_array."
    ):
        uv.write_uvh5(out_file, clobber=True)

    uv.write_uvh5(out_file, check_autos=False, clobber=True)

    with pytest.raises(
        ValueError, match="Some auto-correlations have non-real values in data_array."
    ):
        uv1 = uv.from_file(
            out_file, fix_autos=False, use_future_array_shapes=future_shapes
        )
    warn_types = [UserWarning]
    msg = ["Fixing auto-correlations to be be real"]
    if not future_shapes:
        warn_types.append(DeprecationWarning)
        msg.append(_future_array_shapes_warning)
    with uvtest.check_warnings(warn_types, match=msg):
        uv1 = UVData.from_file(out_file, use_future_array_shapes=future_shapes)

    with uvtest.check_warnings(
        UserWarning, match="Fixing auto-correlations to be be real"
    ):
        uv.write_uvh5(out_file, fix_autos=True, clobber=True)

    uv2 = uv.from_file(out_file, use_future_array_shapes=future_shapes)

    assert uv1 == uv2

    assert uv == uv1


@pytest.mark.parametrize(
    "select_kwargs,err_type,err_msg",
    [
        [{"bls": (1, 2)}, ValueError, "No autos available in this data set to do"],
        [{"polarizations": -7}, ValueError, "Cannot normalize xy, matching pols"],
    ],
)
def test_normalize_by_autos_errs(uv_phase_comp, select_kwargs, err_type, err_msg):
    uv, _ = uv_phase_comp
    uv.select(**select_kwargs)
    with pytest.raises(err_type, match=err_msg):
        uv.normalize_by_autos()


@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
@pytest.mark.parametrize("muck_data", ["pol", "time", None])
def test_normalize_by_autos_roundtrip(hera_uvh5, muck_data, future_shapes):
    """
    Check that we can roundtrip autocorrelation normalization under various
    different circumstances.
    """
    if not future_shapes:
        hera_uvh5.use_current_array_shapes()

    if muck_data == "pol":
        # Stick in pseudo-Stokes pols and verify that these roundtrip correctly
        hera_uvh5.polarization_array[:] = [1, 2]
    elif muck_data == "time":
        # Flip the times so that they are now out of order.
        hera_uvh5.time_array[:] = hera_uvh5.time_array[::-1]

    hera_copy = hera_uvh5.copy()
    # Figure out where all the crosses live
    cross_mask = hera_uvh5.ant_1_array != hera_uvh5.ant_2_array

    # Make sure that all the values have high amps
    assert np.all(np.abs(hera_copy.data_array[cross_mask]) > 1)

    # Normalize and verify that things look as expected.
    hera_copy.normalize_by_autos()
    assert np.all(np.abs(hera_copy.data_array[cross_mask]) < 1)
    assert hera_copy != hera_uvh5

    # Complete the roundtrip, and verify that things look identical to how
    # we started.
    hera_copy.normalize_by_autos(invert=True)
    assert hera_copy == hera_uvh5


def test_normalize_by_autos_flag_noautos(hera_uvh5):
    """
    Verify that the crosses are correctly flagged as expected when a given antennas
    autos are missing from the data set.
    """
    hera_uvh5.select(
        bls=[(0, 1), (2, 1), (0, 0), (1, 11), (1, 1), (0, 2), (2, 2), (2, 11), (0, 11)]
    )
    cross_mask = (hera_uvh5.ant_1_array == 11) | (hera_uvh5.ant_2_array == 11)
    assert not np.any(hera_uvh5.flag_array[cross_mask])

    hera_uvh5.normalize_by_autos()
    assert np.all(hera_uvh5.flag_array[cross_mask])


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes")
@pytest.mark.parametrize("multi_phase", [True, False])
def test_split_write_comb_read(tmp_path, multi_phase):
    """Pulled from a failed tutorial example."""
    uvd = UVData()
    filename = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits")

    uvd.read(filename, use_future_array_shapes=True)

    if multi_phase:
        mask = np.full(uvd.Nblts, False)
        mask[: uvd.Nblts // 2] = True
        uvd.phase(ra=0, dec=0, phase_frame="fk5", select_mask=mask, cat_name="foo")

    uvd1 = uvd.select(freq_chans=np.arange(0, 20), inplace=False)
    uvd2 = uvd.select(freq_chans=np.arange(20, 40), inplace=False)
    uvd3 = uvd.select(freq_chans=np.arange(40, 64), inplace=False)

    uvd1.write_uvfits(os.path.join(tmp_path, "select1.uvfits"))
    uvd2.write_uvfits(os.path.join(tmp_path, "select2.uvfits"))
    uvd3.write_uvfits(os.path.join(tmp_path, "select3.uvfits"))
    filenames = [
        os.path.join(tmp_path, f)
        for f in ["select1.uvfits", "select2.uvfits", "select3.uvfits"]
    ]
    uvd2 = UVData()

    warn_type = [DeprecationWarning] + [UserWarning] * 12
    msg = (
        ["The default behavior is to rephase data from the files"]
        + ["Telescope EVLA is not in known_telescopes."] * 3
        + ["The uvw_array does not match the expected values given the antenna"] * 9
    )
    if multi_phase:
        warn_type.append(UserWarning)
        msg.append(
            "Allow_rephase is True but `phase_center_radec` is not set and "
            "the first file has multiple phase centers, so no rephasing "
            "will be done."
        )
    with uvtest.check_warnings(warn_type, match=msg):
        uvd2.read(filenames, use_future_array_shapes=True)

    uvd2._consolidate_phase_center_catalogs(other=uvd)

    uvd2.history = uvd.history
    assert uvd2 == uvd


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.parametrize("set_obj_name", [True, False])
@pytest.mark.parametrize("projected", [True, False])
@pytest.mark.parametrize("check_before_write", [True, False])
@pytest.mark.parametrize("future_shapes", [True, False])
def test_init_like_hera_cal(
    hera_uvh5, tmp_path, set_obj_name, projected, check_before_write, future_shapes
):
    """Pulled from an error in hera_cal."""

    if not future_shapes:
        hera_uvh5.use_current_array_shapes()

    params = [
        "Nants_data",
        "Nants_telescope",
        "Nbls",
        "Nblts",
        "Nfreqs",
        "Npols",
        "Nspws",
        "Ntimes",
        "ant_1_array",
        "ant_2_array",
        "antenna_names",
        "antenna_numbers",
        "baseline_array",
        "channel_width",
        "data_array",
        "flag_array",
        "freq_array",
        "history",
        "x_orientation",
        "instrument",
        "integration_time",
        "lst_array",
        "nsample_array",
        "object_name",
        "phase_type",
        "polarization_array",
        "spw_array",
        "telescope_location",
        "telescope_name",
        "time_array",
        "uvw_array",
        "vis_units",
        "antenna_positions",
    ]

    uvd = UVData()
    if future_shapes:
        uvd._set_future_array_shapes()
    else:
        hera_uvh5.use_current_array_shapes()

    param_dict = {}
    for par in params:
        if par in ["phase_type", "object_name"]:
            continue
        param_dict[par] = getattr(hera_uvh5, par)

    if projected:
        param_dict["phase_type"] = "phased"
    else:
        param_dict["phase_type"] = "drift"
    if set_obj_name:
        param_dict["object_name"] = hera_uvh5.phase_center_catalog[0]["cat_name"]

    uvd = UVData()
    if future_shapes:
        uvd._set_future_array_shapes()
    # set parameters in uvd
    for par in params:
        if par not in param_dict.keys():
            continue
        if par in old_phase_attrs:
            warn_type = DeprecationWarning
            msg = "The older phase attributes, including"
        else:
            warn_type = None
            msg = ""
        with uvtest.check_warnings(warn_type, match=msg):
            uvd.__setattr__(par, param_dict[par])

    assert uvd.phase_center_catalog is None

    if projected:
        with uvtest.check_warnings(
            DeprecationWarning, match=["The older phase attributes, including"] * 4
        ):
            uvd.phase_center_ra = 0.0
            uvd.phase_center_dec = 0.0
            uvd.phase_center_epoch = 2000.0
            uvd.phase_center_frame = "icrs"

        hera_uvh5.phase_center_catalog[0]["cat_type"] = "sidereal"
        hera_uvh5.phase_center_catalog[0]["cat_lon"] = 0.0
        hera_uvh5.phase_center_catalog[0]["cat_lat"] = 0.0
        hera_uvh5.phase_center_catalog[0]["cat_frame"] = "icrs"
        hera_uvh5.phase_center_catalog[0]["cat_epoch"] = 2000.0
        hera_uvh5._set_app_coords_helper()
        warn_type = [DeprecationWarning, DeprecationWarning, UserWarning]
        msg = [
            "The phase_center_catalog was not set and a complete set of old phase "
            "attributes (phase_type, phase_center_ra, phase_center_dec, "
            "phase_center_frame, phase_center_epoch, object_name)",
            "flex_spw_id_array is not set. It will be required starting in version 3.0",
            "The uvw_array does not match the expected values",
        ]

    else:
        warn_type = [DeprecationWarning]
        msg = [
            "The phase_center_catalog was not set and a complete set of old phase "
            "attributes (phase_type",
            "flex_spw_id_array is not set. It will be required starting in version 3.0",
        ]

    if projected and not set_obj_name:
        with pytest.raises(
            ValueError,
            match=re.escape(
                "The phase_center_catalog was not set and some old phase attributes "
                "(phase_type, phase_center_ra, phase_center_dec, phase_center_frame, "
                "phase_center_epoch) were detected, but not enough to define the phase "
                "status of the object, so they could not be converted "
            ),
        ):
            uvd.check()
        return

    uvd.antenna_diameters = hera_uvh5.antenna_diameters
    uvd.extra_keywords = hera_uvh5.extra_keywords

    if check_before_write:
        with uvtest.check_warnings(warn_type, match=msg):
            uvd.check()

        if projected:
            assert uvd.phase_center_catalog[0]["cat_type"] == "sidereal"
        elif not set_obj_name:
            assert uvd.phase_center_catalog[0]["cat_name"] == "unprojected"
            uvd.phase_center_catalog[0]["cat_name"] = hera_uvh5.phase_center_catalog[0][
                "cat_name"
            ]

        # check that setting an old phase attribute when the phase_center_catalog is
        # already set will issue the correct warning (and do nothing)
        with uvtest.check_warnings(
            DeprecationWarning,
            match="phase_center_catalog is already set, so object_name, which is an "
            "old phase attribute, cannot be set.",
        ):
            uvd.object_name = "foo"

        uvd.flex_spw_id_array = hera_uvh5.flex_spw_id_array
        assert uvd == hera_uvh5

    testfile = os.path.join(tmp_path, "outtest.uvh5")
    if check_before_write:
        uvd.write_uvh5(testfile)
    else:
        with uvtest.check_warnings(warn_type, match=msg):
            uvd.write_uvh5(testfile)

    uvd2 = UVData.from_file(testfile, use_future_array_shapes=future_shapes)

    if not check_before_write:
        if projected:
            assert uvd2.phase_center_catalog[0]["cat_type"] == "sidereal"
        elif not set_obj_name:
            assert uvd2.phase_center_catalog[0]["cat_name"] == "unprojected"
            uvd2.phase_center_catalog[0]["cat_name"] = hera_uvh5.phase_center_catalog[
                0
            ]["cat_name"]

    assert uvd2 == hera_uvh5


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_setting_time_axis_wrongly(casa_uvfits):
    casa_uvfits.time_axis_faster_than_bls = True
    with pytest.raises(ValueError, match="time_axis_faster_than_bls is True but"):
        casa_uvfits.check()

    casa_uvfits.reorder_blts(order="time", minor_order="baseline")
    casa_uvfits.blts_are_rectangular = True
    with pytest.raises(
        ValueError, match="time_axis_faster_than_bls is True but time_array"
    ):
        casa_uvfits.time_axis_faster_than_bls = True
        casa_uvfits.check()

    casa_uvfits.select(times=casa_uvfits.time_array[0])
    with pytest.raises(
        ValueError, match="time_axis_faster_than_bls is True but Ntimes is 1"
    ):
        casa_uvfits.time_axis_faster_than_bls = True
        casa_uvfits.check()

    casa_uvfits.reorder_blts(order="baseline", minor_order="time")
    assert not casa_uvfits.time_axis_faster_than_bls
    casa_uvfits.blts_are_rectangular = True
    assert not casa_uvfits.time_axis_faster_than_bls


def test_set_rectangularity(casa_uvfits, hera_uvh5):
    # without setting force=True, starting from unknown rectangularity, it does nothing.
    casa_uvfits.set_rectangularity()
    assert casa_uvfits.blts_are_rectangular is None
    assert casa_uvfits.time_axis_faster_than_bls is None

    # setting force=True will set the rectangularity attributes only if obvious
    casa_uvfits.set_rectangularity(force=True)
    assert casa_uvfits.blts_are_rectangular is False
    assert casa_uvfits.time_axis_faster_than_bls is False

    hera_uvh5.reorder_blts(order="time", minor_order="baseline")
    hera_uvh5.set_rectangularity(force=True)
    assert hera_uvh5.blts_are_rectangular is True
    assert hera_uvh5.time_axis_faster_than_bls is False

    hera_uvh5.reorder_blts(order=np.random.permutation(hera_uvh5.Nblts))
    hera_uvh5.set_rectangularity(force=True)
    assert hera_uvh5.blts_are_rectangular is False
    assert hera_uvh5.time_axis_faster_than_bls is False


def test_select_catalog_name_errs(hera_uvh5):
    with pytest.raises(
        ValueError, match="Cannot set both phase_center_ids and catalog_names."
    ):
        hera_uvh5.select(phase_center_ids=[1, 2, 3], catalog_names=[1, 2, 3])


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_catalog_name(carma_miriad):
    # Select out the source
    for cat_id, cat_dict in carma_miriad.phase_center_catalog.items():
        uv_name = carma_miriad.select(catalog_names=cat_dict["cat_name"], inplace=False)
        uv_id = carma_miriad.select(phase_center_ids=cat_id, inplace=False)

        assert uv_id.history != uv_name.history
        uv_id.history = uv_name.history = None

        assert uv_name == uv_id


# pyuvdata/uvdata/tests/test_uvfits.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for UVFITS object.

"""
import os

import erfa
import numpy as np
import pytest
from astropy.io import fits

import pyuvdata.tests as uvtest
import pyuvdata.utils as uvutils
from pyuvdata import UVData
from pyuvdata.data import DATA_PATH
from pyuvdata.uvdata.uvdata import _future_array_shapes_warning

casa_tutorial_uvfits = os.path.join(
    DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits"
)

paper_uvfits = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAAM.uvfits")


def _fix_uvfits_multi_group_params(vis_hdu):
    par_names = vis_hdu.data.parnames
    group_parameter_list = []

    multi_params = {}
    for name in par_names:
        wh_name = np.nonzero(np.asarray(par_names) == name)[0]
        if len(wh_name) > 1:
            assert len(wh_name) == 2
            multi_params[name] = {"ind": 0}

    for index, name in enumerate(par_names):
        par_value = vis_hdu.data.par(name)

        if name in multi_params.keys():
            # these params need to be split in 2 parts to get high enough accuracy
            # (e.g. time and lst arrays)
            if multi_params[name]["ind"] == 0:
                multi_params[name]["arr1"] = np.float32(par_value)
                multi_params[name]["arr2"] = np.float32(
                    par_value - np.float64(multi_params[name]["arr1"])
                )
                par_value = multi_params[name]["arr1"]
                multi_params[name]["ind"] += 1
            else:
                par_value = multi_params[name]["arr2"]

        # need to account for PSCAL and PZERO values
        group_parameter_list.append(
            par_value / np.float64(vis_hdu.header["PSCAL" + str(index + 1)])
            - vis_hdu.header["PZERO" + str(index + 1)]
        )
    return group_parameter_list


def _make_multi_phase_center(uvobj, init_phase_dict, frame_list):
    init_ra = init_phase_dict["cat_lon"]
    init_dec = init_phase_dict["cat_lat"]
    init_epoch = init_phase_dict["cat_epoch"]
    init_name = init_phase_dict["cat_name"]

    nphase = len(frame_list)
    for frame_ind, frame_use in enumerate(frame_list):
        phase_mask = np.full(uvobj.Nblts, False)
        mask_start = frame_ind * uvobj.Nblts // nphase
        if frame_ind == nphase - 1:
            mask_end = uvobj.Nblts
        else:
            mask_end = (frame_ind + 1) * uvobj.Nblts // nphase
        phase_mask[mask_start:mask_end] = True
        if frame_use == "icrs":
            epoch = None
        elif frame_use == "fk5":
            if frame_ind == 0:
                epoch = init_epoch
            else:
                epoch = init_epoch + 5
        elif frame_use == "fk4":
            epoch = 1950
        if frame_ind == 0:
            cat_name = init_name
        else:
            cat_name = "test_" + str(frame_ind)
        uvobj.phase(
            ra=init_ra + 0.05 * (frame_ind + 1),
            dec=init_dec + 0.05 * (frame_ind + 1),
            phase_frame=frame_use,
            epoch=epoch,
            cat_name=cat_name,
            cat_type="sidereal",
            select_mask=phase_mask,
        )
    assert uvobj.Nphase == nphase


@pytest.fixture(scope="session")
def uvfits_nospw_main():
    uv_in = UVData()
    # This file has a crazy epoch (2291.34057617) which breaks the uvw_antpos check
    # Since it's a PAPER file, I think this is a bug in the file, not in the check.
    uv_in.read(
        paper_uvfits, run_check_acceptability=False, use_future_array_shapes=True
    )

    return uv_in


@pytest.fixture(scope="function")
def uvfits_nospw(uvfits_nospw_main):
    return uvfits_nospw_main.copy()


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_read_nrao(casa_uvfits):
    """Test reading in a CASA tutorial uvfits file."""
    uvobj = casa_uvfits
    expected_extra_keywords = ["OBSERVER", "SORTORD", "SPECSYS", "RESTFREQ", "ORIGIN"]
    assert expected_extra_keywords.sort() == list(uvobj.extra_keywords.keys()).sort()

    # test reading metadata only
    uvobj2 = UVData()
    uvobj2.read(casa_tutorial_uvfits, read_data=False, use_future_array_shapes=True)

    assert expected_extra_keywords.sort() == list(uvobj2.extra_keywords.keys()).sort()
    assert uvobj2.check()

    uvobj3 = uvobj.copy(metadata_only=True)
    assert uvobj2 == uvobj3


@pytest.mark.filterwarnings("ignore:Fixing auto-correlations to be be real-only,")
@pytest.mark.filterwarnings("ignore:ITRF coordinate frame detected")
@pytest.mark.filterwarnings("ignore:Telescope OVRO_MMA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_time_precision(tmp_path):
    """
    This tests that the times are round-tripped through write/read uvfits to sufficient
    precision.
    """
    pytest.importorskip("casacore")
    lwa_file = os.path.join(
        DATA_PATH, "2018-03-21-01_26_33_0004384620257280_000000_downselected.ms"
    )
    uvd = UVData()
    uvd.read(lwa_file, use_future_array_shapes=True)

    testfile = os.path.join(tmp_path, "lwa_testfile.uvfits")
    uvd.write_uvfits(testfile)

    uvd2 = UVData()
    uvd2.read(testfile, use_future_array_shapes=True)

    latitude, longitude, altitude = uvd2.telescope_location_lat_lon_alt_degrees
    unique_times, inverse_inds = np.unique(uvd2.time_array, return_inverse=True)
    unique_lst_array = uvutils.get_lst_for_time(
        unique_times, latitude, longitude, altitude
    )

    calc_lst_array = unique_lst_array[inverse_inds]

    assert np.allclose(
        calc_lst_array,
        uvd2.lst_array,
        rtol=uvd2._lst_array.tols[0],
        atol=uvd2._lst_array.tols[1],
    )

    # The incoming ra is specified as negative, it gets 2pi added to it in the roundtrip
    uvd2.phase_center_catalog[1]["cat_lon"] -= 2 * np.pi

    uvd2._consolidate_phase_center_catalogs(reference_catalog=uvd.phase_center_catalog)
    assert uvd2.__eq__(
        uvd,
        allowed_failures=[
            "filename",
            "scan_number_array",
            "dut1",
            "earth_omega",
            "gst0",
            "rdate",
            "timesys",
        ],
    )


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_break_read_uvfits():
    """Test errors on reading in a uvfits file with subarrays and other problems."""
    uvobj = UVData()
    multi_subarray_file = os.path.join(DATA_PATH, "multi_subarray.uvfits")
    with pytest.raises(ValueError, match="This file appears to have multiple subarray"):
        uvobj.read(multi_subarray_file)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:UVFITS file is missing AIPS SU table")
def test_source_group_params(casa_uvfits, tmp_path):
    # make a file with a single source to test that it works
    uv_in = casa_uvfits
    write_file = os.path.join(tmp_path, "outtest_casa.uvfits")
    write_file2 = os.path.join(tmp_path, "outtest_casa2.uvfits")
    uv_in.write_uvfits(write_file)

    with fits.open(write_file, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()
        raw_data_array = vis_hdu.data.data
        par_names = vis_hdu.data.parnames

        group_parameter_list = _fix_uvfits_multi_group_params(vis_hdu)

        par_names.append("SOURCE")
        source_array = np.ones_like(vis_hdu.data.par("BASELINE"))
        group_parameter_list.append(source_array)

        vis_hdu = fits.GroupData(
            raw_data_array, parnames=par_names, pardata=group_parameter_list, bitpix=-32
        )
        vis_hdu = fits.GroupsHDU(vis_hdu)
        vis_hdu.header = vis_hdr
        ant_hdu = hdu_list[hdunames["AIPS AN"]]

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu])
        hdulist.writeto(write_file2, overwrite=True)
        hdulist.close()

    uv_out = UVData()
    uv_out.read(write_file2, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_casa2.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.filterwarnings("ignore:The entry name")
@pytest.mark.filterwarnings("ignore:The provided name")
@pytest.mark.parametrize("frame", [["icrs"], ["fk5"], ["fk4"], ["fk5", "icrs"]])
@pytest.mark.parametrize("high_precision", [True, False])
def test_read_write_multi_source(casa_uvfits, tmp_path, frame, high_precision):
    uv_in = casa_uvfits

    # generate a multi source object by phasing it in multiple directions
    init_phase_dict = uv_in.phase_center_catalog[0]
    init_frame = init_phase_dict["cat_frame"]
    frame_list = [init_frame] + frame

    _make_multi_phase_center(uv_in, init_phase_dict, frame_list)

    if high_precision:
        # Increase the precision of the data_array because the uvw_array precision is
        # tied to the data_array precision
        uv_in.data_array = uv_in.data_array.astype(np.complex128)

    write_file = os.path.join(tmp_path, "outtest_multisource.uvfits")
    uv_in.write_uvfits(write_file)
    uv_out = UVData.from_file(write_file, use_future_array_shapes=True)

    if frame == "fk5":
        # objects should just be equal because all the phase centers had the same frames
        uv_out._consolidate_phase_center_catalogs(
            reference_catalog=uv_in.phase_center_catalog
        )
        assert uv_in == uv_out

    if not high_precision:
        # replace the uvw_array because of loss of precision roundtripping through
        # uvfits
        uv_out.uvw_array = uv_in.uvw_array

    # Now rephase to the same places as the initial object was phased to. Note that if
    # frame == ["fk5"] this should not change anything (but it does if uvws lose
    # precision and they aren't fixed before here)
    _make_multi_phase_center(uv_out, init_phase_dict, frame_list)

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )

    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The older phase attributes")
@pytest.mark.parametrize("frame", ["icrs", "fk4"])
@pytest.mark.filterwarnings("ignore:UVFITS file is missing AIPS SU table")
def test_source_frame_defaults(casa_uvfits, tmp_path, frame):
    # make a file with a single source to test that it works
    uv_in = casa_uvfits
    write_file = os.path.join(tmp_path, "outtest_casa.uvfits")
    write_file2 = os.path.join(tmp_path, "outtest_casa2.uvfits")
    uv_in.write_uvfits(write_file)

    with fits.open(write_file, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()
        raw_data_array = vis_hdu.data.data
        par_names = vis_hdu.data.parnames

        group_parameter_list = _fix_uvfits_multi_group_params(vis_hdu)

        vis_hdu = fits.GroupData(
            raw_data_array, parnames=par_names, pardata=group_parameter_list, bitpix=-32
        )
        vis_hdu = fits.GroupsHDU(vis_hdu)
        vis_hdu.header = vis_hdr
        del vis_hdu.header["RADESYS"]
        if frame == "icrs":
            del vis_hdu.header["EPOCH"]
        elif frame == "fk4":
            vis_hdu.header["EPOCH"] = 1950.0
        ant_hdu = hdu_list[hdunames["AIPS AN"]]

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu])
        hdulist.writeto(write_file2, overwrite=True)
        hdulist.close()

    uv_out = UVData()
    uv_out.read(write_file2, use_future_array_shapes=True)
    assert uv_out.phase_center_catalog[0]["cat_frame"] == frame


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The entry name")
@pytest.mark.filterwarnings("ignore:The provided name")
@pytest.mark.parametrize("frame_list", [["fk5", "fk5"], ["fk4", "fk4"], ["fk4", "fk5"]])
def test_multi_source_frame_defaults(casa_uvfits, tmp_path, frame_list):
    # make a file with a single source to test that it works
    uv_in = casa_uvfits
    write_file = os.path.join(tmp_path, "outtest_casa.uvfits")
    write_file2 = os.path.join(tmp_path, "outtest_casa2.uvfits")

    init_phase_dict = uv_in.phase_center_catalog[0]
    _make_multi_phase_center(uv_in, init_phase_dict, frame_list)
    uv_in._clear_unused_phase_centers()

    in_frame_list = []
    for phase_dict in uv_in.phase_center_catalog.values():
        in_frame_list.append(phase_dict["cat_frame"])
    assert in_frame_list == frame_list

    uv_in.write_uvfits(write_file)

    with fits.open(write_file, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()
        raw_data_array = vis_hdu.data.data
        par_names = vis_hdu.data.parnames

        group_parameter_list = _fix_uvfits_multi_group_params(vis_hdu)

        vis_hdu = fits.GroupData(
            raw_data_array, parnames=par_names, pardata=group_parameter_list, bitpix=-32
        )
        vis_hdu = fits.GroupsHDU(vis_hdu)
        vis_hdu.header = vis_hdr
        del vis_hdu.header["RADESYS"]

        ant_hdu = hdu_list[hdunames["AIPS AN"]]
        su_hdu = hdu_list[hdunames["AIPS SU"]]

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu, su_hdu])
        hdulist.writeto(write_file2, overwrite=True)
        hdulist.close()

    uv_out = UVData()
    uv_out.read(write_file2, use_future_array_shapes=True)

    out_frame_list = []
    for phase_dict in uv_out.phase_center_catalog.values():
        out_frame_list.append(phase_dict["cat_frame"])
    assert out_frame_list == frame_list

    if frame_list[0] == frame_list[1]:
        # don't check this for different frames because we cannot write different frames
        # so they won't actually match
        uv_out._consolidate_phase_center_catalogs(
            reference_catalog=uv_in.phase_center_catalog
        )

        assert uv_out == uv_in


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_missing_aips_su_table(casa_uvfits, tmp_path):
    # make a file with multiple sources to test error condition
    uv_in = casa_uvfits
    write_file = str(tmp_path / "outtest_casa.uvfits")
    write_file2 = str(tmp_path / "outtest_casa2.uvfits")
    uv_in.write_uvfits(write_file)

    with fits.open(write_file, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()
        raw_data_array = vis_hdu.data.data

        par_names = vis_hdu.data.parnames
        group_parameter_list = _fix_uvfits_multi_group_params(vis_hdu)

        par_names.append("SOURCE")
        source_array = np.ones_like(vis_hdu.data.par("BASELINE"))
        mid_index = source_array.shape[0] // 2
        source_array[mid_index:] = source_array[mid_index:] * 2
        group_parameter_list.append(source_array)

        vis_hdu = fits.GroupData(
            raw_data_array, parnames=par_names, pardata=group_parameter_list, bitpix=-32
        )
        vis_hdu = fits.GroupsHDU(vis_hdu)
        vis_hdu.header = vis_hdr
        ant_hdu = hdu_list[hdunames["AIPS AN"]]

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu])
        hdulist.writeto(write_file2, overwrite=True)
        hdulist.close()

    with uvtest.check_warnings(
        [UserWarning] * 3 + [DeprecationWarning],
        [
            "UVFITS file is missing AIPS SU table, which is required when ",
            "Telescope EVLA is not",
            "The uvw_array does not match the expected values",
            _future_array_shapes_warning,
        ],
    ):
        uv_in.read(write_file2)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_multispw_supported():
    """Test reading in a uvfits file with multiple spws."""
    uvobj = UVData()
    testfile = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1scan.uvfits")
    uvobj.read(testfile, use_future_array_shapes=True)

    # We know this file has two spws
    assert uvobj.Nspws == 2

    # Verify that the data array has the right shape
    assert np.size(uvobj.data_array, axis=1) == uvobj.Nfreqs

    # Verify that the freq array has the right shape
    assert np.size(uvobj.freq_array) == uvobj.Nfreqs

    # Verift thaat the spw_array is the right length
    assert len(uvobj.spw_array) == uvobj.Nspws


def test_casa_nonascii_bytes_antenna_names():
    """Test that nonascii bytes in antenna names are handled properly."""
    uv1 = UVData()
    testfile = os.path.join(DATA_PATH, "corrected2_zen.2458106.28114.ant012.HH.uvfits")
    # this file has issues with the telescope location so turn checking off
    with uvtest.check_warnings(
        UserWarning, "Telescope mock-HERA is not in known_telescopes."
    ):
        uv1.read(testfile, run_check=False, use_future_array_shapes=True)
    # fmt: off
    expected_ant_names = [
        'HH0', 'HH1', 'HH2', 'H2', 'H2', 'H2', 'H2', 'H2', 'H2', 'H2',
        'H2', 'HH11', 'HH12', 'HH13', 'HH14', 'H14', 'H14', 'H14', 'H14',
        'H14', 'H14', 'H14', 'H14', 'HH23', 'HH24', 'HH25', 'HH26', 'HH27',
        'H27', 'H27', 'H27', 'H27', 'H27', 'H27', 'H27', 'H27', 'HH36',
        'HH37', 'HH38', 'HH39', 'HH40', 'HH41', 'H41', 'H41', 'H41', 'H41',
        'H41', 'H41', 'H41', 'H41', 'HH50', 'HH51', 'HH52', 'HH53', 'HH54',
        'HH55', 'H55', 'H55', 'H55', 'H55', 'H55', 'H55', 'H55', 'H55',
        'H55', 'HH65', 'HH66', 'HH67', 'HH68', 'HH69', 'HH70', 'HH71',
        'H71', 'H71', 'H71', 'H71', 'H71', 'H71', 'H71', 'H71', 'H71',
        'H71', 'HH82', 'HH83', 'HH84', 'HH85', 'HH86', 'HH87', 'HH88',
        'H88', 'H88', 'H88', 'H88', 'H88', 'H88', 'H88', 'H88', 'H88',
        'HH98', 'H98', 'H98', 'H98', 'H98', 'H98', 'H98', 'H98', 'H98',
        'H98', 'H98', 'H98', 'H98', 'H98', 'H98', 'H98', 'H98', 'H98',
        'H98', 'H98', 'H98', 'H98', 'HH120', 'HH121', 'HH122', 'HH123',
        'HH124', 'H124', 'H124', 'H124', 'H124', 'H124', 'H124', 'H124',
        'H124', 'H124', 'H124', 'H124', 'HH136', 'HH137', 'HH138', 'HH139',
        'HH140', 'HH141', 'HH142', 'HH143']
    # fmt: on
    assert uv1.antenna_names == expected_ant_names


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_readwriteread(tmp_path, casa_uvfits, future_shapes):
    """
    CASA tutorial uvfits loopback test.

    Read in uvfits file, write out new uvfits file, read back in and check for
    object equality.
    """
    uv_in = casa_uvfits

    if not future_shapes:
        uv_in.use_current_array_shapes()

    uv_out = UVData()
    write_file = str(tmp_path / "outtest_casa.uvfits")

    uv_in.write_uvfits(write_file)
    uv_out.read(write_file, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_casa.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.parametrize("uvw_suffix", ["---SIN", "---NCP"])
def test_uvw_coordinate_suffixes(casa_uvfits, tmp_path, uvw_suffix):
    uv_in = casa_uvfits
    write_file = str(tmp_path / "outtest_casa.uvfits")
    write_file2 = str(tmp_path / "outtest_casa2.uvfits")
    uv_in.write_uvfits(write_file)

    with fits.open(write_file, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()
        raw_data_array = vis_hdu.data.data
        par_names = vis_hdu.data.parnames

        group_parameter_list = _fix_uvfits_multi_group_params(vis_hdu)

        for name in ["UU", "VV", "WW"]:
            name_locs = np.nonzero(np.array(par_names) == name)[0]
            for index in name_locs:
                par_names[index] = name + uvw_suffix
        vis_hdu = fits.GroupData(
            raw_data_array, parnames=par_names, pardata=group_parameter_list, bitpix=-32
        )
        vis_hdu = fits.GroupsHDU(vis_hdu)
        vis_hdu.header = vis_hdr
        ant_hdu = hdu_list[hdunames["AIPS AN"]]
        source_hdu = hdu_list[hdunames["AIPS SU"]]

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu, source_hdu])
        hdulist.writeto(write_file2, overwrite=True)
        hdulist.close()

    if uvw_suffix == "---NCP":
        with uvtest.check_warnings(
            UserWarning,
            match=[
                "Telescope EVLA is not in known_telescopes.",
                "The baseline coordinates (uvws) in this file are specified in the "
                "---NCP coordinate system",
                "The uvw_array does not match the expected values",
            ],
        ):
            uv2 = UVData.from_file(write_file2, use_future_array_shapes=True)
        uv2.uvw_array = uvutils._rotate_one_axis(
            uv2.uvw_array[:, :, None], -1 * (uv2.phase_center_app_dec - np.pi / 2), 0
        )[:, :, 0]
    else:
        uv2 = UVData.from_file(write_file2, use_future_array_shapes=True)

    uv2._consolidate_phase_center_catalogs(reference_catalog=uv_in.phase_center_catalog)
    assert uv2 == uv_in


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.parametrize(
    "uvw_suffix", [["---SIN", "", ""], ["", "---NCP", ""], ["", "---NCP", "---SIN"]]
)
def test_uvw_coordinate_suffix_errors(casa_uvfits, tmp_path, uvw_suffix):
    uv_in = casa_uvfits
    write_file = str(tmp_path / "outtest_casa.uvfits")
    write_file2 = str(tmp_path / "outtest_casa2.uvfits")
    uv_in.write_uvfits(write_file)

    with fits.open(write_file, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()
        raw_data_array = vis_hdu.data.data
        par_names = vis_hdu.data.parnames

        group_parameter_list = _fix_uvfits_multi_group_params(vis_hdu)

        for ind, name in enumerate(["UU", "VV", "WW"]):
            name_locs = np.nonzero(np.array(par_names) == name)[0]
            for index in name_locs:
                par_names[index] = name + uvw_suffix[ind]
        vis_hdu = fits.GroupData(
            raw_data_array, parnames=par_names, pardata=group_parameter_list, bitpix=-32
        )
        vis_hdu = fits.GroupsHDU(vis_hdu)
        vis_hdu.header = vis_hdr
        ant_hdu = hdu_list[hdunames["AIPS AN"]]
        source_hdu = hdu_list[hdunames["AIPS SU"]]

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu, source_hdu])
        hdulist.writeto(write_file2, overwrite=True)
        hdulist.close()

    with pytest.raises(
        ValueError,
        match="There is no consistent set of baseline coordinates in this file.",
    ):
        UVData.from_file(write_file2, use_future_array_shapes=True)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_readwriteread_no_lst(tmp_path, casa_uvfits):
    uv_in = casa_uvfits
    uv_out = UVData()
    write_file = str(tmp_path / "outtest_casa.uvfits")

    # test that it works with write_lst = False
    uv_in.write_uvfits(write_file, write_lst=False)
    uv_out.read(write_file, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_casa.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_readwriteread_x_orientation(tmp_path, casa_uvfits):
    uv_in = casa_uvfits
    uv_out = UVData()
    write_file = str(tmp_path / "outtest_casa.uvfits")

    # check that if x_orientation is set, it's read back out properly
    uv_in.x_orientation = "east"
    uv_in.write_uvfits(write_file)
    uv_out.read(write_file, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_casa.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_readwriteread_antenna_diameters(tmp_path, casa_uvfits):
    uv_in = casa_uvfits
    uv_out = UVData()
    write_file = str(tmp_path / "outtest_casa.uvfits")

    # check that if antenna_diameters is set, it's read back out properly
    uv_in.antenna_diameters = (
        np.zeros((uv_in.Nants_telescope,), dtype=np.float64) + 14.0
    )
    uv_in.write_uvfits(write_file)
    uv_out.read(write_file, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_casa.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_readwriteread_large_antnums(tmp_path, casa_uvfits):
    uv_in = casa_uvfits
    uv_out = UVData()
    write_file = str(tmp_path / "outtest_casa.uvfits")

    # check that if antenna_numbers are > 256 everything works
    uv_in.antenna_numbers = uv_in.antenna_numbers + 256
    uv_in.ant_1_array = uv_in.ant_1_array + 256
    uv_in.ant_2_array = uv_in.ant_2_array + 256
    uv_in.baseline_array = uv_in.antnums_to_baseline(
        uv_in.ant_1_array, uv_in.ant_2_array
    )
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions",
            "Found antenna numbers > 255 in this data set. This is permitted by "
            "UVFITS ",
            "antnums_to_baseline: found antenna numbers > 255, using 2048 baseline",
        ],
    ):
        uv_in.write_uvfits(write_file)
    uv_out.read(write_file, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_casa.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_readwriteread_missing_info(tmp_path, casa_uvfits):
    uv_in = casa_uvfits
    uv_out = UVData()
    write_file = str(tmp_path / "outtest_casa.uvfits")
    write_file2 = str(tmp_path / "outtest_casa2.uvfits")

    # check missing telescope_name, timesys vs timsys spelling, xyz_telescope_frame=????
    uv_in.write_uvfits(write_file)
    with fits.open(write_file, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()

        vis_hdr.pop("TELESCOP")

        vis_hdu.header = vis_hdr

        ant_hdu = hdu_list[hdunames["AIPS AN"]]
        ant_hdr = ant_hdu.header.copy()

        time_sys = ant_hdr.pop("TIMESYS")
        ant_hdr["TIMSYS"] = time_sys
        ant_hdr["FRAME"] = "????"

        ant_hdu.header = ant_hdr
        source_hdu = hdu_list[hdunames["AIPS SU"]]

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu, source_hdu])
        hdulist.writeto(write_file2, overwrite=True)

    uv_out.read(write_file2, use_future_array_shapes=True)
    assert uv_out.telescope_name == "EVLA"
    assert uv_out.timesys == time_sys

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_readwriteread_error_timesys(tmp_path, casa_uvfits):
    uv_in = casa_uvfits
    write_file = str(tmp_path / "outtest_casa.uvfits")

    # check error if timesys is 'IAT'
    uv_in.timesys = "IAT"
    with pytest.raises(ValueError) as cm:
        uv_in.write_uvfits(write_file)
    assert str(cm.value).startswith(
        "This file has a time system IAT. " 'Only "UTC" time system files are supported'
    )

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_readwriteread_error_single_time(tmp_path, casa_uvfits):
    uv_in = casa_uvfits
    uv_out = UVData()
    write_file = str(tmp_path / "outtest_casa.uvfits")
    write_file2 = str(tmp_path / "outtest_casa2.uvfits")

    # check error if one time & no inttime specified
    uv_singlet = uv_in.select(times=uv_in.time_array[0], inplace=False)
    uv_singlet.write_uvfits(write_file)

    with fits.open(write_file, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()
        raw_data_array = vis_hdu.data.data
        par_names = vis_hdu.data.parnames

        group_parameter_list = _fix_uvfits_multi_group_params(vis_hdu)

        inttime_ind = np.nonzero(np.asarray(par_names) == "INTTIM")[0]
        assert inttime_ind.size == 1
        inttime_ind = inttime_ind[0]
        del par_names[inttime_ind]
        del group_parameter_list[inttime_ind]

        vis_hdu = fits.GroupData(
            raw_data_array, parnames=par_names, pardata=group_parameter_list, bitpix=-32
        )
        vis_hdu = fits.GroupsHDU(vis_hdu)
        vis_hdu.header = vis_hdr

        ant_hdu = hdu_list[hdunames["AIPS AN"]]

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu])
        hdulist.writeto(write_file2, overwrite=True)

    with pytest.raises(
        ValueError, match="Required UVParameter _integration_time has not been set"
    ):
        with uvtest.check_warnings(
            [
                UserWarning,
                erfa.core.ErfaWarning,
                erfa.core.ErfaWarning,
                UserWarning,
                UserWarning,
            ],
            [
                "Telescope EVLA is not",
                "ERFA function 'utcut1' yielded 1 of 'dubious year (Note 3)'",
                "ERFA function 'utctai' yielded 1 of 'dubious year (Note 3)'",
                "LST values stored in this file are not self-consistent",
                "The integration time is not specified and only one time",
            ],
        ):
            uv_out.read(write_file2, use_future_array_shapes=True),

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_readwriteread_unflagged_data_warnings(tmp_path, casa_uvfits):
    uv_in = casa_uvfits
    write_file = str(tmp_path / "outtest_casa.uvfits")

    # check that unflagged data with nsample = 0 will cause warnings
    uv_in.nsample_array[list(range(11, 22))] = 0
    uv_in.flag_array[list(range(11, 22))] = False
    with uvtest.check_warnings(
        UserWarning,
        [
            "The uvw_array does not match the expected values given the antenna "
            "positions",
            "Some unflagged data has nsample = 0",
        ],
    ):
        uv_in.write_uvfits(write_file)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.parametrize(
    "kwd_name,kwd_value,warnstr,errstr",
    (
        [
            "testdict",
            {"testkey": 23},
            "testdict in extra_keywords is a list, array or dict",
            "Extra keyword testdict is of <class 'dict'>",
        ],
        [
            "testlist",
            [12, 14, 90],
            "testlist in extra_keywords is a list, array or dict",
            "Extra keyword testlist is of <class 'list'>",
        ],
        [
            "testarr",
            np.array([12, 14, 90]),
            "testarr in extra_keywords is a list, array or dict",
            "Extra keyword testarr is of <class 'numpy.ndarray'>",
        ],
        [
            "test_long_key",
            True,
            "key test_long_key in extra_keywords is longer than 8 characters",
            None,
        ],
    ),
)
def test_extra_keywords_errors(
    casa_uvfits, tmp_path, kwd_name, kwd_value, warnstr, errstr
):
    uv_in = casa_uvfits
    testfile = str(tmp_path / "outtest_casa.uvfits")

    uvw_warn_str = "The uvw_array does not match the expected values"
    # check for warnings & errors with extra_keywords that are dicts, lists or arrays
    uv_in.extra_keywords[kwd_name] = kwd_value
    if warnstr is None:
        warnstr_list = [uvw_warn_str]
    else:
        warnstr_list = [warnstr, uvw_warn_str]

    with uvtest.check_warnings(UserWarning, match=warnstr_list):
        uv_in.check()

    if errstr is not None:
        with pytest.raises(TypeError, match=errstr):
            uv_in.write_uvfits(testfile, run_check=False)
    else:
        with uvtest.check_warnings(UserWarning, match=warnstr):
            uv_in.write_uvfits(testfile, run_check=False)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.parametrize(
    "kwd_names,kwd_values",
    (
        [["bool", "bool2"], [True, False]],
        [["int1", "int2"], [np.int64(5), 7]],
        [["float1", "float2"], [np.int64(5.3), 6.9]],
        [["complex1", "complex2"], [np.complex64(5.3 + 1.2j), 6.9 + 4.6j]],
        [
            ["str", "comment"],
            [
                "hello",
                "this is a very long comment that will be broken into several "
                "lines\nif everything works properly.",
            ],
        ],
    ),
)
def test_extra_keywords(casa_uvfits, tmp_path, kwd_names, kwd_values):
    uv_in = casa_uvfits
    uv_out = UVData()
    testfile = str(tmp_path / "outtest_casa.uvfits")

    for name, value in zip(kwd_names, kwd_values):
        uv_in.extra_keywords[name] = value
    uv_in.write_uvfits(testfile)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_casa.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.parametrize("order", ["time", "bda"])
def test_roundtrip_blt_order(casa_uvfits, order, tmp_path):
    uv_in = casa_uvfits
    uv_out = UVData()
    write_file = str(tmp_path / "blt_order_test.uvfits")

    uv_in.reorder_blts(order)

    uv_in.write_uvfits(write_file)
    uv_out.read(write_file, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["blt_order_test.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "select_kwargs",
    [
        {"antenna_nums": np.array([1, 20, 12, 25, 4, 24, 2, 21, 22])},
        {"freq_chans": np.arange(12, 22)},
        {"freq_chans": [0]},
        {"polarizations": [-1, -2]},
        {"time_inds": np.array([0, 1])},
        {"lst_inds": np.array([0, 1])},
        {
            "antenna_nums": np.array([1, 20, 12, 25, 4, 24, 2, 21, 22]),
            "freq_chans": np.arange(12, 22),
            "polarizations": [-1, -2],
        },
        {
            "antenna_nums": np.array([1, 2]),
            "freq_chans": np.arange(12, 22),
            "polarizations": [-1, -2],
        },
        {
            "antenna_nums": np.array([1, 2, 3, 4, 7, 8, 9, 12, 15, 19, 20, 21, 22, 23]),
            "freq_chans": np.arange(12, 64),
            "polarizations": [-1, -2],
        },
    ],
)
def test_select_read(casa_uvfits, tmp_path, select_kwargs):
    uvfits_uv = UVData()
    uvfits_uv2 = UVData()

    uvfits_uv2 = casa_uvfits
    if "time_inds" in select_kwargs.keys():
        time_inds = select_kwargs.pop("time_inds")
        unique_times = np.unique(uvfits_uv2.time_array)
        select_kwargs["time_range"] = unique_times[time_inds]

    if "lst_inds" in select_kwargs.keys():
        lst_inds = select_kwargs.pop("lst_inds")
        unique_lsts = np.unique(uvfits_uv2.lst_array)
        select_kwargs["lst_range"] = unique_lsts[lst_inds]

    uvfits_uv.read(casa_tutorial_uvfits, use_future_array_shapes=True, **select_kwargs)
    uvfits_uv2.select(**select_kwargs)
    assert uvfits_uv == uvfits_uv2

    testfile = str(tmp_path / "outtest_casa.uvfits")
    uvfits_uv.write_uvfits(testfile)
    uvfits_uv2.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uvfits_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uvfits_uv2.filename == ["outtest_casa.uvfits"]
    uvfits_uv.filename = uvfits_uv2.filename

    uvfits_uv2._consolidate_phase_center_catalogs(
        reference_catalog=uvfits_uv.phase_center_catalog
    )
    assert uvfits_uv == uvfits_uv2


@pytest.mark.filterwarnings("ignore:Required Antenna keyword 'FRAME'")
@pytest.mark.filterwarnings("ignore:telescope_location is not set")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize(
    "select_kwargs",
    [{"antenna_nums": np.array([2, 4, 5])}, {"freq_chans": np.arange(4, 8)}],
)
def test_select_read_nospw(uvfits_nospw, tmp_path, select_kwargs):
    uvfits_uv2 = uvfits_nospw

    uvfits_uv = UVData()
    # This file has a crazy epoch (2291.34057617) which breaks the uvw_antpos check
    # Since it's a PAPER file, I think this is a bug in the file, not in the check.
    uvfits_uv.read(
        paper_uvfits,
        run_check_acceptability=False,
        use_future_array_shapes=True,
        **select_kwargs
    )

    uvfits_uv2.select(run_check_acceptability=False, **select_kwargs)
    assert uvfits_uv == uvfits_uv2


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_select_read_nospw_pol(casa_uvfits, tmp_path):
    # this requires writing a new file because the no spw file we have has only 1 pol

    with fits.open(casa_tutorial_uvfits, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        vis_hdr = vis_hdu.header.copy()
        raw_data_array = vis_hdu.data.data
        raw_data_array = raw_data_array[:, :, :, 0, :, :, :]

        vis_hdr["NAXIS"] = 6

        vis_hdr["NAXIS5"] = vis_hdr["NAXIS6"]
        vis_hdr["CTYPE5"] = vis_hdr["CTYPE6"]
        vis_hdr["CRVAL5"] = vis_hdr["CRVAL6"]
        vis_hdr["CDELT5"] = vis_hdr["CDELT6"]
        vis_hdr["CRPIX5"] = vis_hdr["CRPIX6"]
        vis_hdr["CROTA5"] = vis_hdr["CROTA6"]

        vis_hdr["NAXIS6"] = vis_hdr["NAXIS7"]
        vis_hdr["CTYPE6"] = vis_hdr["CTYPE7"]
        vis_hdr["CRVAL6"] = vis_hdr["CRVAL7"]
        vis_hdr["CDELT6"] = vis_hdr["CDELT7"]
        vis_hdr["CRPIX6"] = vis_hdr["CRPIX7"]
        vis_hdr["CROTA6"] = vis_hdr["CROTA7"]

        vis_hdr.pop("NAXIS7")
        vis_hdr.pop("CTYPE7")
        vis_hdr.pop("CRVAL7")
        vis_hdr.pop("CDELT7")
        vis_hdr.pop("CRPIX7")
        vis_hdr.pop("CROTA7")

        par_names = vis_hdu.data.parnames

        group_parameter_list = _fix_uvfits_multi_group_params(vis_hdu)

        vis_hdu = fits.GroupData(
            raw_data_array, parnames=par_names, pardata=group_parameter_list, bitpix=-32
        )
        vis_hdu = fits.GroupsHDU(vis_hdu)
        vis_hdu.header = vis_hdr

        ant_hdu = hdu_list[hdunames["AIPS AN"]]

        write_file = str(tmp_path / "outtest_casa.uvfits")
        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu])
        hdulist.writeto(write_file, overwrite=True)

    pols_to_keep = [-1, -2]
    uvfits_uv = UVData()
    uvfits_uv.read(write_file, polarizations=pols_to_keep, use_future_array_shapes=True)
    uvfits_uv2 = casa_uvfits
    uvfits_uv2.select(polarizations=pols_to_keep)

    # make sure filenames are what we expect
    assert uvfits_uv.filename == ["outtest_casa.uvfits"]
    assert uvfits_uv2.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uvfits_uv.filename = uvfits_uv2.filename

    assert uvfits_uv == uvfits_uv2


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_read_uvfits_write_miriad(casa_uvfits, tmp_path):
    """
    read uvfits, write miriad test.
    Read in uvfits file, write out as miriad, read back in and check for
    object equality.
    """
    pytest.importorskip("pyuvdata._miriad")
    uvfits_uv = casa_uvfits
    miriad_uv = UVData()
    testfile = str(tmp_path / "outtest_miriad")
    uvfits_uv.write_miriad(testfile, clobber=True)
    miriad_uv.read_miriad(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert miriad_uv.filename == ["outtest_miriad"]
    assert uvfits_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    miriad_uv.filename = uvfits_uv.filename

    miriad_uv._consolidate_phase_center_catalogs(
        reference_catalog=uvfits_uv.phase_center_catalog
    )
    assert miriad_uv == uvfits_uv

    # check that setting the projected keyword also works
    miriad_uv.read_miriad(testfile, projected=True, use_future_array_shapes=True)

    # check that setting the projected False raises an error
    with pytest.raises(
        ValueError, match="projected is False but the RA values are constant."
    ):
        miriad_uv.read_miriad(testfile, projected=False, use_future_array_shapes=True)

    # check that setting it works after selecting a single time
    uvfits_uv.select(times=uvfits_uv.time_array[0])
    uvfits_uv.write_miriad(testfile, clobber=True)
    miriad_uv.read_miriad(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert miriad_uv.filename == ["outtest_miriad"]
    assert uvfits_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    miriad_uv.filename = uvfits_uv.filename

    miriad_uv._consolidate_phase_center_catalogs(
        reference_catalog=uvfits_uv.phase_center_catalog
    )
    assert miriad_uv == uvfits_uv


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_multi_files(casa_uvfits, tmp_path):
    """
    Reading multiple files at once.
    """
    uv_full = casa_uvfits
    testfile1 = str(tmp_path / "uv1.uvfits")
    testfile2 = str(tmp_path / "uv2.uvfits")
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_uvfits(testfile1)
    uv2.write_uvfits(testfile2)
    uv1.read(
        np.array([testfile1, testfile2]),
        file_type="uvfits",
        allow_rephase=False,
        use_future_array_shapes=True,
    )

    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis "
        "using pyuvdata.",
        uv1.history,
    )

    uv1.history = uv_full.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1.uvfits", "uv2.uvfits"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_full.filename
    uv1._filename.form = (1,)

    uv1._consolidate_phase_center_catalogs(
        reference_catalog=uv_full.phase_center_catalog
    )
    assert uv1 == uv_full


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_multi_files_axis(casa_uvfits, tmp_path):
    """
    Reading multiple files at once using "axis" keyword.
    """
    uv_full = casa_uvfits
    testfile1 = str(tmp_path / "uv1.uvfits")
    testfile2 = str(tmp_path / "uv2.uvfits")
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_uvfits(testfile1)
    uv2.write_uvfits(testfile2)

    uv1.read(
        [testfile1, testfile2],
        axis="freq",
        allow_rephase=False,
        use_future_array_shapes=True,
    )
    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis "
        "using pyuvdata.",
        uv1.history,
    )

    uv1.history = uv_full.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1.uvfits", "uv2.uvfits"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_full.filename
    uv1._filename.form = (1,)

    uv1._consolidate_phase_center_catalogs(
        reference_catalog=uv_full.phase_center_catalog
    )
    assert uv1 == uv_full


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_multi_files_metadata_only(casa_uvfits, tmp_path):
    """
    Reading multiple files at once with metadata only.
    """
    uv_full = casa_uvfits
    testfile1 = str(tmp_path / "uv1.uvfits")
    testfile2 = str(tmp_path / "uv2.uvfits")
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_uvfits(testfile1)
    uv2.write_uvfits(testfile2)

    # check with metadata_only
    uv_full = uv_full.copy(metadata_only=True)
    uv1 = UVData()
    uv1.read(
        [testfile1, testfile2],
        read_data=False,
        allow_rephase=False,
        use_future_array_shapes=True,
    )

    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis "
        "using pyuvdata.",
        uv1.history,
    )

    uv1.history = uv_full.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1.uvfits", "uv2.uvfits"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_full.filename
    uv1._filename.form = (1,)

    uv1._consolidate_phase_center_catalogs(
        reference_catalog=uv_full.phase_center_catalog
    )
    assert uv1 == uv_full


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
def test_multi_unphase_on_read(casa_uvfits, tmp_path):
    uv_full = casa_uvfits
    uv_full2 = UVData()
    testfile1 = str(tmp_path / "uv1.uvfits")
    testfile2 = str(tmp_path / "uv2.uvfits")
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_uvfits(testfile1)
    uv2.write_uvfits(testfile2)
    with uvtest.check_warnings(
        [UserWarning] * 6 + [DeprecationWarning] * 2,
        ["Telescope EVLA is not"] * 2
        + [
            "The uvw_array does not match the expected values given the "
            "antenna positions."
        ]
        * 2
        + ["Unprojecting this UVData object."] * 2
        + ["The `unphase_to_drift` option is deprecated"] * 2,
    ):
        uv1.read(
            np.array([testfile1, testfile2]),
            unphase_to_drift=True,
            use_future_array_shapes=True,
        )

    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis "
        "using pyuvdata.",
        uv1.history,
    )

    uv_full.unproject_phase()

    uv1.history = uv_full.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1.uvfits", "uv2.uvfits"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_full.filename
    uv1._filename.form = (1,)

    uv1._consolidate_phase_center_catalogs(
        reference_catalog=uv_full.phase_center_catalog
    )
    assert uv1 == uv_full

    # check unphasing when reading only one file
    with uvtest.check_warnings(
        [UserWarning] * 3 + [DeprecationWarning],
        [
            "Telescope EVLA is not",
            "The uvw_array does not match the expected values given the "
            "antenna positions.",
            "Unprojecting this UVData object.",
            "The `unphase_to_drift` option is deprecated",
        ],
    ):
        uv_full2.read(
            casa_tutorial_uvfits, unphase_to_drift=True, use_future_array_shapes=True
        )
    assert uv_full2 == uv_full


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.filterwarnings("ignore:The xyz array in ENU_from_ECEF")
@pytest.mark.filterwarnings("ignore:The enu array in ECEF_from_ENU")
@pytest.mark.filterwarnings("ignore:The older phase attributes")
def test_multi_phase_on_read(casa_uvfits, tmp_path):
    uv_full = casa_uvfits
    uv_full2 = UVData()
    testfile1 = str(tmp_path / "uv1.uvfits")
    testfile2 = str(tmp_path / "uv2.uvfits")
    phase_center_radec = [
        uv_full.phase_center_catalog[0]["cat_lon"] + 0.01,
        uv_full.phase_center_catalog[0]["cat_lat"] + 0.01,
    ]
    uv1 = uv_full.copy()
    uv2 = uv_full.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_uvfits(testfile1)
    uv2.write_uvfits(testfile2)
    with uvtest.check_warnings(
        [UserWarning] * 6 + [DeprecationWarning] * 2,
        ["Telescope EVLA is not"] * 2
        + [
            "The uvw_array does not match the expected values given the "
            "antenna positions."
        ]
        * 2
        + ["Phasing this UVData object to"] * 2
        + ["The `allow_rephase` option is deprecated"]
        + ["The `phase_center_radec` parameter is deprecated"],
    ):
        uv1.read(
            np.array([testfile1, testfile2]),
            phase_center_radec=phase_center_radec,
            allow_rephase=True,
            use_future_array_shapes=True,
        )

    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis "
        "using pyuvdata.",
        uv1.history,
    )

    uv_full.phase(*phase_center_radec, cat_name=uv1.phase_center_catalog[0]["cat_name"])
    uv1.history = uv_full.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1.uvfits", "uv2.uvfits"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_full.filename
    uv1._filename.form = (1,)

    uv_full._consolidate_phase_center_catalogs(
        reference_catalog=uv1.phase_center_catalog
    )
    assert uv1 == uv_full

    # check phasing when reading only one file
    with uvtest.check_warnings(
        [UserWarning] * 3 + [DeprecationWarning],
        [
            "Telescope EVLA is not",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
            "Phasing this UVData object to",
            "The `phase_center_radec` parameter is deprecated",
        ],
    ):
        uv_full2.read(
            casa_tutorial_uvfits,
            phase_center_radec=phase_center_radec,
            use_future_array_shapes=True,
        )

    uv_full2._consolidate_phase_center_catalogs(
        reference_catalog=uv_full.phase_center_catalog
    )
    assert uv_full2 == uv_full

    with pytest.raises(ValueError, match="phase_center_radec should have length 2."):
        with uvtest.check_warnings(
            DeprecationWarning, match="The `phase_center_radec` parameter is deprecated"
        ):
            uv_full2.read(
                casa_tutorial_uvfits,
                phase_center_radec=phase_center_radec[0],
                use_future_array_shapes=True,
            )


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
def test_read_ms_write_uvfits_casa_history(tmp_path):
    """
    read in .ms file.
    Write to a uvfits file, read back in and check for casa_history parameter
    """
    pytest.importorskip("casacore")
    ms_uv = UVData()
    uvfits_uv = UVData()
    ms_file = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.ms")
    testfile = str(tmp_path / "outtest.uvfits")
    ms_uv.read_ms(ms_file, use_future_array_shapes=True)
    ms_uv.write_uvfits(testfile)
    uvfits_uv.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert ms_uv.filename == ["day2_TDEM0003_10s_norx_1src_1spw.ms"]
    assert uvfits_uv.filename == ["outtest.uvfits"]
    ms_uv.filename = uvfits_uv.filename

    # propagate scan numbers to the uvfits, ONLY for comparison
    uvfits_uv.scan_number_array = ms_uv.scan_number_array

    for item in ["dut1", "earth_omega", "gst0", "rdate", "timesys"]:
        # Check to make sure that the UVFITS-specific paramters are set on the
        # UVFITS-based obj, and not on our original object. Then set it to None for the
        # UVFITS-based obj.
        assert getattr(ms_uv, item) is None
        assert getattr(uvfits_uv, item) is not None
        setattr(uvfits_uv, item, None)

    uvfits_uv._consolidate_phase_center_catalogs(
        reference_catalog=ms_uv.phase_center_catalog
    )
    assert ms_uv == uvfits_uv


def test_cotter_telescope_frame(tmp_path):
    file1 = os.path.join(DATA_PATH, "1061316296.uvfits")
    write_file = os.path.join(tmp_path, "emulate_cotter.uvfits")
    uvd1 = UVData()

    with fits.open(file1, memmap=True) as hdu_list:
        hdunames = uvutils._fits_indexhdus(hdu_list)
        vis_hdu = hdu_list[0]
        ant_hdu = hdu_list[hdunames["AIPS AN"]]
        ant_hdu.header.pop("FRAME")

        hdulist = fits.HDUList(hdus=[vis_hdu, ant_hdu])
        hdulist.writeto(write_file, overwrite=True)
        hdulist.close()

    with uvtest.check_warnings(
        UserWarning,
        ["Required Antenna keyword 'FRAME' not set; Assuming frame is 'ITRF'."],
    ):
        uvd1.read_uvfits(write_file, read_data=False, use_future_array_shapes=True)


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_readwriteread_reorder_pols(tmp_path, casa_uvfits, future_shapes):
    """
    CASA tutorial uvfits loopback test.

    Read in uvfits file, write out new uvfits file, read back in and check for
    object equality. We check that on-the-fly polarization reordering works.
    """
    uv_in = casa_uvfits

    if not future_shapes:
        uv_in.use_current_array_shapes()

    uv_out = UVData()
    write_file = str(tmp_path / "outtest_casa.uvfits")

    # reorder polarizations
    polarization_input = uv_in.polarization_array
    uv_in.reorder_pols(order=[3, 0, 2, 1])
    assert not np.allclose(uv_in.polarization_array, polarization_input)

    uv_in.write_uvfits(write_file)
    uv_out.read(write_file, use_future_array_shapes=future_shapes)

    # put polarizations back in order
    uv_in.reorder_pols(order="AIPS")

    # make sure filename is what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_casa.uvfits"]
    uv_in.filename = uv_out.filename

    uv_out._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )
    assert uv_in == uv_out


@pytest.mark.parametrize(
    "freq_val,chan_val,msg",
    [
        [-1, 1, "Frequency values must be > 0 for UVFITS!"],
        [1, 0, "Something is wrong, frequency values not"],
    ],
)
def test_flex_spw_uvfits_write_errs(sma_mir, freq_val, chan_val, msg):
    sma_mir.freq_array[:] = freq_val
    sma_mir.channel_width[:] = chan_val
    with pytest.raises(ValueError, match=msg):
        sma_mir.write_uvfits("dummy")


def test_mwax_birli_frame(tmp_path):
    fits_file = os.path.join(DATA_PATH, "1061316296.uvfits")
    outfile = tmp_path / "mwax_birli.uvfits"
    with fits.open(fits_file, memmap=True) as hdu_list:
        hdu_list[0].header["SOFTWARE"] = "birli"
        # remove the frame keyword
        del hdu_list[1].header["FRAME"]
        hdu_list.writeto(outfile)
    with uvtest.check_warnings(
        UserWarning,
        ["Required Antenna keyword 'FRAME' not set; Assuming frame is 'ITRF'."],
    ):
        UVData.from_file(outfile, read_data=False, use_future_array_shapes=True)


def test_mwax_missing_frame_comment(tmp_path):
    fits_file = os.path.join(DATA_PATH, "1061316296.uvfits")
    outfile = tmp_path / "mwax_birli.uvfits"
    with fits.open(fits_file, memmap=True) as hdu_list:
        del hdu_list[1].header["FRAME"], hdu_list[0].header["COMMENT"]
        hdu_list[0].header["COMMENT"] = "A dummy comment."
        hdu_list.writeto(outfile)
    with uvtest.check_warnings(
        UserWarning,
        ["Required Antenna keyword 'FRAME' not set; Assuming frame is 'ITRF'."],
    ):
        UVData.from_file(outfile, read_data=False, use_future_array_shapes=True)


@pytest.mark.filterwarnings("ignore:LST values stored in this file are not ")
@pytest.mark.parametrize("spoof", [False, True])
def test_no_spoof(sma_mir, tmp_path, spoof):
    """
    Verify that option UVParameters are correctly generated when writing to a UVFITS
    file if not previously set, and that spoof_nonessential throws the correct warning
    message if used.
    """
    sma_uvfits = UVData()
    sma_mir._set_app_coords_helper()
    filename = os.path.join(tmp_path, "spoof.uvfits" if spoof else "no_spoof.uvfits")

    with uvtest.check_warnings(
        DeprecationWarning if spoof else None,
        "UVFITS-required metadata are now set automatically to " if spoof else None,
    ):
        sma_mir.write_uvfits(filename, spoof_nonessential=spoof)

    sma_uvfits = UVData.from_file(filename, use_future_array_shapes=True)

    # UVFITS has some differences w/ the MIR format that are expected -- handle
    # all of that here, making sure that the returned values are consistent with
    # what we expect. Start w/ spectral windows
    assert len(np.unique(sma_mir.spw_array)) == len(np.unique(sma_uvfits.spw_array))

    spw_dict = dict(zip(sma_uvfits.spw_array, sma_mir.spw_array))

    assert np.all(
        [
            idx == spw_dict[jdx]
            for idx, jdx in zip(sma_mir.flex_spw_id_array, sma_uvfits.flex_spw_id_array)
        ]
    )
    sma_uvfits.spw_array = sma_mir.spw_array
    sma_uvfits.flex_spw_id_array = sma_mir.flex_spw_id_array

    # Check the history next
    assert sma_uvfits.history.startswith(sma_mir.history)
    sma_mir.history = sma_uvfits.history

    # We have to do a bit of special handling for the phase_center_catalog, because
    # _very_ small floating point errors can creep in.
    for cat_name in sma_mir.phase_center_catalog.keys():
        this_cat = sma_mir.phase_center_catalog[cat_name]
        other_cat = sma_uvfits.phase_center_catalog[cat_name]
        assert np.isclose(this_cat["cat_lat"], other_cat["cat_lat"])
        assert np.isclose(this_cat["cat_lon"], other_cat["cat_lon"])
    sma_uvfits.phase_center_catalog = sma_mir.phase_center_catalog

    # Finally, move on to the "non-spoofed paramters"
    exp_dict = {
        "dut1": -0.2137079,
        "earth_omega": 360.9856438593,
        "gst0": 122.6673828188983,
        "rdate": "2020-07-24",
        "timesys": "UTC",
    }

    for key, value in exp_dict.items():
        if isinstance(value, str):
            assert value == getattr(sma_uvfits, key)
        else:
            assert np.isclose(getattr(sma_uvfits, key), value)
        assert getattr(sma_mir, key) is None
        setattr(sma_mir, key, value)

    assert sma_uvfits == sma_mir


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvfits_phasing_errors(hera_uvh5, tmp_path):
    # check error if phase_type is wrong and force_phase not set
    with pytest.raises(
        ValueError, match="The data are not all phased to a sidereal source"
    ):
        hera_uvh5.write_uvfits(tmp_path)


@pytest.mark.filterwarnings("ignore:The original `phase` method is deprecated")
@pytest.mark.parametrize("use_ant_pos", [True, False])
def test_fix_phase(hera_uvh5, tmpdir, use_ant_pos):
    """
    Test the phase fixing method fix_phase
    """
    # Make some copies of the data
    uv_in = hera_uvh5
    uv_in.extra_keywords = {}

    # These values could be anything -- we're just picking something that we know should
    # be visible from the telescope at the time of obs (ignoring horizon limits).
    phase_ra = uv_in.lst_array[-1]
    phase_dec = uv_in.telescope_location_lat_lon_alt[0] * 0.333
    with uvtest.check_warnings(
        DeprecationWarning,
        match="The `use_old_proj` option is deprecated and will be removed",
    ):
        uv_in.phase(
            phase_ra,
            phase_dec,
            use_old_proj=True,
            use_ant_pos=use_ant_pos,
            cat_name="foo",
        )

    write_file = tmpdir + "oldphase_test.uvfits"

    uv_in.write_uvfits(write_file)
    if use_ant_pos:
        warn_msg = "Fixing phases using antenna positions."
    else:
        warn_msg = "Attempting to fix residual phasing errors from the old `phase`"

    with uvtest.check_warnings(UserWarning, warn_msg):
        uv_fixed = UVData.from_file(
            write_file,
            fix_old_proj=True,
            fix_use_ant_pos=use_ant_pos,
            use_future_array_shapes=True,
        )

    with uvtest.check_warnings(UserWarning, warn_msg):
        uv_in.fix_phase(use_ant_pos=use_ant_pos)

    uv_fixed._consolidate_phase_center_catalogs(
        reference_catalog=uv_in.phase_center_catalog
    )

    assert uv_fixed.__eq__(uv_in, check_extra=False)


# pyuvdata/uvdata/tests/test_uvh5.py

# -*- mode: python; coding: utf-8 -*-
# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 2-clause BSD License

"""Tests for HDF5 object

"""
import json
import os
import re
import shutil
import tempfile
from copy import deepcopy
from pathlib import Path

import h5py
import numpy as np
import pytest
from astropy.time import Time
from packaging import version

import pyuvdata.tests as uvtest
import pyuvdata.utils as uvutils
from pyuvdata import UVData
from pyuvdata.data import DATA_PATH
from pyuvdata.uvdata.uvdata import _future_array_shapes_warning

from .. import uvh5

# ignore common file-read warnings
pytestmark = [
    pytest.mark.filterwarnings("ignore:Altitude is not present in Miriad"),
    pytest.mark.filterwarnings("ignore:Telescope EVLA is not"),
]


@pytest.fixture(scope="session")
def uv_uvh5_main():
    # read in a uvh5 test file
    uv_uvh5 = UVData()
    uvh5_filename = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    uv_uvh5.read_uvh5(uvh5_filename, use_future_array_shapes=True)
    return uv_uvh5


@pytest.fixture(scope="function")
def uv_uvh5(uv_uvh5_main):
    yield uv_uvh5_main.copy()


@pytest.fixture(scope="function")
def uv_partial_write(casa_uvfits, tmp_path):
    # convert a uvfits file to uvh5, cutting down the amount of data
    uv_uvfits = casa_uvfits
    uv_uvfits.select(antenna_nums=[3, 7, 24])
    uv_uvfits.lst_array = uvutils.get_lst_for_time(
        uv_uvfits.time_array, *uv_uvfits.telescope_location_lat_lon_alt_degrees
    )

    testfile = str(tmp_path / "outtest.uvh5")
    uv_uvfits.write_uvh5(testfile)
    uv_uvh5 = UVData()
    uv_uvh5.read(testfile, use_future_array_shapes=True)

    yield uv_uvh5

    # clean up when done
    del uv_uvh5
    os.remove(testfile)

    return


def initialize_with_zeros(uvd, filename):
    """
    Make a uvh5 file with all zero values for data-sized arrays.

    This function is a helper function used for tests of partial writing.
    """
    uvd.initialize_uvh5_file(filename, clobber=True)
    data_shape = (uvd.Nblts, 1, uvd.Nfreqs, uvd.Npols)
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    with h5py.File(filename, "r+") as h5f:
        dgrp = h5f["/Data"]
        data_dset = dgrp["visdata"]
        flags_dset = dgrp["flags"]
        nsample_dset = dgrp["nsamples"]
        data_dset = data  # noqa
        flags_dset = flags  # noqa
        nsample_dset = nsamples  # noqa
    return


def initialize_with_zeros_ints(uvd, filename):
    """
    Make a uvh5 file with all zeros for data-sized arrays.

    This function is a helper function used for tests of partial writing with
    integer data types.
    """
    uvd.initialize_uvh5_file(
        filename, clobber=True, data_write_dtype=uvh5._hera_corr_dtype
    )
    data_shape = (uvd.Nblts, uvd.Nfreqs, uvd.Npols)
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    with h5py.File(filename, "r+") as h5f:
        dgrp = h5f["/Data"]
        data_dset = dgrp["visdata"]
        flags_dset = dgrp["flags"]
        nsample_dset = dgrp["nsamples"]
        data_dset[:, :, :, "r"] = data.real
        data_dset[:, :, :, "i"] = data.imag
        flags_dset = flags  # noqa
        nsample_dset = nsamples  # noqa
    return


def make_old_shapes(filename):
    """Modify the file to have the old shapes

    (it always writes them with the future shapes)
    """
    with h5py.File(filename, "r+") as h5f:
        freq_array = h5f["Header/freq_array"][()]
        del h5f["Header/freq_array"]
        h5f["Header/freq_array"] = freq_array[np.newaxis, :]

        channel_width = h5f["Header/channel_width"][()]
        del h5f["Header/channel_width"]
        h5f["Header/channel_width"] = channel_width[0]

        data_array = h5f["Data/visdata"][()]
        del h5f["Data/visdata"]
        h5f["Data/visdata"] = data_array[:, np.newaxis, :, :]

        flag_array = h5f["Data/flags"][()]
        del h5f["Data/flags"]
        h5f["Data/flags"] = flag_array[:, np.newaxis, :, :]

        nsamples = h5f["Data/nsamples"][()]
        del h5f["Data/nsamples"]
        h5f["Data/nsamples"] = nsamples[:, np.newaxis, :, :]


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_read_miriad_write_uvh5_read_uvh5(paper_miriad, future_shapes, tmp_path):
    """
    Test a miriad file round trip.
    """
    uv_in = paper_miriad
    if not future_shapes:
        uv_in.use_current_array_shapes()

    uv_out = UVData()
    testfile = str(tmp_path / "outtest_miriad.uvh5")

    # create the file so the clobber gets tested
    with h5py.File(testfile, "w") as h5file:
        h5file.create_dataset("Test", list(range(10)))

    uv_in.write_uvh5(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2456865.60537.xy.uvcRREAA"]
    assert uv_out.filename == ["outtest_miriad.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # also test round-tripping phased data
    uv_in.phase_to_time(Time(np.mean(uv_in.time_array), format="jd"))
    uv_in.write_uvh5(testfile, clobber=True)
    uv_out.read_uvh5(testfile, use_future_array_shapes=future_shapes)

    assert uv_in == uv_out

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_uvfits_write_uvh5_read_uvh5(casa_uvfits, tmp_path):
    """
    Test a uvfits file round trip.
    """
    uv_in = casa_uvfits
    uv_out = UVData()
    testfile = str(tmp_path / "outtest_uvfits.uvh5")
    uv_in.write_uvh5(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_uvfits.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # also test writing double-precision data_array
    uv_in.data_array = uv_in.data_array.astype(np.complex128)
    uv_in.write_uvh5(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)
    assert uv_in == uv_out

    # clean up
    os.remove(testfile)

    return


def test_read_uvh5_errors():
    """
    Test raising errors in read function.
    """
    if version.parse(h5py.version.hdf5_version) >= version.parse("1.14.0"):
        err_msg = "Unable to synchronously open file"
    else:
        err_msg = "Unable to open file"

    uv_in = UVData()
    fake_file = os.path.join(DATA_PATH, "fake_file.uvh5")
    with pytest.raises(IOError, match=err_msg):
        uv_in.read_uvh5(fake_file)


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
def test_write_uvh5_errors(casa_uvfits, tmp_path):
    """
    Test raising errors in write_uvh5 function.
    """
    uv_in = casa_uvfits
    uv_in.use_current_array_shapes()

    uv_out = UVData()
    testfile = str(tmp_path / "outtest_uvfits.uvh5")
    with open(testfile, "a"):
        os.utime(testfile, None)

    # assert IOError if file exists
    with pytest.raises(IOError, match="File exists; skipping"):
        uv_in.write_uvh5(testfile, clobber=False)

    # use clobber=True to write out anyway
    uv_in.write_uvh5(testfile, clobber=True)
    with uvtest.check_warnings(
        [UserWarning, UserWarning, DeprecationWarning],
        match=[
            "Telescope EVLA is not in known_telescopes.",
            "The uvw_array does not match the expected values",
            _future_array_shapes_warning,
        ],
    ):
        uv_out.read(testfile)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_uvfits.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_optional_parameters(casa_uvfits, tmp_path):
    """
    Test reading and writing optional parameters not in sample files.
    """
    uv_in = casa_uvfits
    uv_out = UVData()
    testfile = str(tmp_path / "outtest_uvfits.uvh5")

    # set optional parameters
    uv_in.x_orientation = "east"
    uv_in.antenna_diameters = np.ones_like(uv_in.antenna_numbers) * 1.0
    uv_in.uvplane_reference_time = 0

    # reorder_blts
    uv_in.reorder_blts()

    # write out and read back in
    uv_in.write_uvh5(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_uvfits.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # test with blt_order = bda as well (single entry in tuple)
    uv_in.reorder_blts(order="bda")

    uv_in.write_uvh5(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)
    assert uv_in == uv_out

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_compression_options(casa_uvfits, tmp_path):
    """
    Test writing data with compression filters.
    """
    import h5py

    uv_in = casa_uvfits
    uv_out = UVData()
    testfile = str(tmp_path / "outtest_uvfits_compression.uvh5")

    uv_in.use_current_array_shapes()
    # write out and read back in
    uv_in.write_uvh5(
        testfile,
        clobber=True,
        chunks=True,
        data_compression="lzf",
        flags_compression=None,
        nsample_compression=None,
    )
    uv_out.read(testfile)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_uvfits_compression.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # test with non-auto chunking
    uv_in.use_future_array_shapes()
    chunks = (680, 16, 1)
    uv_in.write_uvh5(
        testfile,
        clobber=True,
        chunks=chunks,
        data_compression="lzf",
        flags_compression=None,
        nsample_compression=None,
    )
    uv_out.read(testfile, use_future_array_shapes=True)
    assert uv_in == uv_out

    # check that chunks match
    with h5py.File(testfile, "r") as f:
        assert f["Data"]["visdata"].chunks == chunks

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:Telescope EVLA is not in known_telescopes.")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_read_multiple_files(casa_uvfits, tmp_path):
    """
    Test reading multiple uvh5 files.
    """
    uv_in = casa_uvfits
    testfile1 = str(tmp_path / "uv1.uvh5")
    testfile2 = str(tmp_path / "uv2.uvh5")
    uv1 = uv_in.copy()
    uv2 = uv_in.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_uvh5(testfile1, clobber=True)
    uv2.write_uvh5(testfile2, clobber=True)
    uv1.read(
        np.array([testfile1, testfile2]),
        file_type="uvh5",
        allow_rephase=False,
        use_future_array_shapes=True,
    )

    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_in.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis using"
        " pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_in.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1.uvh5", "uv2.uvh5"}
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_in.filename
    uv1._filename.form = (1,)

    assert uv1 == uv_in

    # clean up
    os.remove(testfile1)
    os.remove(testfile2)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_read_multiple_files_metadata_only(casa_uvfits, tmp_path):
    """
    Test reading multiple uvh5 files with metadata only.
    """
    uv_in = casa_uvfits
    testfile1 = str(tmp_path / "uv1.uvh5")
    testfile2 = str(tmp_path / "uv2.uvh5")
    uv1 = uv_in.copy()
    uv2 = uv_in.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_uvh5(testfile1, clobber=True)
    uv2.write_uvh5(testfile2, clobber=True)

    uvfits_filename = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.uvfits")
    uv_full = UVData()
    uv_full.read_uvfits(uvfits_filename, read_data=False, use_future_array_shapes=True)
    uv1.read(
        [testfile1, testfile2],
        read_data=False,
        allow_rephase=False,
        use_future_array_shapes=True,
    )
    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_full.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis using"
        " pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_full.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1.uvh5", "uv2.uvh5"}
    assert uv_full.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_full.filename
    uv1._filename.form = (1,)

    assert uv1 == uv_full

    # clean up
    os.remove(testfile1)
    os.remove(testfile2)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_read_multiple_files_axis(casa_uvfits, tmp_path):
    """
    Test reading multiple uvh5 files with setting axis.
    """
    uv_in = casa_uvfits
    testfile1 = str(tmp_path / "uv1.uvh5")
    testfile2 = str(tmp_path / "uv2.uvh5")
    uv1 = uv_in.copy()
    uv2 = uv_in.copy()
    uv1.select(freq_chans=np.arange(0, 32))
    uv2.select(freq_chans=np.arange(32, 64))
    uv1.write_uvh5(testfile1, clobber=True)
    uv2.write_uvh5(testfile2, clobber=True)
    uv1.read(
        [testfile1, testfile2],
        axis="freq",
        allow_rephase=False,
        use_future_array_shapes=True,
    )
    # Check history is correct, before replacing and doing a full object check
    assert uvutils._check_histories(
        uv_in.history + "  Downselected to "
        "specific frequencies using pyuvdata. "
        "Combined data along frequency axis using"
        " pyuvdata.",
        uv1.history,
    )
    uv1.history = uv_in.history

    # make sure filenames are what we expect
    assert set(uv1.filename) == {"uv1.uvh5", "uv2.uvh5"}
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    uv1.filename = uv_in.filename
    uv1._filename.form = (1,)

    assert uv1 == uv_in

    # clean up
    os.remove(testfile1)
    os.remove(testfile2)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_partial_read_antennas(casa_uvfits, tmp_path):
    """
    Test reading in only certain antennas from disk.
    """
    uv_in = casa_uvfits

    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)
    uvh5_uv.read(testfile, use_future_array_shapes=True)

    # select on antennas
    ants_to_keep = np.array([1, 20, 12, 25, 4, 24, 2, 21, 22])
    uvh5_uv.read(testfile, antenna_nums=ants_to_keep, use_future_array_shapes=True)
    uvh5_uv2.read(testfile, use_future_array_shapes=True)
    uvh5_uv2.select(antenna_nums=ants_to_keep)
    assert uvh5_uv == uvh5_uv2

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_partial_read_freqs(casa_uvfits, tmp_path):
    """
    Test reading in only certain frequencies from disk.
    """
    uv_in = casa_uvfits

    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)

    uvh5_uv.read(testfile, use_future_array_shapes=True)

    # select on frequency channels
    chans_to_keep = np.arange(12, 22)
    uvh5_uv.read(testfile, freq_chans=chans_to_keep, use_future_array_shapes=True)
    uvh5_uv2.read(testfile, use_future_array_shapes=True)
    uvh5_uv2.select(freq_chans=chans_to_keep)
    assert uvh5_uv == uvh5_uv2

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Selected polarization values are not evenly spaced")
def test_uvh5_partial_read_pols(casa_uvfits, tmp_path):
    """
    Test reading in only certain polarizations from disk.
    """
    uv_in = casa_uvfits

    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)
    uvh5_uv.read(testfile, use_future_array_shapes=True)

    # select on pols
    pols_to_keep = [-1, -2]
    uvh5_uv.read(testfile, polarizations=pols_to_keep, use_future_array_shapes=True)
    uvh5_uv2.read(testfile, use_future_array_shapes=True)
    uvh5_uv2.select(polarizations=pols_to_keep)
    assert uvh5_uv == uvh5_uv2

    # select on pols (non contiguous in file)
    # and check consistent results with and without multidim_index
    pols_to_keep = [-1, -2, -4]
    uvh5_uv.read(
        testfile,
        polarizations=pols_to_keep,
        multidim_index=True,
        use_future_array_shapes=True,
    )
    uvh5_uv2.read(testfile, multidim_index=False, use_future_array_shapes=True)
    uvh5_uv2.select(polarizations=pols_to_keep)
    assert uvh5_uv == uvh5_uv2

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_partial_read_times(casa_uvfits, tmp_path):
    """
    Test reading in only certain times from disk.
    """
    uv_in = casa_uvfits

    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)
    uvh5_uv.read(testfile, use_future_array_shapes=True)

    # select on read using time_range
    unique_times = np.unique(uvh5_uv.time_array)
    uvh5_uv.read(
        testfile,
        time_range=[unique_times[0], unique_times[1]],
        use_future_array_shapes=True,
    )
    uvh5_uv2.read(testfile, use_future_array_shapes=True)
    uvh5_uv2.select(time_range=[unique_times[0], unique_times[1]])
    assert uvh5_uv == uvh5_uv2

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_partial_read_lsts(casa_uvfits, tmp_path):
    """
    Test reading in only certain lsts from disk.
    """
    uv_in = casa_uvfits

    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)
    uvh5_uv.read(testfile, use_future_array_shapes=True)

    # select on read using lst_range
    unique_lsts = np.unique(uvh5_uv.lst_array)
    uvh5_uv.read(
        testfile,
        lst_range=[unique_lsts[0], unique_lsts[2]],
        use_future_array_shapes=True,
    )
    uvh5_uv2.read(testfile, use_future_array_shapes=True)
    uvh5_uv2.select(lst_range=[unique_lsts[0], unique_lsts[2]])
    assert uvh5_uv == uvh5_uv2

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Selected polarization values are not evenly spaced")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_read_multi1(casa_uvfits, future_shapes, tmp_path):
    """
    Test select-on-read for multiple axes, frequencies being smallest fraction.
    """
    uv_in = casa_uvfits
    if not future_shapes:
        uv_in.use_current_array_shapes()

    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)

    if not future_shapes:
        make_old_shapes(testfile)

    uvh5_uv.read(testfile, use_future_array_shapes=future_shapes)

    # now test selecting on multiple axes
    # read frequencies first
    ants_to_keep = np.array([1, 20, 12, 25, 4, 24, 2, 21, 22])
    chans_to_keep = np.arange(12, 22)
    pols_to_keep = [-1, -2]
    uvh5_uv.read(
        testfile,
        antenna_nums=ants_to_keep,
        freq_chans=chans_to_keep,
        polarizations=pols_to_keep,
        use_future_array_shapes=future_shapes,
    )
    uvh5_uv2.read(testfile, use_future_array_shapes=future_shapes)
    uvh5_uv2.select(
        antenna_nums=ants_to_keep, freq_chans=chans_to_keep, polarizations=pols_to_keep
    )
    assert uvh5_uv == uvh5_uv2

    # now check with multidim_index
    # use different versions of blt_inds and pol_inds
    # ones that are and are not sliceable by UVH5 standards
    chans_to_keep = np.arange(20, 35)
    uvh5_uv3 = UVData()
    uvh5_uv4 = UVData()
    random_blts = np.random.choice(np.arange(uv_in.Nblts), size=1000, replace=False)
    for blts_to_keep in [random_blts, np.arange(1000)]:
        for pols_to_keep in [[-1, -2], [-1, -2, -4]]:
            uvh5_uv3.read(
                testfile,
                blt_inds=blts_to_keep,
                polarizations=pols_to_keep,
                freq_chans=chans_to_keep,
                multidim_index=True,
                use_future_array_shapes=future_shapes,
            )
            uvh5_uv4.read(testfile, use_future_array_shapes=future_shapes)
            uvh5_uv4.select(
                blt_inds=blts_to_keep,
                polarizations=pols_to_keep,
                freq_chans=chans_to_keep,
            )
            assert uvh5_uv3 == uvh5_uv4

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Selected polarization values are not evenly spaced")
@pytest.mark.filterwarnings("ignore:Selected frequencies are not evenly spaced")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_read_multi2(casa_uvfits, future_shapes, tmp_path):
    """
    Test select-on-read for multiple axes, baselines being smallest fraction.
    """
    uv_in = casa_uvfits
    if not future_shapes:
        uv_in.use_current_array_shapes()

    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)

    if not future_shapes:
        make_old_shapes(testfile)

    uvh5_uv.read(testfile, use_future_array_shapes=future_shapes)

    # now test selecting on multiple axes
    # read baselines first
    ants_to_keep = np.array([1, 2])
    chans_to_keep = np.arange(12, 22)
    pols_to_keep = [-1, -2]
    uvh5_uv.read(
        testfile,
        antenna_nums=ants_to_keep,
        freq_chans=chans_to_keep,
        polarizations=pols_to_keep,
        use_future_array_shapes=future_shapes,
    )
    uvh5_uv2.read(testfile, use_future_array_shapes=future_shapes)
    uvh5_uv2.select(
        antenna_nums=ants_to_keep, freq_chans=chans_to_keep, polarizations=pols_to_keep
    )
    assert uvh5_uv == uvh5_uv2

    # now check with multidim_index
    # use different versions of freq_inds and pol_inds
    # ones that are and are not sliceable by UVH5 standards
    blts_to_keep = np.arange(100)
    uvh5_uv3 = UVData()
    uvh5_uv4 = UVData()
    random_freqs = np.random.choice(np.arange(uv_in.Nfreqs), size=50, replace=False)
    for chans_to_keep in [random_freqs, np.arange(50)]:
        for pols_to_keep in [[-1, -2], [-1, -2, -4]]:
            uvh5_uv3.read(
                testfile,
                blt_inds=blts_to_keep,
                freq_chans=chans_to_keep,
                polarizations=pols_to_keep,
                multidim_index=True,
                use_future_array_shapes=future_shapes,
            )
            uvh5_uv4.read(testfile, use_future_array_shapes=future_shapes)
            uvh5_uv4.select(
                blt_inds=blts_to_keep,
                freq_chans=chans_to_keep,
                polarizations=pols_to_keep,
            )
            assert uvh5_uv3 == uvh5_uv4

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Selected frequencies are not evenly spaced")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_read_multi3(casa_uvfits, future_shapes, tmp_path):
    """
    Test select-on-read for multiple axes, polarizations being smallest fraction.
    """
    uv_in = casa_uvfits
    if not future_shapes:
        uv_in.use_current_array_shapes()

    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)

    if not future_shapes:
        make_old_shapes(testfile)

    uvh5_uv.read(testfile, use_future_array_shapes=future_shapes)

    # now test selecting on multiple axes
    # read polarizations first
    ants_to_keep = np.array([1, 2, 3, 4, 7, 8, 9, 12, 15, 19, 20, 21, 22, 23])
    chans_to_keep = np.arange(12, 64)
    pols_to_keep = [-1, -2]
    uvh5_uv.read(
        testfile,
        antenna_nums=ants_to_keep,
        freq_chans=chans_to_keep,
        polarizations=pols_to_keep,
        use_future_array_shapes=future_shapes,
    )
    uvh5_uv2.read(testfile, use_future_array_shapes=future_shapes)
    uvh5_uv2.select(
        antenna_nums=ants_to_keep, freq_chans=chans_to_keep, polarizations=pols_to_keep
    )
    assert uvh5_uv == uvh5_uv2

    # now check with multidim_index
    # use different versions of blt_inds and freq_inds
    # ones that are and are not sliceable by UVH5 standards
    pols_to_keep = [-1, -2]
    uvh5_uv3 = UVData()
    uvh5_uv4 = UVData()
    random_blts = np.random.choice(np.arange(uv_in.Nblts), size=1000, replace=False)
    random_freqs = np.random.choice(np.arange(uv_in.Nfreqs), size=50, replace=False)
    for blts_to_keep in [random_blts, np.arange(1000)]:
        for chans_to_keep in [random_freqs, np.arange(50)]:
            uvh5_uv3.read(
                testfile,
                blt_inds=blts_to_keep,
                freq_chans=chans_to_keep,
                polarizations=pols_to_keep,
                multidim_index=True,
                use_future_array_shapes=future_shapes,
            )
            uvh5_uv4.read(testfile, use_future_array_shapes=future_shapes)
            uvh5_uv4.select(
                blt_inds=blts_to_keep,
                freq_chans=chans_to_keep,
                polarizations=pols_to_keep,
            )
            assert uvh5_uv3 == uvh5_uv4

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:Selected frequencies are not evenly spaced")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_read_multdim_index(tmp_path, future_shapes, casa_uvfits):
    """
    Test some odd cases for UVH5 multdim indexing
    """
    uv_in = casa_uvfits
    if not future_shapes:
        uv_in.use_current_array_shapes()

    testfile = str(tmp_path / "outtest.uvh5")
    # change telescope name to avoid errors
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)
    uvh5_uv = UVData()
    uvh5_uv.read(testfile, use_future_array_shapes=future_shapes)

    # check that non sliceable multidim index is caught
    # and does not fail
    ants_to_keep = np.array([1, 20, 12, 25, 4, 24, 2, 21, 22])
    chans_to_keep = [15, 17, 20]
    uvh5_uv = UVData()
    uvh5_uv.read(
        testfile,
        antenna_nums=ants_to_keep,
        freq_chans=chans_to_keep,
        use_future_array_shapes=future_shapes,
    )

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_antpairs(uv_partial_write, future_shapes, tmp_path):
    """
    Test writing an entire UVH5 file in pieces by antpairs.
    """
    full_uvh5 = uv_partial_write
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=True)

    # write to file by iterating over antpairpol
    antpairpols = full_uvh5.get_antpairpols()
    for key in antpairpols:
        data = full_uvh5.get_data(key, squeeze="none")
        flags = full_uvh5.get_flags(key, squeeze="none")
        nsamples = full_uvh5.get_nsamples(key, squeeze="none")
        partial_uvh5.write_uvh5_part(partial_testfile, data, flags, nsamples, bls=key)

    # now read in the full file and make sure that it matches the original
    partial_uvh5.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filename is what we expect
    assert full_uvh5.filename == ["outtest.uvh5"]
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    full_uvh5.filename = partial_uvh5.filename

    assert full_uvh5 == partial_uvh5

    # test add_to_history
    key = antpairpols[0]
    data = full_uvh5.get_data(key, squeeze="none")
    flags = full_uvh5.get_flags(key, squeeze="none")
    nsamples = full_uvh5.get_nsamples(key, squeeze="none")
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, bls=key, add_to_history="foo"
    )
    partial_uvh5.read(partial_testfile, read_data=False)
    assert "foo" in partial_uvh5.history

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_frequencies(uv_partial_write, future_shapes, tmp_path):
    """
    Test writing an entire UVH5 file in pieces by frequencies.
    """
    full_uvh5 = uv_partial_write
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    # start over, and write frequencies
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=True)

    Nfreqs = full_uvh5.Nfreqs
    Hfreqs = Nfreqs // 2
    freqs1 = np.arange(Hfreqs)
    freqs2 = np.arange(Hfreqs, Nfreqs)
    if future_shapes:
        data = full_uvh5.data_array[:, freqs1, :]
        flags = full_uvh5.flag_array[:, freqs1, :]
        nsamples = full_uvh5.nsample_array[:, freqs1, :]
    else:
        data = full_uvh5.data_array[:, :, freqs1, :]
        flags = full_uvh5.flag_array[:, :, freqs1, :]
        nsamples = full_uvh5.nsample_array[:, :, freqs1, :]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, freq_chans=freqs1
    )
    if future_shapes:
        data = full_uvh5.data_array[:, freqs2, :]
        flags = full_uvh5.flag_array[:, freqs2, :]
        nsamples = full_uvh5.nsample_array[:, freqs2, :]
    else:
        data = full_uvh5.data_array[:, :, freqs2, :]
        flags = full_uvh5.flag_array[:, :, freqs2, :]
        nsamples = full_uvh5.nsample_array[:, :, freqs2, :]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, freq_chans=freqs2
    )

    # read in the full file and make sure it matches
    partial_uvh5.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames match what we expect
    assert full_uvh5.filename == ["outtest.uvh5"]
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    full_uvh5.filename = partial_uvh5.filename

    assert full_uvh5 == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_blts(uv_partial_write, future_shapes, tmp_path):
    """
    Test writing an entire UVH5 file in pieces by blt.
    """
    full_uvh5 = uv_partial_write
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    # start over, write chunks of blts
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=True)

    Nblts = full_uvh5.Nblts
    Hblts = Nblts // 2
    blts1 = np.arange(Hblts)
    blts2 = np.arange(Hblts, Nblts)
    data = full_uvh5.data_array[blts1]
    flags = full_uvh5.flag_array[blts1]
    nsamples = full_uvh5.nsample_array[blts1]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, blt_inds=blts1
    )
    data = full_uvh5.data_array[blts2]
    flags = full_uvh5.flag_array[blts2]
    nsamples = full_uvh5.nsample_array[blts2]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, blt_inds=blts2
    )

    # read in the full file and make sure it matches
    partial_uvh5.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert full_uvh5.filename == ["outtest.uvh5"]
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    full_uvh5.filename = partial_uvh5.filename

    assert full_uvh5 == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_pols(uv_partial_write, future_shapes, tmp_path):
    """
    Test writing an entire UVH5 file in pieces by pol.
    """
    full_uvh5 = uv_partial_write
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    # start over, write groups of pols
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=True)

    Npols = full_uvh5.Npols
    Hpols = Npols // 2
    pols1 = np.arange(Hpols)
    pols2 = np.arange(Hpols, Npols)
    if future_shapes:
        data = full_uvh5.data_array[:, :, pols1]
        flags = full_uvh5.flag_array[:, :, pols1]
        nsamples = full_uvh5.nsample_array[:, :, pols1]
    else:
        data = full_uvh5.data_array[:, :, :, pols1]
        flags = full_uvh5.flag_array[:, :, :, pols1]
        nsamples = full_uvh5.nsample_array[:, :, :, pols1]
    partial_uvh5.write_uvh5_part(
        partial_testfile,
        data,
        flags,
        nsamples,
        polarizations=full_uvh5.polarization_array[:Hpols],
    )
    if future_shapes:
        data = full_uvh5.data_array[:, :, pols2]
        flags = full_uvh5.flag_array[:, :, pols2]
        nsamples = full_uvh5.nsample_array[:, :, pols2]
    else:
        data = full_uvh5.data_array[:, :, :, pols2]
        flags = full_uvh5.flag_array[:, :, :, pols2]
        nsamples = full_uvh5.nsample_array[:, :, :, pols2]
    partial_uvh5.write_uvh5_part(
        partial_testfile,
        data,
        flags,
        nsamples,
        polarizations=full_uvh5.polarization_array[Hpols:],
    )

    # read in the full file and make sure it matches
    partial_uvh5.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert full_uvh5.filename == ["outtest.uvh5"]
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    full_uvh5.filename = partial_uvh5.filename

    assert full_uvh5 == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_partial_write_irregular_blt(uv_partial_write, tmp_path):
    """
    Test writing a uvh5 file using irregular intervals for single blt.
    """
    full_uvh5 = uv_partial_write
    partial_uvh5 = UVData()

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # write a single blt to file
    blt_inds = np.arange(1)
    data = full_uvh5.data_array[blt_inds]
    flags = full_uvh5.flag_array[blt_inds]
    nsamples = full_uvh5.nsample_array[blt_inds]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, blt_inds=blt_inds
    )

    # also write the arrays to the partial object
    partial_uvh5.data_array[blt_inds] = data
    partial_uvh5.flag_array[blt_inds] = flags
    partial_uvh5.nsample_array[blt_inds] = nsamples

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_partial_write_irregular_freq(uv_partial_write, tmp_path):
    """
    Test writing a uvh5 file using irregular intervals for single frequency.
    """
    full_uvh5 = uv_partial_write
    partial_uvh5 = UVData()

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # write a single freq to file
    freq_inds = np.arange(1)
    data = full_uvh5.data_array[:, freq_inds, :]
    flags = full_uvh5.flag_array[:, freq_inds, :]
    nsamples = full_uvh5.nsample_array[:, freq_inds, :]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, freq_chans=freq_inds
    )

    # also write the arrays to the partial object
    partial_uvh5.data_array[:, freq_inds, :] = data
    partial_uvh5.flag_array[:, freq_inds, :] = flags
    partial_uvh5.nsample_array[:, freq_inds, :] = nsamples

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_partial_write_irregular_pol(uv_partial_write, tmp_path):
    """
    Test writing a uvh5 file using irregular intervals for single polarization.
    """
    full_uvh5 = uv_partial_write
    partial_uvh5 = UVData()

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # write a single pol to file
    pol_inds = np.arange(1)
    data = full_uvh5.data_array[:, :, pol_inds]
    flags = full_uvh5.flag_array[:, :, pol_inds]
    nsamples = full_uvh5.nsample_array[:, :, pol_inds]
    partial_uvh5.write_uvh5_part(
        partial_testfile,
        data,
        flags,
        nsamples,
        polarizations=partial_uvh5.polarization_array[pol_inds],
    )

    # also write the arrays to the partial object
    partial_uvh5.data_array[:, :, pol_inds] = data
    partial_uvh5.flag_array[:, :, pol_inds] = flags
    partial_uvh5.nsample_array[:, :, pol_inds] = nsamples

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_irregular_multi1(uv_partial_write, future_shapes, tmp_path):
    """
    Test writing a uvh5 file using irregular intervals for blts and freqs.
    """
    full_uvh5 = uv_partial_write
    full_uvh5.telescope_name = "PAPER"
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # define blts and freqs
    blt_inds = [0, 1, 2, 7]
    freq_inds = [0, 2, 3, 4]
    if future_shapes:
        data_shape = (len(blt_inds), len(freq_inds), full_uvh5.Npols)
    else:
        data_shape = (len(blt_inds), 1, len(freq_inds), full_uvh5.Npols)
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    for iblt, blt_idx in enumerate(blt_inds):
        for ifreq, freq_idx in enumerate(freq_inds):
            if future_shapes:
                data[iblt, ifreq, :] = full_uvh5.data_array[blt_idx, freq_idx, :]
                flags[iblt, ifreq, :] = full_uvh5.flag_array[blt_idx, freq_idx, :]
                nsamples[iblt, ifreq, :] = full_uvh5.nsample_array[blt_idx, freq_idx, :]
            else:
                data[iblt, :, ifreq, :] = full_uvh5.data_array[blt_idx, :, freq_idx, :]
                flags[iblt, :, ifreq, :] = full_uvh5.flag_array[blt_idx, :, freq_idx, :]
                nsamples[iblt, :, ifreq, :] = full_uvh5.nsample_array[
                    blt_idx, :, freq_idx, :
                ]
    with uvtest.check_warnings(
        UserWarning, "Selected frequencies are not evenly spaced"
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile,
            data,
            flags,
            nsamples,
            blt_inds=blt_inds,
            freq_chans=freq_inds,
        )

    # also write the arrays to the partial object
    for iblt, blt_idx in enumerate(blt_inds):
        for ifreq, freq_idx in enumerate(freq_inds):
            if future_shapes:
                partial_uvh5.data_array[blt_idx, freq_idx, :] = data[iblt, ifreq, :]
                partial_uvh5.flag_array[blt_idx, freq_idx, :] = flags[iblt, ifreq, :]
                partial_uvh5.nsample_array[blt_idx, freq_idx, :] = nsamples[
                    iblt, ifreq, :
                ]
            else:
                partial_uvh5.data_array[blt_idx, :, freq_idx, :] = data[
                    iblt, :, ifreq, :
                ]
                partial_uvh5.flag_array[blt_idx, :, freq_idx, :] = flags[
                    iblt, :, ifreq, :
                ]
                partial_uvh5.nsample_array[blt_idx, :, freq_idx, :] = nsamples[
                    iblt, :, ifreq, :
                ]

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_irregular_multi2(uv_partial_write, future_shapes, tmp_path):
    """
    Test writing a uvh5 file using irregular intervals for freqs and pols.
    """
    full_uvh5 = uv_partial_write
    full_uvh5.telescope_name = "PAPER"
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # define freqs and pols
    freq_inds = [0, 1, 2, 7]
    pol_inds = [0, 1, 3]
    if future_shapes:
        data_shape = (full_uvh5.Nblts, len(freq_inds), len(pol_inds))
    else:
        data_shape = (full_uvh5.Nblts, 1, len(freq_inds), len(pol_inds))
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    for ifreq, freq_idx in enumerate(freq_inds):
        for ipol, pol_idx in enumerate(pol_inds):
            if future_shapes:
                data[:, ifreq, ipol] = full_uvh5.data_array[:, freq_idx, pol_idx]
                flags[:, ifreq, ipol] = full_uvh5.flag_array[:, freq_idx, pol_idx]
                nsamples[:, ifreq, ipol] = full_uvh5.nsample_array[:, freq_idx, pol_idx]
            else:
                data[:, :, ifreq, ipol] = full_uvh5.data_array[:, :, freq_idx, pol_idx]
                flags[:, :, ifreq, ipol] = full_uvh5.flag_array[:, :, freq_idx, pol_idx]
                nsamples[:, :, ifreq, ipol] = full_uvh5.nsample_array[
                    :, :, freq_idx, pol_idx
                ]
    with uvtest.check_warnings(
        UserWarning,
        [
            "Selected frequencies are not evenly spaced",
            "Selected polarization values are not evenly spaced",
        ],
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile,
            data,
            flags,
            nsamples,
            freq_chans=freq_inds,
            polarizations=full_uvh5.polarization_array[pol_inds],
        )

    # also write the arrays to the partial object
    for ifreq, freq_idx in enumerate(freq_inds):
        for ipol, pol_idx in enumerate(pol_inds):
            if future_shapes:
                partial_uvh5.data_array[:, freq_idx, pol_idx] = data[:, ifreq, ipol]
                partial_uvh5.flag_array[:, freq_idx, pol_idx] = flags[:, ifreq, ipol]
                partial_uvh5.nsample_array[:, freq_idx, pol_idx] = nsamples[
                    :, ifreq, ipol
                ]
            else:
                partial_uvh5.data_array[:, :, freq_idx, pol_idx] = data[
                    :, :, ifreq, ipol
                ]
                partial_uvh5.flag_array[:, :, freq_idx, pol_idx] = flags[
                    :, :, ifreq, ipol
                ]
                partial_uvh5.nsample_array[:, :, freq_idx, pol_idx] = nsamples[
                    :, :, ifreq, ipol
                ]

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_irregular_multi3(uv_partial_write, future_shapes, tmp_path):
    """
    Test writing a uvh5 file using irregular intervals for blts and pols.
    """
    full_uvh5 = uv_partial_write
    full_uvh5.telescope_name = "PAPER"
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # define blts and freqs
    blt_inds = [0, 1, 2, 7]
    pol_inds = [0, 1, 3]
    if future_shapes:
        data_shape = (len(blt_inds), full_uvh5.Nfreqs, len(pol_inds))
    else:
        data_shape = (len(blt_inds), 1, full_uvh5.Nfreqs, len(pol_inds))
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    for iblt, blt_idx in enumerate(blt_inds):
        for ipol, pol_idx in enumerate(pol_inds):
            if future_shapes:
                data[iblt, :, ipol] = full_uvh5.data_array[blt_idx, :, pol_idx]
                flags[iblt, :, ipol] = full_uvh5.flag_array[blt_idx, :, pol_idx]
                nsamples[iblt, :, ipol] = full_uvh5.nsample_array[blt_idx, :, pol_idx]
            else:
                data[iblt, :, :, ipol] = full_uvh5.data_array[blt_idx, :, :, pol_idx]
                flags[iblt, :, :, ipol] = full_uvh5.flag_array[blt_idx, :, :, pol_idx]
                nsamples[iblt, :, :, ipol] = full_uvh5.nsample_array[
                    blt_idx, :, :, pol_idx
                ]
    with uvtest.check_warnings(
        UserWarning, "Selected polarization values are not evenly spaced"
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile,
            data,
            flags,
            nsamples,
            blt_inds=blt_inds,
            polarizations=full_uvh5.polarization_array[pol_inds],
        )

    # also write the arrays to the partial object
    for iblt, blt_idx in enumerate(blt_inds):
        for ipol, pol_idx in enumerate(pol_inds):
            if future_shapes:
                partial_uvh5.data_array[blt_idx, :, pol_idx] = data[iblt, :, ipol]
                partial_uvh5.flag_array[blt_idx, :, pol_idx] = flags[iblt, :, ipol]
                partial_uvh5.nsample_array[blt_idx, :, pol_idx] = nsamples[
                    iblt, :, ipol
                ]
            else:
                partial_uvh5.data_array[blt_idx, :, :, pol_idx] = data[iblt, :, :, ipol]
                partial_uvh5.flag_array[blt_idx, :, :, pol_idx] = flags[
                    iblt, :, :, ipol
                ]
                partial_uvh5.nsample_array[blt_idx, :, :, pol_idx] = nsamples[
                    iblt, :, :, ipol
                ]

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_irregular_multi4(uv_partial_write, future_shapes, tmp_path):
    """
    Test writing a uvh5 file using irregular intervals for all axes.
    """
    full_uvh5 = uv_partial_write
    full_uvh5.telescope_name = "PAPER"
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # define blts and freqs
    blt_inds = [0, 1, 2, 7]
    freq_inds = [0, 2, 3, 4]
    pol_inds = [0, 1, 3]
    if future_shapes:
        data_shape = (len(blt_inds), len(freq_inds), len(pol_inds))
    else:
        data_shape = (len(blt_inds), 1, len(freq_inds), len(pol_inds))
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    for iblt, blt_idx in enumerate(blt_inds):
        for ifreq, freq_idx in enumerate(freq_inds):
            for ipol, pol_idx in enumerate(pol_inds):
                if future_shapes:
                    data[iblt, ifreq, ipol] = full_uvh5.data_array[
                        blt_idx, freq_idx, pol_idx
                    ]
                    flags[iblt, ifreq, ipol] = full_uvh5.flag_array[
                        blt_idx, freq_idx, pol_idx
                    ]
                    nsamples[iblt, ifreq, ipol] = full_uvh5.nsample_array[
                        blt_idx, freq_idx, pol_idx
                    ]
                else:
                    data[iblt, :, ifreq, ipol] = full_uvh5.data_array[
                        blt_idx, :, freq_idx, pol_idx
                    ]
                    flags[iblt, :, ifreq, ipol] = full_uvh5.flag_array[
                        blt_idx, :, freq_idx, pol_idx
                    ]
                    nsamples[iblt, :, ifreq, ipol] = full_uvh5.nsample_array[
                        blt_idx, :, freq_idx, pol_idx
                    ]
    with uvtest.check_warnings(
        UserWarning,
        [
            "Selected frequencies are not evenly spaced",
            "Selected polarization values are not evenly spaced",
        ],
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile,
            data,
            flags,
            nsamples,
            blt_inds=blt_inds,
            freq_chans=freq_inds,
            polarizations=full_uvh5.polarization_array[pol_inds],
        )

    # also write the arrays to the partial object
    for iblt, blt_idx in enumerate(blt_inds):
        for ifreq, freq_idx in enumerate(freq_inds):
            for ipol, pol_idx in enumerate(pol_inds):
                if future_shapes:
                    partial_uvh5.data_array[blt_idx, freq_idx, pol_idx] = data[
                        iblt, ifreq, ipol
                    ]
                    partial_uvh5.flag_array[blt_idx, freq_idx, pol_idx] = flags[
                        iblt, ifreq, ipol
                    ]
                    partial_uvh5.nsample_array[blt_idx, freq_idx, pol_idx] = nsamples[
                        iblt, ifreq, ipol
                    ]
                else:
                    partial_uvh5.data_array[blt_idx, :, freq_idx, pol_idx] = data[
                        iblt, :, ifreq, ipol
                    ]
                    partial_uvh5.flag_array[blt_idx, :, freq_idx, pol_idx] = flags[
                        iblt, :, ifreq, ipol
                    ]
                    partial_uvh5.nsample_array[
                        blt_idx, :, freq_idx, pol_idx
                    ] = nsamples[iblt, :, ifreq, ipol]

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_partial_write_errors(uv_partial_write, tmp_path):
    """
    Test errors in uvh5_write_part method.
    """
    full_uvh5 = uv_partial_write
    partial_uvh5 = UVData()

    # get a waterfall
    antpairpols = full_uvh5.get_antpairpols()
    key = antpairpols[0]
    data = full_uvh5.get_data(key, squeeze="none")
    flags = full_uvh5.get_data(key, squeeze="none")
    nsamples = full_uvh5.get_data(key, squeeze="none")

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # try to write to a file that doesn't exists
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    if os.path.exists(partial_testfile):
        os.remove(partial_testfile)
    with pytest.raises(
        AssertionError, match=re.escape(f"{partial_testfile} does not exist")
    ):
        partial_uvh5.write_uvh5_part(partial_testfile, data, flags, nsamples, bls=key)

    # initialize file on disk
    partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=True)

    # pass in arrays that are different sizes
    with pytest.raises(
        AssertionError, match="data_array and flag_array must have the same shape"
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile, data, flags[:, :, 0], nsamples, bls=key
        )

    with pytest.raises(
        AssertionError, match="data_array and nsample_array must have the same shape"
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile, data, flags, nsamples[:, :, 0], bls=key
        )

    # pass in arrays that are the same size, but don't match expected shape
    with pytest.raises(AssertionError, match="data_array has shape"):
        partial_uvh5.write_uvh5_part(
            partial_testfile, data[:, :, 0], flags[:, :, 0], nsamples[:, :, 0]
        )

    # initialize a file on disk, and pass in a different object so check_header fails
    small_uvd = full_uvh5.select(
        freq_chans=np.arange(full_uvh5.Nfreqs // 2), inplace=False
    )
    with pytest.raises(
        AssertionError,
        match="The object metadata in memory and metadata on disk are different",
    ):
        small_uvd.write_uvh5_part(partial_testfile, data, flags, nsamples, bls=key)

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_initialize_uvh5_file(uv_partial_write, future_shapes, tmp_path):
    """
    Test initializing a UVH5 file on disk.
    """
    full_uvh5 = uv_partial_write
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    full_uvh5.data_array = None
    full_uvh5.flag_array = None
    full_uvh5.nsample_array = None

    # initialize file
    partial_uvh5 = full_uvh5.copy()
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=True)

    # read it in and make sure that the metadata matches the original
    partial_uvh5.read(
        partial_testfile, read_data=False, use_future_array_shapes=future_shapes
    )

    # make sure filenames are what we expect
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    assert full_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5.filename = full_uvh5.filename

    assert partial_uvh5 == full_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_initialize_uvh5_file_errors(uv_partial_write, tmp_path, capsys):
    """
    Test errors in initializing a UVH5 file on disk.
    """
    full_uvh5 = uv_partial_write
    full_uvh5.data_array = None
    full_uvh5.flag_array = None
    full_uvh5.nsample_array = None

    # initialize file
    partial_uvh5 = full_uvh5.copy()
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=True)

    # check that IOError is raised then when clobber == False
    with pytest.raises(IOError) as cm:
        partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=False)
    assert str(cm.value).startswith("File exists; skipping")

    # check we can write to it anyway
    partial_uvh5.initialize_uvh5_file(partial_testfile, clobber=True)
    captured = capsys.readouterr()
    assert captured.out.startswith("File exists; clobbering")
    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_initialize_uvh5_file_compression_opts(uv_partial_write, tmp_path):
    """
    Test initializing a uvh5 file with compression options.
    """
    full_uvh5 = uv_partial_write
    full_uvh5.data_array = None
    full_uvh5.flag_array = None
    full_uvh5.nsample_array = None

    # add options for compression
    partial_uvh5 = full_uvh5.copy()
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(
        partial_testfile,
        clobber=True,
        data_compression="lzf",
        flags_compression=None,
        nsample_compression=None,
    )
    partial_uvh5.read(partial_testfile, read_data=False, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    assert full_uvh5.filename == ["outtest.uvh5"]
    partial_uvh5.filename = full_uvh5.filename

    assert partial_uvh5 == full_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_lst_array(casa_uvfits, tmp_path):
    """
    Test different cases of the lst_array.
    """
    uv_in = casa_uvfits
    uv_out = UVData()
    testfile = str(tmp_path / "outtest_uvfits.uvh5")
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)

    # remove lst_array from file; check that it's correctly computed on read
    with h5py.File(testfile, "r+") as h5f:
        del h5f["/Header/lst_array"]
    uv_out.read_uvh5(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_uvfits.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # now change what's in the file and make sure a warning is raised
    uv_in.write_uvh5(testfile, clobber=True)
    with h5py.File(testfile, "r+") as h5f:
        lst_array = h5f["/Header/lst_array"][:]
        del h5f["/Header/lst_array"]
        h5f["/Header/lst_array"] = 2 * lst_array
    with uvtest.check_warnings(
        UserWarning,
        [
            f"LST values stored in {testfile} are not self-consistent",
            "The uvw_array does not match the expected values given the antenna "
            "positions.",
        ],
    ):
        uv_out.read_uvh5(testfile, use_future_array_shapes=True)
    uv_out.lst_array = lst_array
    assert uv_in == uv_out

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_uvh5_read_header_special_cases(casa_uvfits, tmp_path):
    """
    Test special cases values when reading files.
    """
    uv_in = casa_uvfits
    uv_out = UVData()
    testfile = str(tmp_path / "outtest_uvfits.uvh5")
    uv_in.telescope_name = "PAPER"
    uv_in.write_uvh5(testfile, clobber=True)
    # change some of the metadata to trip certain if/else clauses
    with h5py.File(testfile, "r+") as h5f:
        del h5f["Header/history"]
        del h5f["Header/vis_units"]
        del h5f["Header/phase_center_catalog"]
        h5f["Header/history"] = np.string_("blank history")
        h5f["Header/phase_type"] = np.string_("blah")
    with uvtest.check_warnings(
        UserWarning,
        [
            "Unknown phase types are no longer",
            "The uvw_array does not match the expected values",
        ],
    ):
        uv_out.read_uvh5(testfile, use_future_array_shapes=True)

    # make input and output values match now
    uv_in.history = uv_out.history
    uv_in.phase_center_catalog = uv_out.phase_center_catalog
    uv_in.vis_units = "uncalib"

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_uvfits.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # clean up
    os.remove(testfile)

    return


def test_uvh5_read_ints(uv_uvh5, tmp_path):
    """
    Test reading visibility data saved as integers.
    """
    uv_in = uv_uvh5
    uv_out = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    uv_in.write_uvh5(testfile, clobber=True)

    # read it back in to make sure data is the same
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2458432.34569.uvh5"]
    assert uv_out.filename == ["outtest.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # now read in as np.complex128
    uvh5_filename = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    uv_in.read_uvh5(
        uvh5_filename, data_array_dtype=np.complex128, use_future_array_shapes=True
    )

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2458432.34569.uvh5"]
    assert uv_out.filename == ["outtest.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out
    assert uv_in.data_array.dtype == np.dtype(np.complex128)

    # clean up
    os.remove(testfile)

    return


def test_uvh5_read_ints_error():
    """
    Test raising an error for passing in an unsupported data_array dtype.
    """
    uv_in = UVData()
    uvh5_filename = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")

    # raise error for bogus data_array_dtype
    with pytest.raises(
        ValueError, match="data_array_dtype must be np.complex64 or np.complex128"
    ):
        uv_in.read_uvh5(
            uvh5_filename, data_array_dtype=np.int32, use_future_array_shapes=True
        )

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_write_ints(uv_uvh5, future_shapes, tmp_path):
    """
    Test writing visibility data as integers.
    """
    uv_in = uv_uvh5
    if not future_shapes:
        uv_in.use_current_array_shapes()

    uv_out = UVData()
    testfile = str(tmp_path / "outtest.uvh5")
    uv_in.write_uvh5(testfile, clobber=True, data_write_dtype=uvh5._hera_corr_dtype)

    # read it back in to make sure data is the same
    uv_out.read(testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert uv_in.filename == ["zen.2458432.34569.uvh5"]
    assert uv_out.filename == ["outtest.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # also check that the datatype on disk is the right type
    with h5py.File(testfile, "r") as h5f:
        visdata_dtype = h5f["Data/visdata"].dtype
        assert "r" in visdata_dtype.names
        assert "i" in visdata_dtype.names
        assert visdata_dtype["r"].kind == "i"
        assert visdata_dtype["i"].kind == "i"

    # clean up
    os.remove(testfile)

    return


def test_uvh5_partial_read_ints_antennas():
    """
    Test reading in only some antennas from disk with integer data type.
    """
    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    uvh5_file = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    # This file has weird telescope or antenna location information
    # (not on the surface of the earth)
    # which breaks the phasing when trying to check if the uvws match the antpos.

    # select on antennas
    ants_to_keep = np.array([0, 1])
    uvh5_uv.read(uvh5_file, antenna_nums=ants_to_keep, use_future_array_shapes=True)
    uvh5_uv2.read(uvh5_file, use_future_array_shapes=True)
    uvh5_uv2.select(antenna_nums=ants_to_keep)
    assert uvh5_uv == uvh5_uv2

    return


def test_uvh5_partial_read_ints_freqs():
    """
    Test reading in only some frequencies from disk with integer data type.
    """
    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    uvh5_file = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    # This file has weird telescope or antenna location information
    # (not on the surface of the earth)
    # which breaks the phasing when trying to check if the uvws match the antpos.

    # select on frequency channels
    chans_to_keep = np.arange(12, 22)
    uvh5_uv.read(uvh5_file, freq_chans=chans_to_keep, use_future_array_shapes=True)
    uvh5_uv2.read(uvh5_file, use_future_array_shapes=True)
    uvh5_uv2.select(freq_chans=chans_to_keep)
    assert uvh5_uv == uvh5_uv2

    return


def test_uvh5_partial_read_ints_pols():
    """
    Test reading in only some polarizations from disk with integer data type.
    """
    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    uvh5_file = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    # This file has weird telescope or antenna location information
    # (not on the surface of the earth)
    # which breaks the phasing when trying to check if the uvws match the antpos.

    # select on pols
    pols_to_keep = [-5, -6]
    uvh5_uv.read(uvh5_file, polarizations=pols_to_keep, use_future_array_shapes=True)
    uvh5_uv2.read(uvh5_file, use_future_array_shapes=True)
    uvh5_uv2.select(polarizations=pols_to_keep)
    assert uvh5_uv == uvh5_uv2

    return


def test_uvh5_partial_read_ints_times():
    """
    Test reading in only some times from disk with integer data type.
    """
    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    uvh5_file = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    # This file has weird telescope or antenna location information
    # (not on the surface of the earth)
    # which breaks the phasing when trying to check if the uvws match the antpos.

    # select on read using time_range
    uvh5_uv.read_uvh5(uvh5_file, read_data=False, use_future_array_shapes=True)
    unique_times = np.unique(uvh5_uv.time_array)
    uvh5_uv.read(
        uvh5_file,
        time_range=[unique_times[0], unique_times[1]],
        use_future_array_shapes=True,
    )
    uvh5_uv2.read(uvh5_file, use_future_array_shapes=True)
    uvh5_uv2.select(times=unique_times[0:2])
    assert uvh5_uv == uvh5_uv2

    return


def test_uvh5_partial_read_ints_multi1():
    """
    Test select-on-read for multiple axes, frequencies being smallest fraction.
    """
    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    uvh5_file = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    # This file has weird telescope or antenna location information
    # (not on the surface of the earth)
    # which breaks the phasing when trying to check if the uvws match the antpos.

    # read frequencies first
    ants_to_keep = np.array([0, 1])
    chans_to_keep = np.arange(12, 22)
    pols_to_keep = [-5, -6]
    uvh5_uv.read(
        uvh5_file,
        antenna_nums=ants_to_keep,
        freq_chans=chans_to_keep,
        polarizations=pols_to_keep,
        use_future_array_shapes=True,
    )
    uvh5_uv2.read(uvh5_file, use_future_array_shapes=True)
    uvh5_uv2.select(
        antenna_nums=ants_to_keep, freq_chans=chans_to_keep, polarizations=pols_to_keep
    )
    assert uvh5_uv == uvh5_uv2

    return


def test_uvh5_partial_read_ints_multi2():
    """
    Test select-on-read for multiple axes, baselines being smallest fraction.
    """
    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    uvh5_file = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    # This file has weird telescope or antenna location information
    # (not on the surface of the earth)
    # which breaks the phasing when trying to check if the uvws match the antpos.

    # read baselines first
    ants_to_keep = np.array([0, 1])
    chans_to_keep = np.arange(12, 22)
    pols_to_keep = [-5, -6, -7]
    uvh5_uv.read(
        uvh5_file,
        antenna_nums=ants_to_keep,
        freq_chans=chans_to_keep,
        polarizations=pols_to_keep,
        use_future_array_shapes=True,
    )
    uvh5_uv2.read(uvh5_file, use_future_array_shapes=True)
    uvh5_uv2.select(
        antenna_nums=ants_to_keep, freq_chans=chans_to_keep, polarizations=pols_to_keep
    )
    assert uvh5_uv == uvh5_uv2

    return


def test_uvh5_partial_read_ints_multi3():
    """
    Test select-on-read for multiple axes, polarizations being smallest fraction.
    """
    uvh5_uv = UVData()
    uvh5_uv2 = UVData()
    uvh5_file = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")
    # This file has weird telescope or antenna location information
    # (not on the surface of the earth)
    # which breaks the phasing when trying to check if the uvws match the antpos.

    # read polarizations first
    ants_to_keep = np.array([0, 1, 12])
    chans_to_keep = np.arange(12, 64)
    pols_to_keep = [-5, -6]
    uvh5_uv.read(
        uvh5_file,
        antenna_nums=ants_to_keep,
        freq_chans=chans_to_keep,
        polarizations=pols_to_keep,
        use_future_array_shapes=True,
    )
    uvh5_uv2.read(uvh5_file, use_future_array_shapes=True)
    uvh5_uv2.select(
        antenna_nums=ants_to_keep, freq_chans=chans_to_keep, polarizations=pols_to_keep
    )
    assert uvh5_uv == uvh5_uv2

    return


def test_uvh5_partial_write_ints_antpairs(uv_uvh5, tmp_path):
    """
    Test writing an entire UVH5 file in pieces by antpairs using ints.
    """
    full_uvh5 = uv_uvh5

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(
        partial_testfile, clobber=True, data_write_dtype=uvh5._hera_corr_dtype
    )

    # write to file by iterating over antpairpol
    antpairpols = full_uvh5.get_antpairpols()
    for key in antpairpols:
        data = full_uvh5.get_data(key, squeeze="none")
        flags = full_uvh5.get_flags(key, squeeze="none")
        nsamples = full_uvh5.get_nsamples(key, squeeze="none")
        partial_uvh5.write_uvh5_part(partial_testfile, data, flags, nsamples, bls=key)

    # now read in the full file and make sure that it matches the original
    partial_uvh5.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert full_uvh5.filename == ["zen.2458432.34569.uvh5"]
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    full_uvh5.filename = partial_uvh5.filename

    assert full_uvh5 == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


def test_uvh5_partial_write_ints_frequencies(uv_uvh5, tmp_path):
    """
    Test writing an entire UVH5 file in pieces by frequency using ints.
    """
    full_uvh5 = uv_uvh5

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(
        partial_testfile, clobber=True, data_write_dtype=uvh5._hera_corr_dtype
    )

    # only write certain frequencies
    Nfreqs = full_uvh5.Nfreqs
    Hfreqs = Nfreqs // 2
    freqs1 = np.arange(Hfreqs)
    freqs2 = np.arange(Hfreqs, Nfreqs)
    data = full_uvh5.data_array[:, freqs1, :]
    flags = full_uvh5.flag_array[:, freqs1, :]
    nsamples = full_uvh5.nsample_array[:, freqs1, :]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, freq_chans=freqs1
    )
    data = full_uvh5.data_array[:, freqs2, :]
    flags = full_uvh5.flag_array[:, freqs2, :]
    nsamples = full_uvh5.nsample_array[:, freqs2, :]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, freq_chans=freqs2
    )

    # read in the full file and make sure it matches
    partial_uvh5.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert full_uvh5.filename == ["zen.2458432.34569.uvh5"]
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    full_uvh5.filename = partial_uvh5.filename

    assert full_uvh5 == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


def test_uvh5_partial_write_ints_blts(uv_uvh5, tmp_path):
    """
    Test writing an entire UVH5 file in pieces by blt using ints.
    """
    full_uvh5 = uv_uvh5

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(
        partial_testfile, clobber=True, data_write_dtype=uvh5._hera_corr_dtype
    )

    # only write certain blts
    Nblts = full_uvh5.Nblts
    Hblts = Nblts // 2
    blts1 = np.arange(Hblts)
    blts2 = np.arange(Hblts, Nblts)
    data = full_uvh5.data_array[blts1]
    flags = full_uvh5.flag_array[blts1]
    nsamples = full_uvh5.nsample_array[blts1]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, blt_inds=blts1
    )
    data = full_uvh5.data_array[blts2]
    flags = full_uvh5.flag_array[blts2]
    nsamples = full_uvh5.nsample_array[blts2]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, blt_inds=blts2
    )

    # read in the full file and make sure it matches
    partial_uvh5.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert full_uvh5.filename == ["zen.2458432.34569.uvh5"]
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    full_uvh5.filename = partial_uvh5.filename

    assert full_uvh5 == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


def test_uvh5_partial_write_ints_pols(uv_uvh5, tmp_path):
    """
    Test writing an entire UVH5 file in pieces by polarization using ints.
    """
    full_uvh5 = uv_uvh5

    # delete data arrays in partial file
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    partial_uvh5.initialize_uvh5_file(
        partial_testfile, clobber=True, data_write_dtype=uvh5._hera_corr_dtype
    )

    # only write certain polarizations
    Npols = full_uvh5.Npols
    Hpols = Npols // 2
    pols1 = np.arange(Hpols)
    pols2 = np.arange(Hpols, Npols)
    data = full_uvh5.data_array[:, :, pols1]
    flags = full_uvh5.flag_array[:, :, pols1]
    nsamples = full_uvh5.nsample_array[:, :, pols1]
    partial_uvh5.write_uvh5_part(
        partial_testfile,
        data,
        flags,
        nsamples,
        polarizations=full_uvh5.polarization_array[:Hpols],
    )
    data = full_uvh5.data_array[:, :, pols2]
    flags = full_uvh5.flag_array[:, :, pols2]
    nsamples = full_uvh5.nsample_array[:, :, pols2]
    partial_uvh5.write_uvh5_part(
        partial_testfile,
        data,
        flags,
        nsamples,
        polarizations=full_uvh5.polarization_array[Hpols:],
    )

    # read in the full file and make sure it matches
    partial_uvh5.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert full_uvh5.filename == ["zen.2458432.34569.uvh5"]
    assert partial_uvh5.filename == ["outtest_partial.uvh5"]
    full_uvh5.filename = partial_uvh5.filename

    assert full_uvh5 == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


def test_read_complex_astype(tmp_path):
    # make a testfile with a test dataset
    test_file = str(tmp_path / "test_file.h5")
    test_data_shape = (2, 3, 4, 5)
    test_data = np.zeros(test_data_shape, dtype=np.complex64)
    test_data.real = 1.0
    test_data.imag = 2.0
    with h5py.File(test_file, "w") as h5f:
        dgrp = h5f.create_group("Data")
        dset = dgrp.create_dataset(
            "testdata", test_data_shape, dtype=uvh5._hera_corr_dtype
        )
        dset[:, :, :, :, "r"] = test_data.real
        dset[:, :, :, :, "i"] = test_data.imag

    # test that reading the data back in works as expected
    indices = (np.s_[:], np.s_[:], np.s_[:], np.s_[:])
    with h5py.File(test_file, "r") as h5f:
        dset = h5f["Data/testdata"]
        file_data = uvh5._read_complex_astype(dset, indices, np.complex64)

    assert np.allclose(file_data, test_data)

    # clean up
    os.remove(test_file)

    return


def test_read_complex_astype_errors(tmp_path):
    # make a testfile with a test dataset
    test_file = str(tmp_path / "test_file.h5")
    test_data_shape = (2, 3, 4, 5)
    test_data = np.zeros(test_data_shape, dtype=np.complex64)
    test_data.real = 1.0
    test_data.imag = 2.0
    with h5py.File(test_file, "w") as h5f:
        dgrp = h5f.create_group("Data")
        dset = dgrp.create_dataset(
            "testdata", test_data_shape, dtype=uvh5._hera_corr_dtype
        )
        dset[:, :, :, :, "r"] = test_data.real
        dset[:, :, :, :, "i"] = test_data.imag

    # test passing in a forbidden output datatype
    indices = (np.s_[:], np.s_[:], np.s_[:], np.s_[:])
    with h5py.File(test_file, "r") as h5f:
        dset = h5f["Data/testdata"]
        with pytest.raises(ValueError) as cm:
            uvh5._read_complex_astype(dset, indices, np.int32)
        assert str(cm.value).startswith("output datatype must be one of (complex")

    # clean up
    os.remove(test_file)

    return


def test_write_complex_astype(tmp_path):
    # make sure we can write data out
    test_file = str(tmp_path / "test_file.h5")
    test_data_shape = (2, 3, 4, 5)
    test_data = np.zeros(test_data_shape, dtype=np.complex64)
    test_data.real = 1.0
    test_data.imag = 2.0
    with h5py.File(test_file, "w") as h5f:
        dgrp = h5f.create_group("Data")
        dset = dgrp.create_dataset(
            "testdata", test_data_shape, dtype=uvh5._hera_corr_dtype
        )
        inds = (np.s_[:], np.s_[:], np.s_[:], np.s_[:])
        uvh5._write_complex_astype(test_data, dset, inds)

    # read the data back in to confirm it's right
    with h5py.File(test_file, "r") as h5f:
        dset = h5f["Data/testdata"]
        file_data = np.zeros(test_data_shape, dtype=np.complex64)
        file_data.real = dset.astype(uvh5._hera_corr_dtype)["r"][:, :, :, :]
        file_data.imag = dset.astype(uvh5._hera_corr_dtype)["i"][:, :, :, :]

    assert np.allclose(file_data, test_data)

    return


def test_check_uvh5_dtype_errors():
    # test passing in something that's not a dtype
    with pytest.raises(ValueError) as cm:
        uvh5._check_uvh5_dtype("hi")
    assert str(cm.value).startswith("dtype in a uvh5 file must be a numpy dtype")

    # test using a dtype with bad field names
    dtype = np.dtype([("a", "<i4"), ("b", "<i4")])
    with pytest.raises(ValueError) as cm:
        uvh5._check_uvh5_dtype(dtype)
    assert str(cm.value).startswith("dtype must be a compound datatype")

    # test having different types for 'r' and 'i' fields
    dtype = np.dtype([("r", "<i4"), ("i", "<f4")])
    with pytest.raises(ValueError) as cm:
        uvh5._check_uvh5_dtype(dtype)
    assert str(cm.value).startswith("dtype must have the same kind")

    return


def test_uvh5_partial_write_ints_irregular_blt(uv_uvh5, tmp_path):
    """
    Test writing a uvh5 file using irregular interval for blt and integer dtype.
    """
    full_uvh5 = uv_uvh5
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros_ints(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # write a single blt to file
    blt_inds = np.arange(1)
    data = full_uvh5.data_array[blt_inds]
    flags = full_uvh5.flag_array[blt_inds]
    nsamples = full_uvh5.nsample_array[blt_inds]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, blt_inds=blt_inds
    )

    # also write the arrays to the partial object
    partial_uvh5.data_array[blt_inds] = data
    partial_uvh5.flag_array[blt_inds] = flags
    partial_uvh5.nsample_array[blt_inds] = nsamples

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    # read in the full file and make sure it matches
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["zen.2458432.34569.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


def test_uvh5_partial_write_ints_irregular_freq(uv_uvh5, tmp_path):
    """
    Test writing a uvh5 file using irregular interval for freq and integer dtype.
    """
    full_uvh5 = uv_uvh5
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros_ints(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # write a single freq to file
    freq_inds = np.arange(1)
    data = full_uvh5.data_array[:, freq_inds, :]
    flags = full_uvh5.flag_array[:, freq_inds, :]
    nsamples = full_uvh5.nsample_array[:, freq_inds, :]
    partial_uvh5.write_uvh5_part(
        partial_testfile, data, flags, nsamples, freq_chans=freq_inds
    )

    # also write the arrays to the partial object
    partial_uvh5.data_array[:, freq_inds, :] = data
    partial_uvh5.flag_array[:, freq_inds, :] = flags
    partial_uvh5.nsample_array[:, freq_inds, :] = nsamples

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    # read in the full file and make sure it matches
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["zen.2458432.34569.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


def test_uvh5_partial_write_ints_irregular_pol(uv_uvh5, tmp_path):
    """
    Test writing a uvh5 file using irregular interval for pol and integer dtype.
    """
    full_uvh5 = uv_uvh5
    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros_ints(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # write a single pol to file
    pol_inds = np.arange(1)
    data = full_uvh5.data_array[:, :, pol_inds]
    flags = full_uvh5.flag_array[:, :, pol_inds]
    nsamples = full_uvh5.nsample_array[:, :, pol_inds]
    partial_uvh5.write_uvh5_part(
        partial_testfile,
        data,
        flags,
        nsamples,
        polarizations=partial_uvh5.polarization_array[pol_inds],
    )

    # also write the arrays to the partial object
    partial_uvh5.data_array[:, :, pol_inds] = data
    partial_uvh5.flag_array[:, :, pol_inds] = flags
    partial_uvh5.nsample_array[:, :, pol_inds] = nsamples

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    # read in the full file and make sure it matches
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["zen.2458432.34569.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_ints_irregular_multi1(uv_uvh5, future_shapes, tmp_path):
    """
    Test writing a uvh5 file using irregular interval for blt and freq and
    integer dtype.
    """
    full_uvh5 = uv_uvh5
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros_ints(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # define blts and freqs
    blt_inds = [0, 1, 2, 7]
    freq_inds = [0, 2, 3, 4]
    if future_shapes:
        data_shape = (len(blt_inds), len(freq_inds), full_uvh5.Npols)
    else:
        data_shape = (len(blt_inds), 1, len(freq_inds), full_uvh5.Npols)
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    for iblt, blt_idx in enumerate(blt_inds):
        for ifreq, freq_idx in enumerate(freq_inds):
            if future_shapes:
                data[iblt, ifreq, :] = full_uvh5.data_array[blt_idx, freq_idx, :]
                flags[iblt, ifreq, :] = full_uvh5.flag_array[blt_idx, freq_idx, :]
                nsamples[iblt, ifreq, :] = full_uvh5.nsample_array[blt_idx, freq_idx, :]
            else:
                data[iblt, :, ifreq, :] = full_uvh5.data_array[blt_idx, :, freq_idx, :]
                flags[iblt, :, ifreq, :] = full_uvh5.flag_array[blt_idx, :, freq_idx, :]
                nsamples[iblt, :, ifreq, :] = full_uvh5.nsample_array[
                    blt_idx, :, freq_idx, :
                ]
    with uvtest.check_warnings(
        UserWarning, "Selected frequencies are not evenly spaced"
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile,
            data,
            flags,
            nsamples,
            blt_inds=blt_inds,
            freq_chans=freq_inds,
        )

    # also write the arrays to the partial object
    for iblt, blt_idx in enumerate(blt_inds):
        for ifreq, freq_idx in enumerate(freq_inds):
            if future_shapes:
                partial_uvh5.data_array[blt_idx, freq_idx, :] = data[iblt, ifreq, :]
                partial_uvh5.flag_array[blt_idx, freq_idx, :] = flags[iblt, ifreq, :]
                partial_uvh5.nsample_array[blt_idx, freq_idx, :] = nsamples[
                    iblt, ifreq, :
                ]
            else:
                partial_uvh5.data_array[blt_idx, :, freq_idx, :] = data[
                    iblt, :, ifreq, :
                ]
                partial_uvh5.flag_array[blt_idx, :, freq_idx, :] = flags[
                    iblt, :, ifreq, :
                ]
                partial_uvh5.nsample_array[blt_idx, :, freq_idx, :] = nsamples[
                    iblt, :, ifreq, :
                ]

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    # read in the full file and make sure it matches
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["zen.2458432.34569.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_ints_irregular_multi2(uv_uvh5, future_shapes, tmp_path):
    """
    Test writing a uvh5 file using irregular interval for freq and pol and
    integer dtype.
    """
    full_uvh5 = uv_uvh5
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros_ints(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # define freqs and pols
    freq_inds = [0, 1, 2, 7]
    pol_inds = [0, 1, 3]
    if future_shapes:
        data_shape = (full_uvh5.Nblts, len(freq_inds), len(pol_inds))
    else:
        data_shape = (full_uvh5.Nblts, 1, len(freq_inds), len(pol_inds))
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    for ifreq, freq_idx in enumerate(freq_inds):
        for ipol, pol_idx in enumerate(pol_inds):
            if future_shapes:
                data[:, ifreq, ipol] = full_uvh5.data_array[:, freq_idx, pol_idx]
                flags[:, ifreq, ipol] = full_uvh5.flag_array[:, freq_idx, pol_idx]
                nsamples[:, ifreq, ipol] = full_uvh5.nsample_array[:, freq_idx, pol_idx]
            else:
                data[:, :, ifreq, ipol] = full_uvh5.data_array[:, :, freq_idx, pol_idx]
                flags[:, :, ifreq, ipol] = full_uvh5.flag_array[:, :, freq_idx, pol_idx]
                nsamples[:, :, ifreq, ipol] = full_uvh5.nsample_array[
                    :, :, freq_idx, pol_idx
                ]
    with uvtest.check_warnings(
        UserWarning,
        [
            "Selected frequencies are not evenly spaced",
            "Selected polarization values are not evenly spaced",
        ],
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile,
            data,
            flags,
            nsamples,
            freq_chans=freq_inds,
            polarizations=full_uvh5.polarization_array[pol_inds],
        )

    # also write the arrays to the partial object
    for ifreq, freq_idx in enumerate(freq_inds):
        for ipol, pol_idx in enumerate(pol_inds):
            if future_shapes:
                partial_uvh5.data_array[:, freq_idx, pol_idx] = data[:, ifreq, ipol]
                partial_uvh5.flag_array[:, freq_idx, pol_idx] = flags[:, ifreq, ipol]
                partial_uvh5.nsample_array[:, freq_idx, pol_idx] = nsamples[
                    :, ifreq, ipol
                ]
            else:
                partial_uvh5.data_array[:, :, freq_idx, pol_idx] = data[
                    :, :, ifreq, ipol
                ]
                partial_uvh5.flag_array[:, :, freq_idx, pol_idx] = flags[
                    :, :, ifreq, ipol
                ]
                partial_uvh5.nsample_array[:, :, freq_idx, pol_idx] = nsamples[
                    :, :, ifreq, ipol
                ]

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    # read in the full file and make sure it matches
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["zen.2458432.34569.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_ints_irregular_multi3(uv_uvh5, future_shapes, tmp_path):
    """
    Test writing a uvh5 file using irregular interval for blt and pol and integer dtype.
    """
    full_uvh5 = uv_uvh5
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros_ints(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # define blts and pols
    blt_inds = [0, 1, 2, 7]
    pol_inds = [0, 1, 3]
    if future_shapes:
        data_shape = (len(blt_inds), full_uvh5.Nfreqs, len(pol_inds))
    else:
        data_shape = (len(blt_inds), 1, full_uvh5.Nfreqs, len(pol_inds))
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    for iblt, blt_idx in enumerate(blt_inds):
        for ipol, pol_idx in enumerate(pol_inds):
            if future_shapes:
                data[iblt, :, ipol] = full_uvh5.data_array[blt_idx, :, pol_idx]
                flags[iblt, :, ipol] = full_uvh5.flag_array[blt_idx, :, pol_idx]
                nsamples[iblt, :, ipol] = full_uvh5.nsample_array[blt_idx, :, pol_idx]
            else:
                data[iblt, :, :, ipol] = full_uvh5.data_array[blt_idx, :, :, pol_idx]
                flags[iblt, :, :, ipol] = full_uvh5.flag_array[blt_idx, :, :, pol_idx]
                nsamples[iblt, :, :, ipol] = full_uvh5.nsample_array[
                    blt_idx, :, :, pol_idx
                ]
    with uvtest.check_warnings(
        UserWarning, "Selected polarization values are not evenly spaced"
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile,
            data,
            flags,
            nsamples,
            blt_inds=blt_inds,
            polarizations=full_uvh5.polarization_array[pol_inds],
        )

    # also write the arrays to the partial object
    for iblt, blt_idx in enumerate(blt_inds):
        for ipol, pol_idx in enumerate(pol_inds):
            if future_shapes:
                partial_uvh5.data_array[blt_idx, :, pol_idx] = data[iblt, :, ipol]
                partial_uvh5.flag_array[blt_idx, :, pol_idx] = flags[iblt, :, ipol]
                partial_uvh5.nsample_array[blt_idx, :, pol_idx] = nsamples[
                    iblt, :, ipol
                ]
            else:
                partial_uvh5.data_array[blt_idx, :, :, pol_idx] = data[iblt, :, :, ipol]
                partial_uvh5.flag_array[blt_idx, :, :, pol_idx] = flags[
                    iblt, :, :, ipol
                ]
                partial_uvh5.nsample_array[blt_idx, :, :, pol_idx] = nsamples[
                    iblt, :, :, ipol
                ]

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    # read in the full file and make sure it matches
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["zen.2458432.34569.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:" + _future_array_shapes_warning)
@pytest.mark.filterwarnings("ignore:This method will be removed in version 3.0 when")
@pytest.mark.parametrize("future_shapes", [True, False])
def test_uvh5_partial_write_ints_irregular_multi4(uv_uvh5, future_shapes, tmp_path):
    """
    Test writing a uvh5 file using irregular interval for all axes and integer dtype.
    """
    full_uvh5 = uv_uvh5
    if not future_shapes:
        full_uvh5.use_current_array_shapes()

    partial_uvh5 = full_uvh5.copy()
    partial_uvh5.data_array = None
    partial_uvh5.flag_array = None
    partial_uvh5.nsample_array = None

    # initialize file on disk
    partial_testfile = str(tmp_path / "outtest_partial.uvh5")
    initialize_with_zeros_ints(partial_uvh5, partial_testfile)

    # make a mostly empty object in memory to match what we'll write to disk
    partial_uvh5.data_array = np.zeros_like(full_uvh5.data_array, dtype=np.complex64)
    partial_uvh5.flag_array = np.zeros_like(full_uvh5.flag_array, dtype=np.bool_)
    partial_uvh5.nsample_array = np.zeros_like(
        full_uvh5.nsample_array, dtype=np.float32
    )

    # define blts and freqs
    blt_inds = [0, 1, 2, 7]
    freq_inds = [0, 2, 3, 4]
    pol_inds = [0, 1, 3]
    if future_shapes:
        data_shape = (len(blt_inds), len(freq_inds), len(pol_inds))
    else:
        data_shape = (len(blt_inds), 1, len(freq_inds), len(pol_inds))
    data = np.zeros(data_shape, dtype=np.complex64)
    flags = np.zeros(data_shape, dtype=np.bool_)
    nsamples = np.zeros(data_shape, dtype=np.float32)
    for iblt, blt_idx in enumerate(blt_inds):
        for ifreq, freq_idx in enumerate(freq_inds):
            for ipol, pol_idx in enumerate(pol_inds):
                if future_shapes:
                    data[iblt, ifreq, ipol] = full_uvh5.data_array[
                        blt_idx, freq_idx, pol_idx
                    ]
                    flags[iblt, ifreq, ipol] = full_uvh5.flag_array[
                        blt_idx, freq_idx, pol_idx
                    ]
                    nsamples[iblt, ifreq, ipol] = full_uvh5.nsample_array[
                        blt_idx, freq_idx, pol_idx
                    ]
                else:
                    data[iblt, :, ifreq, ipol] = full_uvh5.data_array[
                        blt_idx, :, freq_idx, pol_idx
                    ]
                    flags[iblt, :, ifreq, ipol] = full_uvh5.flag_array[
                        blt_idx, :, freq_idx, pol_idx
                    ]
                    nsamples[iblt, :, ifreq, ipol] = full_uvh5.nsample_array[
                        blt_idx, :, freq_idx, pol_idx
                    ]
    with uvtest.check_warnings(
        UserWarning,
        [
            "Selected frequencies are not evenly spaced",
            "Selected polarization values are not evenly spaced",
        ],
    ):
        partial_uvh5.write_uvh5_part(
            partial_testfile,
            data,
            flags,
            nsamples,
            blt_inds=blt_inds,
            freq_chans=freq_inds,
            polarizations=full_uvh5.polarization_array[pol_inds],
        )

    # also write the arrays to the partial object
    for iblt, blt_idx in enumerate(blt_inds):
        for ifreq, freq_idx in enumerate(freq_inds):
            for ipol, pol_idx in enumerate(pol_inds):
                if future_shapes:
                    partial_uvh5.data_array[blt_idx, freq_idx, pol_idx] = data[
                        iblt, ifreq, ipol
                    ]
                    partial_uvh5.flag_array[blt_idx, freq_idx, pol_idx] = flags[
                        iblt, ifreq, ipol
                    ]
                    partial_uvh5.nsample_array[blt_idx, freq_idx, pol_idx] = nsamples[
                        iblt, ifreq, ipol
                    ]
                else:
                    partial_uvh5.data_array[blt_idx, :, freq_idx, pol_idx] = data[
                        iblt, :, ifreq, ipol
                    ]
                    partial_uvh5.flag_array[blt_idx, :, freq_idx, pol_idx] = flags[
                        iblt, :, ifreq, ipol
                    ]
                    partial_uvh5.nsample_array[
                        blt_idx, :, freq_idx, pol_idx
                    ] = nsamples[iblt, :, ifreq, ipol]

    # read in the file and make sure it matches
    partial_uvh5_file = UVData()
    # read in the full file and make sure it matches
    partial_uvh5_file.read(partial_testfile, use_future_array_shapes=future_shapes)

    # make sure filenames are what we expect
    assert partial_uvh5_file.filename == ["outtest_partial.uvh5"]
    assert partial_uvh5.filename == ["zen.2458432.34569.uvh5"]
    partial_uvh5_file.filename = partial_uvh5.filename

    assert partial_uvh5_file == partial_uvh5

    # clean up
    os.remove(partial_testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_antenna_names_not_list(casa_uvfits, tmp_path):
    """
    Test if antenna_names is cast to an array, dimensions are preserved in
    ``np.string_`` call during uvh5 write.
    """
    uv_in = casa_uvfits
    uv_out = UVData()
    testfile = str(tmp_path / "outtest_uvfits_ant_names.uvh5")

    # simulate a user defining antenna names as an array of unicode
    uv_in.antenna_names = np.array(uv_in.antenna_names, dtype="U")

    uv_in.write_uvh5(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # recast as list since antenna names should be a list and will be cast as
    # list on read
    uv_in.antenna_names = uv_in.antenna_names.tolist()

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_uvfits_ant_names.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_eq_coeffs_roundtrip(casa_uvfits, tmp_path):
    """Test reading and writing objects with eq_coeffs defined"""
    uv_in = casa_uvfits
    uv_out = UVData()
    testfile = str(tmp_path / "outtest_eq_coeffs.uvh5")
    uv_in.eq_coeffs = np.ones((uv_in.Nants_telescope, uv_in.Nfreqs))
    uv_in.eq_coeffs_convention = "divide"
    uv_in.write_uvh5(testfile, clobber=True)
    uv_out.read(testfile, use_future_array_shapes=True)

    # make sure filenames are what we expect
    assert uv_in.filename == ["day2_TDEM0003_10s_norx_1src_1spw.uvfits"]
    assert uv_out.filename == ["outtest_eq_coeffs.uvh5"]
    uv_in.filename = uv_out.filename

    assert uv_in == uv_out

    # clean up
    os.remove(testfile)

    return


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
def test_read_metadata(casa_uvfits, tmp_path):
    """Test misc properties of reading of metadata"""
    # test bytes metadata
    testfile = str(tmp_path / "metadata_read.uvh5")
    uv_in = casa_uvfits
    uv_in.write_uvh5(testfile, clobber=True)
    # alter to make some metadata bytes type
    with h5py.File(testfile, "r+") as f:
        tname = f["Header"]["telescope_name"][()]
        del f["Header"]["telescope_name"]
        f["Header"]["telescope_name"] = bytes(tname)
    # now read
    uv_out = UVData()
    uv_out.read(testfile, use_future_array_shapes=True)
    assert isinstance(uv_out.telescope_name, str)

    # clean up when done
    os.remove(testfile)


def test_cast_to_multiphase(uv_uvh5, tmp_path):
    """
    Test that round-tripping a UVH5 dataset after turning a single-phase-ctr
    data set into a multi-phase-ctr writes out correctly
    """
    test_uvh5 = UVData()
    testfile = os.path.join(tmp_path, "out_cast_to_multiphase.uvh5")

    uv_uvh5.write_uvh5(testfile)
    test_uvh5.read(testfile, use_future_array_shapes=True)

    assert test_uvh5 == uv_uvh5


@pytest.mark.filterwarnings("ignore:LST values stored ")
def test_old_phase_center_catalog_format(sma_mir, tmp_path):
    testfile = os.path.join(tmp_path, "outtest_old_pc_catalog.uvh5")
    sma_mir.write_uvh5(testfile)

    with h5py.File(testfile, "r+") as h5f:
        del h5f["/Header/phase_center_catalog"]
        header = h5f["/Header"]

        phase_dict = header.create_group("phase_center_catalog")
        for k in sma_mir.phase_center_catalog.keys():
            # Dictionary entries used to be written out as JSON-formatted strings.
            temp_dict = sma_mir.phase_center_catalog[k].copy()
            temp_dict["cat_id"] = k
            temp_name = temp_dict.pop("cat_name")
            phase_dict[temp_name] = np.string_(json.dumps(temp_dict))

    uvd = UVData.from_file(testfile, use_future_array_shapes=True)
    uvd.history = sma_mir.history
    assert uvd == sma_mir


@pytest.mark.filterwarnings("ignore:The uvw_array does not match the expected values")
@pytest.mark.filterwarnings("ignore:The entry name")
@pytest.mark.filterwarnings("ignore:The provided name")
def test_old_phase_attributes_header(casa_uvfits, tmp_path):
    testfile = os.path.join(tmp_path, "outtest_old_phase_attrs.uvh5")

    casa_uvfits.phase(
        lon=casa_uvfits.phase_center_catalog[0]["cat_lon"],
        lat=casa_uvfits.phase_center_catalog[0]["cat_lat"],
        phase_frame="icrs",
        cat_type="sidereal",
        cat_name=casa_uvfits.phase_center_catalog[0]["cat_name"],
    )
    old_phase_compatible, _ = casa_uvfits._old_phase_attributes_compatible()
    assert old_phase_compatible

    casa_uvfits.write_uvh5(testfile)

    phase_dict = list(casa_uvfits.phase_center_catalog.values())[0]
    with h5py.File(testfile, "r+") as h5f:
        del h5f["/Header/phase_center_catalog"]
        del h5f["/Header/phase_center_app_ra"]
        del h5f["/Header/phase_center_app_dec"]
        del h5f["/Header/phase_center_frame_pa"]
        header = h5f["/Header"]
        header["phase_type"] = np.bytes_("phased")
        header["object_name"] = phase_dict["cat_name"]
        header["phase_center_ra"] = phase_dict["cat_lon"]
        header["phase_center_dec"] = phase_dict["cat_lat"]
        header["phase_center_frame"] = np.bytes_(phase_dict["cat_frame"])
        header["phase_center_epoch"] = phase_dict["cat_epoch"]

    with uvtest.check_warnings(
        UserWarning,
        match=[
            "Telescope EVLA is not in known_telescopes.",
            "This data appears to have been phased-up using the old `phase` method, "
            "which is incompatible with the current set of methods. Please run the "
            "`fix_phase` method (or set `fix_old_proj=True` when loading the dataset) "
            "to address this issue.",
        ],
    ):
        uvd = UVData.from_file(
            testfile, fix_old_proj=False, use_future_array_shapes=True
        )
    uvd.history = casa_uvfits.history
    assert uvd == casa_uvfits

    with uvtest.check_warnings(
        UserWarning,
        match=[
            "Telescope EVLA is not in known_telescopes.",
            "Fixing phases using antenna positions.",
        ],
    ):
        uvd2 = UVData.from_file(testfile, use_future_array_shapes=True)

    with uvtest.check_warnings(
        UserWarning, match="Fixing phases using antenna positions."
    ):
        casa_uvfits.fix_phase(use_ant_pos=True)
    uvd2.history = casa_uvfits.history
    assert uvd2 == casa_uvfits


def test_none_extra_keywords(uv_uvh5, tmp_path):
    """Test that we can round-trip None values in extra_keywords"""
    test_uvh5 = UVData()
    testfile = os.path.join(tmp_path, "none_extra_keywords.uvh5")

    uv_uvh5.extra_keywords["foo"] = None

    uv_uvh5.write_uvh5(testfile)
    test_uvh5.read(testfile, use_future_array_shapes=True)

    assert test_uvh5 == uv_uvh5

    # also confirm dataset is empty/null
    with h5py.File(testfile, "r") as h5f:
        assert h5f["Header/extra_keywords/foo"].shape is None

    return


def test_write_uvh5_part_fix_autos(uv_uvh5, tmp_path):
    """Test that fix_autos works correctly on partial UVH5 wrute"""
    test_uvh5 = UVData()
    testfile = os.path.join(tmp_path, "write_uvh5_part_fix_autos.uvh5")

    # Select out the relevant data (where the 0 and 1 indicies of the pol array
    # correspond to xx and yy polarization data), and corrupt it accordingly
    auto_data = uv_uvh5.data_array[uv_uvh5.ant_1_array == uv_uvh5.ant_2_array]
    auto_data[:, :, [0, 1]] *= 1j
    uv_uvh5.data_array[uv_uvh5.ant_1_array == uv_uvh5.ant_2_array] = auto_data

    # Create and write out the data, with fix_autos set to operate
    initialize_with_zeros_ints(uv_uvh5, testfile)
    uv_uvh5.write_uvh5_part(
        testfile,
        uv_uvh5.data_array,
        uv_uvh5.flag_array,
        uv_uvh5.nsample_array,
        fix_autos=True,
    )

    # Fix the autos we corrupted earlier, and plug the data back in to data_array
    auto_data[:, :, [0, 1]] *= -1j
    uv_uvh5.data_array[uv_uvh5.ant_1_array == uv_uvh5.ant_2_array] = auto_data

    # Read in the data on disk, make sure it looks like our manually repaired data
    test_uvh5.read(testfile, use_future_array_shapes=True)

    assert uv_uvh5 == test_uvh5


def test_fix_autos_no_op():
    """Test that a no-op with _fix_autos returns a warning"""
    uvd = UVData()

    with uvtest.check_warnings(
        UserWarning, "Cannot use _fix_autos if ant_1_array, ant_2_array, or "
    ):
        uvd._fix_autos()


def test_uvh5_bitshuffle(uv_phase_comp, tmp_path):
    pytest.importorskip("hdf5plugin")

    uvd, _ = uv_phase_comp

    outfile = os.path.join(tmp_path, "test.uvh5")
    uvd.write_uvh5(outfile, data_compression="bitshuffle")

    with h5py.File(outfile, "r") as f:
        dgrp = f["/Data"]
        assert "32008" in dgrp["visdata"]._filters

    uvd2 = UVData.from_file(outfile, use_future_array_shapes=True)
    assert uvd == uvd2


@pytest.mark.usefixtures("tmp_path_factory")
@pytest.mark.usefixtures("sma_mir")
class TestFastUVH5Meta:
    def setup_class(self):
        self.fl = os.path.join(DATA_PATH, "zen.2458432.34569.uvh5")

        self.tmp_path = tempfile.TemporaryDirectory("fastuvh5meta")

        uvd = UVData()
        uvd.read(self.fl, bls=[(26, 26)], use_future_array_shapes=True)
        self.fl_singlebl = os.path.join(self.tmp_path.name, "singlebl.uvh5")
        uvd.write_uvh5(self.fl_singlebl)

        meta = uvh5.FastUVH5Meta(self.fl)
        uvd = meta.to_uvdata()
        uvd.reorder_blts(order="baseline", minor_order="time")
        self.fltime_axis_faster_than_bls = os.path.join(
            self.tmp_path.name, "time_axis_faster_than_bls.uvh5"
        )
        uvd.initialize_uvh5_file(self.fltime_axis_faster_than_bls, clobber=True)

    def teardown_class(self):
        self.tmp_path.cleanup()

    def test_input_file_type(self):
        uv1 = uvh5.FastUVH5Meta(self.fl)
        uv2 = uvh5.FastUVH5Meta(Path(self.fl))
        with h5py.File(self.fl, "r") as f:
            uv3 = uvh5.FastUVH5Meta(f)
            uv4 = uvh5.FastUVH5Meta(f["/Header"])

        assert uv1.antpairs == uv2.antpairs
        assert uv1.antpairs == uv3.antpairs
        assert uv1.antpairs == uv4.antpairs

    def test_closing(self):
        uv1 = uvh5.FastUVH5Meta(self.fl)
        file_id = uv1.header.file.id.id
        uv1.close()

        # After we close, it has to reopen and therefore has a differnt fid
        assert uv1.header.file.id.id != file_id

        assert bool(uv1.header.file)  # file is open
        flobj = uv1.header.file

        uv1.close()
        assert uv1.datagrp.file.id.id != file_id

        del uv1
        assert not bool(flobj)  # file is closed

    def test_get_blt_order(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        blt_order = meta.get_blt_order()
        assert blt_order == ("time",)
        assert meta.blt_order is None

        meta = uvh5.FastUVH5Meta(self.fl, blt_order="determine")
        assert meta.blt_order == ("time",)

        meta = uvh5.FastUVH5Meta(self.fl, blt_order=("time",))
        assert meta.blt_order == ("time",)

    def test_blts_rectangular(self):
        meta = uvh5.FastUVH5Meta(self.fl, blts_are_rectangular=None)
        assert meta.blts_are_rectangular

        meta = uvh5.FastUVH5Meta(self.fl, blts_are_rectangular=True)
        assert meta.blts_are_rectangular

        meta = uvh5.FastUVH5Meta(self.fl, blts_are_rectangular=False)
        assert not meta.blts_are_rectangular

    def test_time_axis_faster_than_bls(self):
        meta = uvh5.FastUVH5Meta(self.fl, time_axis_faster_than_bls=None)
        assert not meta.time_axis_faster_than_bls

        meta = uvh5.FastUVH5Meta(self.fl_singlebl)
        assert meta.time_axis_faster_than_bls

        meta = uvh5.FastUVH5Meta(self.fl_singlebl, blts_are_rectangular=True)
        assert meta.time_axis_faster_than_bls

        meta = uvh5.FastUVH5Meta(self.fl, blts_are_rectangular=False)
        assert not meta.time_axis_faster_than_bls

        meta1 = uvh5.FastUVH5Meta(self.fltime_axis_faster_than_bls)
        assert np.all(meta1.times == meta.times)
        assert meta1.time_axis_faster_than_bls

    def test_phase_type_with_pcc(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        assert meta.phase_type == "drift"

        uvd = meta.to_uvdata()
        uvd.initialize_uvh5_file(os.path.join(self.tmp_path.name, "new_pcc.uvh5"))

        meta1 = uvh5.FastUVH5Meta(os.path.join(self.tmp_path.name, "new_pcc.uvh5"))
        assert meta1.phase_center_catalog is not None
        assert meta1.phase_type == "drift"

    def test_getting_lsts(self):
        meta = uvh5.FastUVH5Meta(self.fl)

        shutil.copy(self.fl, os.path.join(self.tmp_path.name, "no_lsts.uvh5"))
        with h5py.File(os.path.join(self.tmp_path.name, "no_lsts.uvh5"), "r+") as f:
            del f["/Header/lst_array"]

        meta1 = uvh5.FastUVH5Meta(os.path.join(self.tmp_path.name, "no_lsts.uvh5"))
        assert np.allclose(meta1.lst_array, meta.lst_array)

        # Now test a different ordering.
        uvd = meta.to_uvdata()
        uvd.reorder_blts(order="baseline", minor_order="time")
        uvd.initialize_uvh5_file(
            os.path.join(self.tmp_path.name, "time_axis_faster_than_bls.uvh5"),
            clobber=True,
        )

        meta1 = uvh5.FastUVH5Meta(
            os.path.join(self.tmp_path.name, "time_axis_faster_than_bls.uvh5")
        )
        assert np.allclose(meta1.lsts, meta.lsts)

    def test_unique_arrays(self):
        def do_asserts(meta):
            assert set(meta.unique_antpair_1_array) == set(meta.ant_1_array)
            assert set(meta.unique_antpair_2_array) == set(meta.ant_2_array)
            assert set(meta.unique_baseline_array) == set(
                np.unique(meta.baseline_array)
            )
            assert set(meta.antpairs) == set(uvd.get_antpairs())
            assert meta.unique_ants == set(
                np.concatenate([meta.ant_1_array, meta.ant_2_array])
            )

        meta = uvh5.FastUVH5Meta(self.fl, blts_are_rectangular=False)
        uvd = meta.to_uvdata()
        do_asserts(meta)

        meta = uvh5.FastUVH5Meta(self.fl, blts_are_rectangular=True)
        do_asserts(meta)

        meta = uvh5.FastUVH5Meta(self.fltime_axis_faster_than_bls)
        do_asserts(meta)

    def test_has_key(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        assert meta.has_key((26, 26))
        assert not meta.has_key((150, 150))
        assert meta.has_key((0, 1))
        assert meta.has_key((1, 0))
        assert (1, 0) not in meta.antpairs

        assert meta.has_key((0, 1, "xy"))
        assert meta.has_key((1, 0, "yx"))

    def test_pols(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        assert meta.pols == ["xx", "yy", "xy", "yx"]

    def test_antpos_enu(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        uvd = meta.to_uvdata()
        assert np.allclose(meta.antpos_enu, uvd.get_ENU_antpos()[0])

    def test_phased_phase_type(self, sma_mir, tmp_path_factory):
        testdir = tmp_path_factory.mktemp("test_phased_phase_type")
        sma_mir.write_uvh5(os.path.join(testdir, "sma_mir.uvh5"))
        meta = uvh5.FastUVH5Meta(os.path.join(testdir, "sma_mir.uvh5"))
        assert meta.phase_type == "phased"

    def test_rectangularity_roundtrip(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        uvd = meta.to_uvdata()
        uvd.initialize_uvh5_file(
            os.path.join(self.tmp_path.name, "rectangular.uvh5"), clobber=True
        )
        meta1 = uvh5.FastUVH5Meta(os.path.join(self.tmp_path.name, "rectangular.uvh5"))
        assert meta1.blts_are_rectangular
        assert not meta1.time_axis_faster_than_bls

        # force them to be wrong!
        meta = uvh5.FastUVH5Meta(self.fl)
        uvd = meta.to_uvdata()
        uvd.blts_are_rectangular = False
        uvd.time_axis_faster_than_bls = False
        uvd.initialize_uvh5_file(
            os.path.join(self.tmp_path.name, "not_rectangular.uvh5"), clobber=True
        )
        meta1 = uvh5.FastUVH5Meta(
            os.path.join(self.tmp_path.name, "not_rectangular.uvh5")
        )
        assert not meta1.blts_are_rectangular
        assert not meta1.time_axis_faster_than_bls

    def test_recompute_nbls(self):
        meta = uvh5.FastUVH5Meta(self.fl, recompute_nbls=False)
        meta2 = uvh5.FastUVH5Meta(self.fl, recompute_nbls=True)

        nbls = meta.Nbls

        newfl = os.path.join(self.tmp_path.name, "wrong_Nbls.uvh5")
        uvd = meta.to_uvdata()
        uvd.Nbls = uvd.Nblts
        uvd.Ntimes = uvd.Nblts // nbls
        uvd.telescope_name = "HERA"
        uvd.initialize_uvh5_file(newfl, clobber=True)
        meta.close()

        meta3 = uvh5.FastUVH5Meta(newfl, recompute_nbls=None)

        assert meta.Nbls == meta2.Nbls == meta3.Nbls

        newfl = os.path.join(self.tmp_path.name, "not_hera.uvh5")
        uvd = meta.to_uvdata()
        uvd.telescope_name = "not-HERA"
        uvd.initialize_uvh5_file(newfl, clobber=True)
        meta.close()

        meta4 = uvh5.FastUVH5Meta(newfl, recompute_nbls=None)
        assert meta4.Nbls == meta.Nbls

        meta5 = uvh5.FastUVH5Meta(
            self.fl, recompute_nbls=True, blts_are_rectangular=True
        )
        assert meta5.Nbls == meta.Nbls

    def test_pickleability(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        meta2 = deepcopy(meta)

        assert meta == meta2

    def test_hashability(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        meta2 = uvh5.FastUVH5Meta(self.fltime_axis_faster_than_bls)

        assert meta != meta2
        dct = {meta: 1, meta2: 2}
        assert dct[meta] == 1

    def test_equality(self):
        meta = uvh5.FastUVH5Meta(self.fl)

        assert meta == meta
        assert meta != 1

    def test_transactional(self):
        meta = uvh5.FastUVH5Meta(self.fl)

        lsts = meta.get_transactional("lsts")
        assert not meta.is_open()
        assert np.allclose(lsts, meta.lsts)

        # This attribute uses __getattr__
        meta.get_transactional("freq_array", cache=False)
        assert not meta.is_open()
        assert "freq_array" not in meta.__dict__

        # This is cached property
        chwidth = meta.get_transactional("channel_width", cache=False)
        assert not meta.is_open()
        assert "channel_width" not in meta.__dict__
        assert np.allclose(chwidth, meta.channel_width)
        assert "channel_width" in meta.__dict__

    def test_close_before_open(self):
        meta = uvh5.FastUVH5Meta(self.fl)
        meta.close()
        assert not meta.is_open()
        assert isinstance(meta.header, h5py.Group)


# pyuvdata/uvdata/tests/test_initializers.py
"""Tests of in-memory initialization of UVData objects."""
from typing import Any

import numpy as np
import pytest
from astropy.coordinates import EarthLocation

from pyuvdata import UVData
from pyuvdata.uvdata.initializers import (
    configure_blt_rectangularity,
    get_antenna_params,
    get_baseline_params,
    get_freq_params,
    get_time_params,
)


@pytest.fixture(scope="function")
def simplest_working_params() -> dict[str, Any]:
    return {
        "freq_array": np.linspace(1e8, 2e8, 100),
        "polarization_array": ["xx", "yy"],
        "antenna_positions": {0: [0, 0, 0], 1: [0, 0, 1], 2: [0, 0, 2]},
        "telescope_location": EarthLocation.from_geodetic(0, 0, 0),
        "telescope_name": "test",
        "unique_antpairs": [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)],
        "unique_times": np.linspace(2459855, 2459856, 20),
    }


def test_simplest_new_uvdata(simplest_working_params: dict[str, Any]):
    uvd = UVData.new(**simplest_working_params)

    assert uvd.Nfreqs == 100
    assert uvd.Npols == 2
    assert uvd.Nants_data == 3
    assert uvd.Nbls == 6
    assert uvd.Ntimes == 20
    assert uvd.Nblts == 120
    assert uvd.Nspws == 1


def test_bad_inputs(simplest_working_params: dict[str, Any]):
    with pytest.raises(ValueError, match="vis_units must be one of"):
        UVData.new(**simplest_working_params, vis_units="foo")

    with pytest.raises(
        ValueError, match="Keyword argument derp is not a valid UVData attribute"
    ):
        UVData.new(**simplest_working_params, derp="foo")


def test_bad_antenna_inputs(simplest_working_params: dict[str, Any]):
    with pytest.raises(
        ValueError, match="Either antenna_numbers or antenna_names must be provided"
    ):
        badp = {
            k: v for k, v in simplest_working_params.items() if k != "antenna_positions"
        }
        UVData.new(
            antenna_positions=np.array([[0, 0, 0], [0, 0, 1], [0, 0, 2]]),
            antenna_numbers=None,
            antenna_names=None,
            **badp,
        )

    with pytest.raises(ValueError, match="Antenna names must be integers"):
        badp = {
            k: v for k, v in simplest_working_params.items() if k != "antenna_positions"
        }
        UVData.new(
            antenna_positions=np.array([[0, 0, 0], [0, 0, 1], [0, 0, 2]]),
            antenna_numbers=None,
            antenna_names=["foo", "bar", "baz"],
            **badp,
        )

    with pytest.raises(ValueError, match="antenna_positions must be a numpy array"):
        badp = {
            k: v for k, v in simplest_working_params.items() if k != "antenna_positions"
        }
        UVData.new(
            antenna_positions="foo",
            antenna_numbers=[0, 1, 2],
            antenna_names=["foo", "bar", "baz"],
            **badp,
        )

    with pytest.raises(ValueError, match="antenna_positions must be a 2D array"):
        badp = {
            k: v for k, v in simplest_working_params.items() if k != "antenna_positions"
        }
        UVData.new(
            antenna_positions=np.array([0, 0, 0]), antenna_numbers=np.array([0]), **badp
        )

    with pytest.raises(ValueError, match="Duplicate antenna names found"):
        UVData.new(antenna_names=["foo", "bar", "foo"], **simplest_working_params)

    with pytest.raises(ValueError, match="Duplicate antenna numbers found"):
        badp = {
            k: v for k, v in simplest_working_params.items() if k != "antenna_positions"
        }
        UVData.new(
            antenna_positions=np.array([[0, 0, 0], [0, 0, 1], [0, 0, 2]]),
            antenna_numbers=[0, 1, 0],
            antenna_names=["foo", "bar", "baz"],
            **badp,
        )

    with pytest.raises(
        ValueError, match="antenna_numbers and antenna_names must have the same length"
    ):
        UVData.new(antenna_names=["foo", "bar"], **simplest_working_params)


def test_bad_time_inputs(simplest_working_params: dict[str, Any]):
    with pytest.raises(ValueError, match="time_array must be a numpy array"):
        get_time_params(
            telescope_location=simplest_working_params["telescope_location"],
            time_array="hello this is a string",
        )

    with pytest.raises(ValueError, match="integration_time must be a numpy array"):
        get_time_params(
            telescope_location=simplest_working_params["telescope_location"],
            integration_time={"a": "dict"},
            time_array=simplest_working_params["unique_times"],
        )

    with pytest.raises(
        ValueError, match="integration_time must be the same shape as time_array"
    ):
        get_time_params(
            integration_time=np.ones(len(simplest_working_params["unique_times"]) + 1),
            telescope_location=simplest_working_params["telescope_location"],
            time_array=simplest_working_params["unique_times"],
        )


def test_bad_freq_inputs(simplest_working_params: dict[str, Any]):
    with pytest.raises(ValueError, match="freq_array must be a numpy array"):
        badp = {k: v for k, v in simplest_working_params.items() if k != "freq_array"}
        UVData.new(freq_array="hello this is a string", **badp)

    with pytest.raises(ValueError, match="channel_width must be a numpy array"):
        badp = {
            k: v for k, v in simplest_working_params.items() if k != "channel_width"
        }
        UVData.new(channel_width={"a": "dict"}, **badp)

    with pytest.raises(
        ValueError, match="channel_width must be the same shape as freq_array"
    ):
        badp = {
            k: v for k, v in simplest_working_params.items() if k != "channel_width"
        }
        UVData.new(
            channel_width=np.ones(len(simplest_working_params["freq_array"]) + 1),
            **badp,
        )


def test_bad_baseline_inputs():
    with pytest.raises(
        ValueError, match="Either antpairs or unique_antpairs must be provided"
    ):
        get_baseline_params(
            antenna_positions=np.array([[0, 0, 0], [0, 0, 1], [0, 0, 2]])
        )


def test_bad_rectangularity_inputs():
    with pytest.raises(
        ValueError, match="Either baseline_array or unique_baselines must be provided"
    ):
        configure_blt_rectangularity(unique_times=np.linspace(2459855, 2459856, 20))

    with pytest.raises(
        ValueError, match="Either time_array or unique_times must be provided"
    ):
        configure_blt_rectangularity(
            unique_baselines=np.array([[0, 1], [0, 2], [1, 2]])
        )

    with pytest.raises(
        ValueError,
        match="Only one of baseline_array or unique_baselines can be provided",
    ):
        configure_blt_rectangularity(
            unique_times=np.linspace(2459855, 2459856, 20),
            unique_baselines=np.array([[0, 1], [0, 2], [1, 2]]),
            baseline_array=np.array([0, 1, 2, 3, 4, 5]),
        )

    with pytest.raises(
        ValueError, match="Only one of time_array or unique_times can be provided"
    ):
        configure_blt_rectangularity(
            unique_times=np.linspace(2459855, 2459856, 20),
            unique_baselines=np.array([[0, 1], [0, 2], [1, 2]]),
            time_array=np.linspace(2459855, 2459856, 20),
        )

    with pytest.raises(
        ValueError,
        match="If unique_times are provided, unique_baselines must be provided",
    ):
        configure_blt_rectangularity(
            unique_times=np.linspace(2459855, 2459856, 20),
            baseline_array=np.array([0, 1, 2, 3, 4, 5]),
        )

    with pytest.raises(
        ValueError,
        match="If unique_baselines are provided, unique_times must be provided",
    ):
        configure_blt_rectangularity(
            unique_baselines=np.array([[0, 1], [0, 2], [1, 2]]),
            time_array=np.linspace(2459855, 2459856, 20),
        )

    with pytest.raises(
        ValueError,
        match="If unique_times are provided, blts_are_rectangular must be True",
    ):
        configure_blt_rectangularity(
            unique_times=np.linspace(2459855, 2459856, 20),
            unique_baselines=np.array([[0, 1], [0, 2], [1, 2]]),
            blts_are_rectangular=False,
        )


def test_alternate_antenna_inputs():
    antpos_dict = {
        0: np.array([0, 0, 0]),
        1: np.array([0, 0, 1]),
        2: np.array([0, 0, 2]),
    }

    antpos_array = np.array([[0, 0, 0], [0, 0, 1], [0, 0, 2]])
    antnum = np.array([0, 1, 2])
    antname = np.array(["000", "001", "002"])

    pos, names, nums = get_antenna_params(antenna_positions=antpos_dict)
    pos2, names2, nums2 = get_antenna_params(
        antenna_positions=antpos_array, antenna_numbers=antnum, antenna_names=antname
    )

    assert np.allclose(pos, pos2)
    assert np.all(names == names2)
    assert np.all(nums == nums2)

    antpos_dict = {
        "000": np.array([0, 0, 0]),
        "001": np.array([0, 0, 1]),
        "002": np.array([0, 0, 2]),
    }
    pos, names, nums = get_antenna_params(antenna_positions=antpos_dict)
    assert np.allclose(pos, pos2)
    assert np.all(names == names2)
    assert np.all(nums == nums2)


def test_alternate_time_inputs():
    loc = EarthLocation.from_geodetic(0, 0, 0)

    time_array = np.linspace(2459855, 2459856, 20)
    integration_time = (time_array[1] - time_array[0]) * 24 * 60 * 60

    times, ints = get_time_params(
        time_array=time_array, integration_time=integration_time, telescope_location=loc
    )
    times2, ints2 = get_time_params(
        time_array=time_array,
        integration_time=integration_time * np.ones_like(time_array),
        telescope_location=loc,
    )
    assert np.allclose(times, times2)
    assert np.allclose(ints, ints2)

    times3, ints3 = get_time_params(time_array=time_array, telescope_location=loc)
    assert np.allclose(times, times3)
    assert np.allclose(ints, ints3)


def test_alternate_freq_inputs():
    freq_array = np.linspace(1e8, 2e8, 15)
    channel_width = freq_array[1] - freq_array[0]

    freqs, widths = get_freq_params(freq_array=freq_array, channel_width=channel_width)

    freqs2, widths2 = get_freq_params(
        freq_array=freq_array, channel_width=channel_width * np.ones_like(freq_array)
    )
    assert np.allclose(freqs, freqs2)
    assert np.allclose(widths, widths2)

    freqs3, widths3 = get_freq_params(freq_array=freq_array)
    assert np.allclose(freqs, freqs3)
    assert np.allclose(widths, widths3)


def test_alternative_baseline_inputs():
    antpos = np.array([[0, 0, 0], [0, 0, 1], [0, 0, 2]])
    ap = [(0, 0), (0, 1), (0, 2)]

    bls, ubls = get_baseline_params(antenna_positions=antpos, antpairs=ap)
    bls2, ubls2 = get_baseline_params(antenna_positions=antpos, unique_antpairs=ap)

    assert np.all(bls == ubls2)
    assert np.all(bls2 == ubls)


def test_empty(simplest_working_params: dict[str, Any]):
    uvd = UVData.new(empty=True, **simplest_working_params)

    assert uvd.data_array.shape == (uvd.Nblts, uvd.Nfreqs, uvd.Npols)
    assert uvd.flag_array.shape == uvd.data_array.shape == uvd.nsample_array.shape
    assert not np.any(uvd.flag_array)
    assert np.all(uvd.nsample_array == 1)
    assert np.all(uvd.data_array == 0)


def test_passing_data(simplest_working_params: dict[str, Any]):
    uvd = UVData.new(empty=True, **simplest_working_params)
    shape = uvd.data_array.shape

    uvd = UVData.new(
        data_array=np.zeros(shape, dtype=complex), **simplest_working_params
    )

    assert np.all(uvd.data_array == 0)
    assert np.all(uvd.flag_array == 0)
    assert np.all(uvd.nsample_array == 1)

    uvd = UVData.new(
        data_array=np.zeros(shape, dtype=complex),
        flag_array=np.ones(shape, dtype=bool),
        **simplest_working_params,
    )

    assert np.all(uvd.data_array == 0)
    assert np.all(uvd.flag_array)
    assert np.all(uvd.nsample_array == 1)

    uvd = UVData.new(
        data_array=np.zeros(shape, dtype=complex),
        flag_array=np.ones(shape, dtype=bool),
        nsample_array=np.ones(shape, dtype=int),
        **simplest_working_params,
    )

    assert np.all(uvd.data_array == 0)
    assert np.all(uvd.flag_array)
    assert np.all(uvd.nsample_array == 1)


def test_passing_bad_data(simplest_working_params: dict[str, Any]):
    uvd = UVData.new(empty=True, **simplest_working_params)
    shape = uvd.data_array.shape

    with pytest.raises(ValueError, match="Data array shape"):
        uvd = UVData.new(
            data_array=np.zeros((1, 2, 3), dtype=float), **simplest_working_params
        )

    with pytest.raises(ValueError, match="Flag array shape"):
        uvd = UVData.new(
            data_array=np.zeros(shape, dtype=complex),
            flag_array=np.ones((1, 2, 3), dtype=float),
            **simplest_working_params,
        )

    with pytest.raises(ValueError, match="nsample array shape"):
        uvd = UVData.new(
            data_array=np.zeros(shape, dtype=complex),
            flag_array=np.ones(shape, dtype=bool),
            nsample_array=np.ones((1, 2, 3), dtype=float),
            **simplest_working_params,
        )


def test_passing_kwargs(simplest_working_params: dict[str, Any]):
    uvd = UVData.new(blt_order=("time", "baseline"), **simplest_working_params)

    assert uvd.blt_order == ("time", "baseline")


def test_blt_rect():
    utimes = np.linspace(2459855, 2459856, 20)
    ubls = np.array([1, 2, 3])

    nbls, ntimes, rect, axis, times, bls = configure_blt_rectangularity(
        unique_times=utimes, unique_baselines=ubls, time_axis_faster_than_bls=False
    )

    assert nbls == 3
    assert ntimes == 20
    assert rect
    assert not axis
    assert len(times) == len(bls)
    assert times[1] == times[0]
    assert bls[1] != bls[0]

    TIMES, BLS = np.meshgrid(utimes, ubls)
    TIMES = TIMES.flatten()
    BLS = BLS.flatten()

    nbls, ntimes, rect, axis, times, bls = configure_blt_rectangularity(
        time_array=TIMES, baseline_array=BLS, blts_are_rectangular=True
    )

    assert nbls == 3
    assert ntimes == 20
    assert rect
    assert axis
    assert len(times) == len(bls)
    assert times[1] != times[0]
    assert bls[1] == bls[0]

    BLS, TIMES = np.meshgrid(ubls, utimes)
    TIMES = TIMES.flatten()
    BLS = BLS.flatten()

    nbls, ntimes, rect, axis, times, bls = configure_blt_rectangularity(
        time_array=TIMES, baseline_array=BLS, blts_are_rectangular=True
    )

    assert nbls == 3
    assert ntimes == 20
    assert rect
    assert not axis
    assert len(times) == len(bls)
    assert times[1] == times[0]
    assert bls[1] != bls[0]

    nbls, ntimes, rect, axis, times, bls = configure_blt_rectangularity(
        time_array=TIMES, baseline_array=BLS, blts_are_rectangular=False
    )

    assert nbls == 3
    assert ntimes == 20
    assert not rect
    assert not axis
    assert len(times) == len(bls)
    assert times[1] == times[0]
    assert bls[1] != bls[0]

    nbls, ntimes, rect, axis, times, bls = configure_blt_rectangularity(
        time_array=TIMES, baseline_array=BLS
    )

    assert nbls == 3
    assert ntimes == 20
    assert rect
    assert not axis
    assert len(times) == len(bls)
    assert times[1] == times[0]
    assert bls[1] != bls[0]
