Repository: RadioAstronomySoftwareGroup/pyuvdata

Issue:
Option to phase multiple files on read, enabling add on files with different phase centers
If you have a lot of files with different phase centers but want to create a single uvdata object from them with a single phase center (or a drift scan object), there isn't a graceful way to do it with a single read.  You need to read each one individually, then phase it, then add them.  Would be nice to just do it with a single call to read and a keyword that lets you define the new phase center or drift scan mode.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705?src=pr&el=h1) Report
> Merging [#705](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705?src=pr&el=desc) into [master](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/commit/6eaa2d32d7656adad3021d221712f47745efce46?src=pr&el=desc) will **decrease** coverage by `0.24%`.
> The diff coverage is `77.77%`.

[![Impacted file tree graph](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705/graphs/tree.svg?width=650&token=4QykcCeB1H&height=150&src=pr)](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #705      +/-   ##
==========================================
- Coverage   98.99%   98.75%   -0.25%     
==========================================
  Files          20       20              
  Lines        9067     9074       +7     
==========================================
- Hits         8976     8961      -15     
- Misses         91      113      +22
```


| [Impacted Files](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pyuvdata/uvdata.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705/diff?src=pr&el=tree#diff-cHl1dmRhdGEvdXZkYXRhLnB5) | `99.51% <77.77%> (-0.49%)` | :arrow_down: |
| [pyuvdata/version.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705/diff?src=pr&el=tree#diff-cHl1dmRhdGEvdmVyc2lvbi5weQ==) | `96.61% <0%> (-3.39%)` | :arrow_down: |
| [pyuvdata/utils.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705/diff?src=pr&el=tree#diff-cHl1dmRhdGEvdXRpbHMucHk=) | `99.03% <0%> (-0.97%)` | :arrow_down: |
| [pyuvdata/uvh5.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705/diff?src=pr&el=tree#diff-cHl1dmRhdGEvdXZoNS5weQ==) | `99.18% <0%> (-0.82%)` | :arrow_down: |
| [pyuvdata/aipy\_extracts.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705/diff?src=pr&el=tree#diff-cHl1dmRhdGEvYWlweV9leHRyYWN0cy5weQ==) | `84.9% <0%> (-0.63%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705?src=pr&el=footer). Last update [6eaa2d3...fa392a6](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/705?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

mkolopanis: The `hera_cal` and `hera_qm` failures here are a little annoying. In `hera_cal` the HERAData object which is used for processing overloads the `read` function. As a result when pyuvdata tries to call `self.read` while looping over a list of files it instead calls the subclass's `read` function.

We may be okay with these updates causing breaking changes on that object and the downstream repos updating to adjust. If we want to try to play nice with it we could do something like the following whenever we call `self.read` for the looping over lists.
```
            read_func = self.read
            if issubclass(self.__class__, UVData):
                read_func = super(self.__class__, self).read
            read_func
```
bhazelton: This seems like something that the downstream repos should fix.
mkolopanis: Interesting the tests I added failing on Mac OSX, uvw arrays seem to be the same to within one part in 10^5 but the data arrays are failing the == check.
bhazelton: @mkolopanis there's a known error introduced by phasing & unphasing at something like a part in 10^5 (see the phasing memo), so in the existing phasing tests we increase the tolerance on the uvw_array to account for it. Not ideal, but the best we can currently do. I think increasing the tolerance for this test is entirely reasonable and in keeping with the other tests. Maybe add a comment that references the phasing memo.
bhazelton: Oh, wait. The tolerances are already only 1e-3. Now I understand the confusion. I'll investigate on my mac.
bhazelton: This is crazy. I'm seeing differences in the data array of order 1.
bhazelton: does this also close #654?
bhazelton: @PyxieLouStar I did some work integrating `read_mwa_corr_fits` in this PR to make it symmetric with other read methods. Can you please take a look and make sure it makes sense?
mkolopanis: I realized there very simply could just be an else statement to set the
select_antenna_nums and others that are specific to those there to None
also. I must have been tired this morning. I just noticed that it's trying
to select on say a uvh5 read and erroring because a few of the select
arguments are not set.

On Fri, Dec 6, 2019, 10:38 Bryna Hazelton <notifications@github.com> wrote:

> *@bhazelton* commented on this pull request.
> ------------------------------
>
> In pyuvdata/uvdata.py
> <https://github.com/RadioAstronomySoftwareGroup/pyuvdata/pull/705#discussion_r354950363>
> :
>
> > +                if file_type in ['fhd', 'ms', 'mwa_corr_fits']:
> +                    # these file types do not have select on read, so set all
> +                    # select parameters
> +                    select_antenna_nums = antenna_nums
> +                    select_antenna_names = antenna_names
> +                    select_ant_str = ant_str
> +                    select_bls = bls
> +                    select_frequencies = frequencies
> +                    select_freq_chans = freq_chans
> +                    select_times = times
> +                    select_polarizations = polarizations
> +                    select_blt_inds = blt_inds
> +
> +                    if time_range is not None:
> +                        unique_times = np.unique(self.time_array)
> +                        select_times = unique_times[
> +                            np.where((unique_times >= np.min(time_range))
> +                                     & (unique_times <= np.max(time_range)))]
> +
> +                self.select(
> +                    antenna_nums=select_antenna_nums,
> +                    antenna_names=select_antenna_names,
> +                    ant_str=select_ant_str,
> +                    bls=select_bls,
> +                    frequencies=select_frequencies,
> +                    freq_chans=select_freq_chans,
> +                    times=select_times,
> +                    polarizations=select_polarizations,
> +                    blt_inds=select_blt_inds,
> +                    run_check=run_check,
> +                    check_extra=check_extra,
> +                    run_check_acceptability=run_check_acceptability,
> +                    keep_all_metadata=keep_all_metadata)
>
> In the other file types, if time_range is set then select_times is set
> and this select is run. There used to be separate selects for each file
> type but I consolidated them here.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/RadioAstronomySoftwareGroup/pyuvdata/pull/705?email_source=notifications&email_token=ACB7BLOI45SJI36LYFYMBR3QXKEYBA5CNFSM4JAW6EI2YY3PNVWWK3TUL52HS4DFWFIHK3DMKJSXC5LFON2FEZLWNFSXPKTDN5WW2ZLOORPWSZGOCOJEONI#discussion_r354950363>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ACB7BLN75C6XVAEJIHDL37LQXKEYBANCNFSM4JAW6EIQ>
> .
>

bhazelton: Oh, maybe I screwed up the refactor, I'll take a look at the errors.

bhazelton: @plaplant we can talk about this more in an hour, but I think it's ready for another pass when you have time.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aa6baeda..74e25577 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,12 +4,15 @@ All notable changes to this project will be documented in this file.
 ## [Unreleased]
 
 ### Added
+- Support for rephasing phased data including on `read`, `__add__` and `fast_concat` so that files with different phasing can be read in together.
 - `sum_vis` and `diff_vis` for summing or differencing visibilities in the data_array.
 - `read_mwa_corr_fits` for reading in MWA correlator gpubox files and applying cable corrections.
 - `eq_coeffs` for storing equalization coefficients and `remove_eq_coeffs` for removing them.
 - `utils.apply_uvflag` for applying UVFlag objects to UVData objects
 
 ### Fixed
+- Arrays of file names can now be passed to `read` (not just lists).
+- run_check is no longer turned off in `read_uvfits` and `read_uvh5` when `read_data` is False.
 - Redundancy finder will now error if any baselines appear in multiple groups.
 - A bug in `UVCal` objects that prevented them from properly getting data with `ee`/`nn`-style polarizations.
 - a bug in `UVFlag` where `x_orientation` was not set during initialization.
@@ -18,6 +21,13 @@ All notable changes to this project will be documented in this file.
 - A bug in UVFlag.to_baseline() where force_pol kwarg did not work for UVData Npols > 1
 - `UVData.read_uvfits` no longer breaks if there are non-ascii bytes in antenna names (which CASA sometimes writes).
 
+### Deprecated
+- Reading in multiple files (or file sets) using file-type specific read methods (e.g. `read_uvfits`) in favor of the generic `read` method.
+- The `phase_center` and `phase_data` keywords to `read_mwa_corr_fits` in favor of `phase_to_pointing_center` and the `phase_center_radec` keyword in the generic `read` method.
+- Support for reading only the header (not all the metadata) of uvfits files.
+- The `read_uvfits_metadata` and `read_uvfits_data` methods on the UVFITS object.
+- The `read_miriad_metadata` method on the Mirad object.
+
 ## [1.4.2] - 2019-10-15
 
 ### Added
diff --git a/docs/tutorial.rst b/docs/tutorial.rst
index 3cf237fe..feebd57e 100644
--- a/docs/tutorial.rst
+++ b/docs/tutorial.rst
@@ -20,12 +20,12 @@ a) miriad -> uvfits
   >>> UV = UVData()
 
   # This miriad file is known to be a drift scan.
-  # Use either the file type specific read_miriad or the generic read function,
-  # optionally specify the file type
+  # Use the `read` method, optionally specify the file type. Can also use the
+  # file type specific `read_miriad` method, but only if reading a single file.
   >>> miriad_file = 'pyuvdata/data/new.uvA'
-  >>> UV.read_miriad(miriad_file)
-  >>> UV.read(miriad_file, file_type='miriad')
   >>> UV.read(miriad_file)
+  >>> UV.read(miriad_file, file_type='miriad')
+  >>> UV.read_miriad(miriad_file)
 
   # Write out the uvfits file
   >>> UV.write_uvfits('tutorial.uvfits', force_phase=True, spoof_nonessential=True)
@@ -41,11 +41,11 @@ b) uvfits -> miriad
   >>> UV = UVData()
   >>> uvfits_file = 'pyuvdata/data/day2_TDEM0003_10s_norx_1src_1spw.uvfits'
 
-  # Use either the file type specific read_uvfits or the generic read function,
-  # optionally specify the file type
-  >>> UV.read_uvfits(uvfits_file)
-  >>> UV.read(uvfits_file, file_type='uvfits')
+  # Use the `read` method, optionally specify the file type. Can also use the
+  # file type specific `read_uvfits` method, but only if reading a single file.
   >>> UV.read(uvfits_file)
+  >>> UV.read(uvfits_file, file_type='uvfits')
+  >>> UV.read_uvfits(uvfits_file)
 
   # Write out the miriad file
   >>> write_file = 'tutorial.uv'
@@ -55,7 +55,7 @@ b) uvfits -> miriad
 
 c) FHD -> uvfits
 ****************
-When reading FHD format, we need to point to several files.
+When reading FHD format, we need to point to several files for each observation.
 ::
 
   >>> from pyuvdata import UVData
@@ -67,11 +67,11 @@ When reading FHD format, we need to point to several files.
   ...                                       'vis_YY.sav', 'vis_model_XX.sav',
   ...                                       'vis_model_YY.sav', 'settings.txt']]
 
-  # Use either the file type specific read_fhd or the generic read function,
-  # optionally specify the file type
-  >>> UV.read_fhd(fhd_files)
-  >>> UV.read(fhd_files, file_type='fhd')
+  # Use the `read` method, optionally specify the file type. Can also use the
+  # file type specific `read_fhd` method, but only if reading a single observation.
   >>> UV.read(fhd_files)
+  >>> UV.read(fhd_files, file_type='fhd')
+  >>> UV.read_fhd(fhd_files)
   >>> UV.write_uvfits('tutorial.uvfits', spoof_nonessential=True)
 
 d) FHD -> miriad
@@ -103,9 +103,11 @@ e) CASA -> uvfits
   >>> UV = UVData()
   >>> ms_file = 'pyuvdata/data/day2_TDEM0003_10s_norx_1src_1spw.ms'
 
-  # Use either the file type specific read_ms or the generic read function,
-  # optionally specify the file type
+  # Use the `read` method, optionally specify the file type. Can also use the
+  # file type specific `read_ms` method, but only if reading a single file.
   # note that reading CASA measurement sets requires casacore to be installed
+  >>> UV.read(ms_file)
+  >>> UV.read(ms_file, file_type='ms')
   >>> UV.read_ms(ms_file)
 
   # Write out uvfits file
@@ -160,11 +162,12 @@ h) uvfits -> uvh5
    ...    os.remove(write_file)
    >>> UV.write_uvh5(write_file)
 
-   # Read the uvh5 file back in. Use either the file type specific read_uvh5 or
-   # the generic read function, optionally specify the file type
-   >>> UV.read_uvh5(write_file)
-   >>> UV.read(write_file, file_type='uvh5')
+   # Read the uvh5 file back in.
+   # Use the `read` method, optionally specify the file type. Can also use the
+   # file type specific `read_uvh5` method, but only if reading a single file.
    >>> UV.read(write_file)
+   >>> UV.read(write_file, file_type='uvh5')
+   >>> UV.read_uvh5(write_file)
 
 i) MWA correlator -> uvfits
 *****************
@@ -172,15 +175,15 @@ i) MWA correlator -> uvfits
 
    >>> from pyuvdata import UVData
    >>> UV = UVData()
-   
+
    # Construct the list of files
    >>> data_path = 'pyuvdata/data/mwa_corr_fits_testfiles/'
    >>> filelist = [data_path + i for i in ['1131733552.metafits', '1131733552_20151116182537_mini_gpubox01_00.fits']]
-   
+
    # Use the file type specific read_mwa_corr_fits
    # Apply cable corrections and phase data before writing to uvfits
    >>> UV.read_mwa_corr_fits(filelist, correct_cable_len=True, phase_data=True)
-   
+
    # Write out uvfits file
    >>> UV.write_uvfits('tutorial.uvfits', spoof_nonessential=True)
 
@@ -296,24 +299,23 @@ Phasing/unphasing data
   >>> print(UV.phase_type)
   drift
 
-  # Phase the data to the zenith at first time step
+  # Phase the data to the zenith at first time step. Can either be specified
+  # as a astropy Time object or as a float which is taken to be in JD.
   >>> UV.phase_to_time(Time(UV.time_array[0], format='jd'))
   >>> print(UV.phase_type)
   phased
 
-  # Undo phasing to try another phase center
+  # Undo phasing
   >>> UV.unphase_to_drift()
+  >>> print(UV.phase_type)
+  drift
 
-  # Phase the data to the zenith at first time step
-  # The time_array can be used directly now as input floats
-  # are assumed to be a JD
+  # Phase the data to the zenith at first time step using float JD.
   >>> UV.phase_to_time(UV.time_array[0])
   >>> print(UV.phase_type)
   phased
 
-  # Undo phasing to try another phase center
-  >>> UV.unphase_to_drift()
-
+  # Rephase to another phase center (unphases and rephases under the hood)
   # Phase to a specific ra/dec/epoch (in radians)
   >>> UV.phase(5.23368, 0.710940, epoch="J2000")
 
@@ -755,43 +757,16 @@ UVData: Working with large files
 To save on memory and time, pyuvdata supports reading only parts of uvfits, uvh5 and
 miriad files.
 
-a) Reading just the header of a uvfits file
-******************************************
-This option is only available for uvfits files, which separate the header which
-is very lightweight to read from the metadata which takes a little more memory.
-When only the header info is read in, the UVData object is not fully specified,
-so only some of the expected attributes are filled out.
-
-The read_metadata keyword is ignored for other file types.
-::
-
-  >>> from pyuvdata import UVData
-  >>> uv = UVData()
-  >>> filename = 'pyuvdata/data/day2_TDEM0003_10s_norx_1src_1spw.uvfits'
-  >>> uv.read(filename, read_data=False, read_metadata=False)
-  >>> print((uv.Nblts, uv.Nfreqs, uv.Npols))
-  (1360, 64, 4)
-
-  >>> print(uv.freq_array.size)
-  64
-
-  >>> print(uv.time_array)
-  None
-
-  >>> print(uv.data_array)
-  None
-
-b) Reading the metadata of a uvfits, uvh5 or miriad file
+a) Reading the metadata of a uvfits, uvh5 or miriad file
 ******************************************
-For uvh5 and uvfits files, reading in the metadata results in a UVData object
-that is still not fully specified, but every attribute except the data_array,
-flag_array and nsample_array are filled out. For Miriad files, less of the
+For uvh5 and uvfits files, reading in the metadata results in a metadata only
+UVData object (which has every attribute except the data_array,
+flag_array and nsample_array filled out). For Miriad files, less of the
 metadata can be read without reading the data, but many of the attributes
-are available. For uvfits files, the metadata can be read in at the same time
-as the header, or you can read in the header followed by the metadata
-(both shown below).
+are available.
 
-FHD and measurement set (ms) files do not support reading only the metadata
+FHD, MWA correlator FITS files, and measurement set (ms) files do not support
+reading only the metadata
 (the read_data keyword is ignored for these file types).
 ::
 
@@ -799,11 +774,7 @@ FHD and measurement set (ms) files do not support reading only the metadata
   >>> uv = UVData()
   >>> filename = 'pyuvdata/data/day2_TDEM0003_10s_norx_1src_1spw.uvfits'
 
-  # read the header and metadata but not the data
-  >>> uv.read(filename, read_data=False)
-
-  # read the header first, then the metadata but not the data
-  >>> uv.read(filename, read_data=False, read_metadata=False)
+  # read the metadata but not the data
   >>> uv.read(filename, read_data=False)
 
   >>> print(uv.time_array.size)
@@ -812,19 +783,14 @@ FHD and measurement set (ms) files do not support reading only the metadata
   >>> print(uv.data_array)
   None
 
-  # If the data_array, flag_array or nsample_array are needed later, they can be
-  # read into the existing object:
-  >>> uv.read(filename)
-  >>> print(uv.data_array.shape)
-  (1360, 1, 64, 4)
-
-c) Reading only parts of uvfits, uvh5 or miriad data
+b) Reading only parts of uvfits, uvh5 or miriad data
 ****************************************************
 The same options that are available for the select function can also be passed to
 the read method to do the select on the read, saving memory and time if only a
 portion of the data are needed.
 
-Note that these keywords can be used for any file type, but for FHD and
+Note that these keywords can be used for any file type, but for FHD,
+MWA correlator FITS files, and
 measurement set (ms) files, the select is done after the read, which does not
 save memory. Miriad only supports some of the selections on the read, the
 unsupported ones are done after the read. Note that miriad supports a select on
@@ -842,7 +808,7 @@ done after the read, which does not save memory.
   >>> print(uv.data_array.shape)
   (1360, 1, 32, 4)
 
-  # Reading in the header and metadata can help with specifying what data to read in
+  # Reading in the metadata can help with specifying what data to read in
   >>> uv = UVData()
   >>> uv.read(filename, read_data=False)
   >>> unique_times = np.unique(uv.time_array)
@@ -866,7 +832,7 @@ done after the read, which does not save memory.
   >>> print(uv.data_array.shape)
   (80, 1, 32, 4)
 
-d) Writing to a uvh5 file in parts
+c) Writing to a uvh5 file in parts
 **********************************
 
 It is possible to write to a uvh5 file in parts, so not all of the file needs to
@@ -1040,7 +1006,7 @@ antenna pairs with no associated data).
     >>> uvd = UVData()
 
     # This file contains a HERA19 layout.
-    >>> uvd.read_uvfits("pyuvdata/data/fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits")
+    >>> uvd.read("pyuvdata/data/fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits")
     >>> uvd.unphase_to_drift(use_ant_pos=True)
     >>> tol = 0.05  # Tolerance in meters
     >>> uvd.select(times=uvd.time_array[0])
@@ -1083,7 +1049,7 @@ baselines from the antenna positions and fills in the full data array based on r
     >>> import copy
     >>> import numpy as np
     >>> uv0 = UVData()
-    >>> uv0.read_uvfits("pyuvdata/data/fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits")
+    >>> uv0.read("pyuvdata/data/fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits")
     >>> tol = 0.02   # In meters
 
     # Compression can be run in-place or return a separate UVData object.
diff --git a/pyuvdata/fhd.py b/pyuvdata/fhd.py
index ff3ce145..b13ef695 100644
--- a/pyuvdata/fhd.py
+++ b/pyuvdata/fhd.py
@@ -73,6 +73,7 @@ def get_fhd_history(settings_file, return_user=False):
 class FHD(UVData):
     """
     Defines a FHD-specific subclass of UVData for reading FHD save files.
+
     This class should not be interacted with directly, instead use the read_fhd
     method on the UVData class.
     """
@@ -99,17 +100,36 @@ class FHD(UVData):
         """
         Read in data from a list of FHD files.
 
-        Args:
-            filelist: The list of FHD save files to read from. Must include at
-                least one polarization file, a params file and a flag file.
-            use_model: Option to read in the model visibilities rather than the
-                dirty visibilities. Default is False.
-            run_check: Option to check for the existence and proper shapes of
-                parameters after reading in the file. Default is True.
-            check_extra: Option to check optional parameters as well as required
-                ones. Default is True.
-            run_check_acceptability: Option to check acceptable range of the values of
-                parameters after reading in the file. Default is True.
+        Parameters
+        ----------
+        filelist : array_like of str
+            The list/array of FHD save files to read from. Must include at
+            least one polarization file, a params file and a flag file.
+        use_model : bool
+            Option to read in the model visibilities rather than the dirty
+            visibilities (the default is False, meaning the dirty visibilities
+            will be read).
+        run_check : bool
+            Option to check for the existence and proper shapes of parameters
+            after after reading in the file (the default is True,
+            meaning the check will be run).
+        check_extra : bool
+            Option to check optional parameters as well as required ones (the
+            default is True, meaning the optional parameters will be checked).
+        run_check_acceptability : bool
+            Option to check acceptable range of the values of parameters after
+            reading in the file (the default is True, meaning the acceptable
+            range check will be done).
+
+        Raises
+        ------
+        IOError
+            If root file directory doesn't exist.
+        ValueError
+            If required files are missing or multiple files for any polarization
+            are included in filelist.
+            If there is no recognized key for visibility weights in the flags_file.
+
         """
         datafiles = {}
         params_file = None
@@ -160,11 +180,11 @@ class FHD(UVData):
                 continue  # pragma: no cover
 
         if len(datafiles) < 1:
-            raise Exception('No data files included in file list')
+            raise ValueError('No data files included in file list')
         if params_file is None:
-            raise Exception('No params file included in file list')
+            raise ValueError('No params file included in file list')
         if flags_file is None:
-            raise Exception('No flags file included in file list')
+            raise ValueError('No flags file included in file list')
         if layout_file is None:
             warnings.warn('No layout file included in file list. '
                           'Support for FHD data without layout files will be '
diff --git a/pyuvdata/miriad.py b/pyuvdata/miriad.py
index ec03373b..c600988f 100644
--- a/pyuvdata/miriad.py
+++ b/pyuvdata/miriad.py
@@ -26,8 +26,10 @@ from . import aipy_extracts
 class Miriad(UVData):
     """
     Defines a Miriad-specific subclass of UVData for reading and writing Miriad files.
-    This class should not be interacted with directly, instead use the read_miriad
+
+    This class should not be interacted with directly, instead use the read
     and write_miriad methods on the UVData class.
+
     """
 
     def _pol_to_ind(self, pol):
@@ -47,36 +49,79 @@ class Miriad(UVData):
         """
         Read in data from a miriad file.
 
-        Args:
-            filepath: The miriad file directory to read from.
-            antenna_nums: The antennas numbers to read into the object.
-            bls: A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-                baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
-                to keep in the object. For length-2 tuples, the  ordering of the numbers
-                within the tuple does not matter. For length-3 tuples, the polarization
-                string is in the order of the two antennas. If length-3 tuples are
-                provided, the polarizations argument below must be None.
-            ant_str: A string containing information about what kinds of visibility data
-                to read-in.  Can be 'auto', 'cross', 'all'. Cannot provide ant_str if
-                antenna_nums and/or bls is not None.
-            polarizations: List of polarization integers or strings to read-in.
-                Ex: ['xx', 'yy', ...]
-            time_range: len-2 list containing min and max range of times (Julian Date) to read-in.
-                Ex: [2458115.20, 2458115.40]
-            read_data: Read in the visibility and flag data. If set to false,
-                only the metadata will be read in. Results in an incompletely
-                defined object (check will not pass). Default True.
-            phase_type: Either 'drift' meaning zenith drift, 'phased' meaning
-                the data are phased to a single RA/Dec or None and it will be
-                guessed at based on the file. Default None.
-            correct_lat_lon: flag -- that only matters if altitude is missing --
-                to update the latitude and longitude from the known_telescopes list
-            run_check: Option to check for the existence and proper shapes of
-                parameters after reading in the file. Default is True.
-            check_extra: Option to check optional parameters as well as required
-                ones. Default is True.
-            run_check_acceptability: Option to check acceptable range of the values of
-                parameters after reading in the file. Default is True.
+        Parameters
+        ----------
+        filepath : str
+            The miriad root directory to read from.
+            Support for a list/array of file directories will be
+            deprecated in version 2.0 in favor of a call to the generic
+            `read` method.
+        antenna_nums : array_like of int, optional
+            The antennas numbers to read into the object.
+        bls : list of tuple, optional
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
+            to include when reading data into the object. For length-2 tuples,
+            the ordering of the numbers within the tuple does not matter. For
+            length-3 tuples, the polarization string is in the order of the two
+            antennas. If length-3 tuples are provided, `polarizations` must be
+            None.
+        ant_str : str, optional
+            A string containing information about what antenna numbers
+            and polarizations to include when reading data into the object.
+            Can be 'auto', 'cross', 'all', or combinations of antenna numbers
+            and polarizations (e.g. '1', '1_2', '1x_2y').  See tutorial for more
+            examples of valid strings and the behavior of different forms for ant_str.
+            If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
+            pyuvdata object.
+            An ant_str cannot be passed in addition to any of `antenna_nums`,
+            `bls` or `polarizations` parameters, if it is a ValueError will be raised.
+        polarizations : array_like of int or str, optional
+            List of polarization integers or strings to read-in. e.g. ['xx', 'yy', ...]
+        time_range : list of float, optional
+            len-2 list containing min and max range of times in Julian Date to
+            include when reading data into the object. e.g. [2458115.20, 2458115.40]
+        read_data : bool
+            Read in the uvws, times, visibility and flag data. If set to False,
+            only the metadata that can be read quickly (without reading the data)
+            will be read in. For Miriad, some of the normally required metadata
+            are not fast to read in (e.g. uvws, times) so will not be read in
+            if this keyword is False. Therefore, setting read_data to False
+            results in an incompletely defined object (check will not pass).
+        phase_type : str, optional
+            Option to specify the phasing status of the data. Options are 'drift',
+            'phased' or None. 'drift' means the data are zenith drift data,
+            'phased' means the data are phased to a single RA/Dec. Default is None
+            meaning it will be guessed at based on the file contents.
+        correct_lat_lon : bool
+            Option to update the latitude and longitude from the known_telescopes
+            list if the altitude is missing.
+        run_check : bool
+            Option to check for the existence and proper shapes of parameters
+            after after reading in the file (the default is True,
+            meaning the check will be run). Ignored if read_data is False.
+        check_extra : bool
+            Option to check optional parameters as well as required ones (the
+            default is True, meaning the optional parameters will be checked).
+            Ignored if read_data is False.
+        run_check_acceptability : bool
+            Option to check acceptable range of the values of parameters after
+            reading in the file (the default is True, meaning the acceptable
+            range check will be done). Ignored if read_data is False.
+
+        Raises
+        ------
+        IOError
+            If root file directory doesn't exist.
+        ValueError
+            If incompatible select keywords are set (e.g. `ant_str` with other
+            antenna selectors, `times` and `time_range`) or select keywords
+            exclude all data or if keywords are set to the wrong type.
+            If the data are multi source or have multiple
+            spectral windows.
+            If the metadata are not internally consistent.
+
         """
         if not os.path.exists(filepath):
             raise IOError(filepath + ' not found')
@@ -84,7 +129,12 @@ class Miriad(UVData):
 
         # load metadata
         (default_miriad_variables, other_miriad_variables, extra_miriad_variables,
-         check_variables) = self.read_miriad_metadata(uv, correct_lat_lon=correct_lat_lon)
+         check_variables) = self._read_miriad_metadata(uv, correct_lat_lon=correct_lat_lon)
+
+        if not read_data:
+            # don't read in the data. This means the object is incomplete,
+            # but that may not matter for many purposes.
+            return
 
         # read through the file and get the data
         _source = uv['source']  # check source of initial visibility
@@ -546,18 +596,36 @@ class Miriad(UVData):
         """
         Write the data to a miriad file.
 
-        Args:
-            filename: The miriad file directory to write to.
-            run_check: Option to check for the existence and proper shapes of
-                parameters before writing the file. Default is True.
-            check_extra: Option to check optional parameters as well as required
-                ones. Default is True.
-            run_check_acceptability: Option to check acceptable range of the values of
-                parameters before writing the file. Default is True.
-            clobber: Option to overwrite the filename if the file already exists.
-                Default is False. If False and file exists, raises an IOError.
-            no_antnums: Option to not write the antnums variable to the file.
-                Should only be used for testing purposes.
+        Parameters
+        ----------
+        filename : str
+            The miriad root directory to write to.
+        run_check : bool
+            Option to check for the existence and proper shapes of parameters
+            after before writing the file (the default is True,
+            meaning the check will be run).
+        check_extra : bool
+            Option to check optional parameters as well as required ones (the
+            default is True, meaning the optional parameters will be checked).
+        run_check_acceptability : bool
+            Option to check acceptable range of the values of parameters before
+            writing the file (the default is True, meaning the acceptable
+            range check will be done).
+        clobber : bool
+            Option to overwrite the filename if the file already exists.
+        no_antnums : bool
+            Option to not write the antnums variable to the file.
+            Should only be used for testing purposes.
+
+        Raises
+        ------
+        ValueError
+            If the frequencies are not evenly spaced or are separated by more
+            than their channel width.
+            The `phase_type` of the object is "unknown".
+        TypeError
+            If any entry in extra_keywords is not a single string or number.
+
         """
         # change time_array and lst_array to mark beginning of integration, per Miriad format
         miriad_time_array = self.time_array - self.integration_time / (24 * 3600.) / 2
@@ -830,19 +898,35 @@ class Miriad(UVData):
 
     def read_miriad_metadata(self, filename, correct_lat_lon=True):
         """
-        Read in metadata (parameter info) but not data from a miriad file.
-
-        Args:
-            filename : The miriad file to read
-            correct_lat_lon: flag -- that only matters if altitude is missing --
-                to update the latitude and longitude from the known_telescopes list
+        Deprecated: Read in metadata (parameter info) but not data from a miriad file.
+
+        Deprecated in favor of `_read_miriad_metadata` because it is not an
+        API level method (it's only called internally.)
+
+        Parameters
+        ----------
+        filename : str
+            The miriad file to read.
+        correct_lat_lon : bool
+            Option to update the latitude and longitude from the known_telescopes
+            list if the altitude is missing.
+
+        Returns
+        -------
+        default_miriad_variables : list
+            list of default miriad variables
+        other_miriad_variables: list
+            list of other miriad variables
+        extra_miriad_variables: list
+            list of extra, non-standard variables
+        check_variables: dict
+            dict of extra miriad variables
 
-        Returns:
-            default_miriad_variables: list of default miriad variables
-            other_miriad_variables: list of other miriad variables
-            extra_miriad_variables: list of extra, non-standard variables
-            check_variables: dict of extra miriad variables
         """
+        warnings.warn('The read_miriad_metadata method is deprecated in favor of '
+                      '_read_miriad_metadata because it is not API level code. This '
+                      'function will be removed in version 2.0', DeprecationWarning)
+
         # check for data array
         if self.data_array is not None:
             raise ValueError('data_array is already defined, cannot read metadata')
@@ -852,7 +936,32 @@ class Miriad(UVData):
             uv = aipy_extracts.UV(filename)
         elif isinstance(filename, aipy_extracts.UV):
             uv = filename
+        self._read_miriad_metadata(uv)
+
+    def _read_miriad_metadata(self, uv, correct_lat_lon=True):
+        """
+        Read in metadata (parameter info) but not data from a miriad file.
+
+        Parameters
+        ----------
+        uv : aipy.miriad.UV
+            aipy object to load metadata from.
+        correct_lat_lon : bool
+            Option to update the latitude and longitude from the known_telescopes
+            list if the altitude is missing.
+
+        Returns
+        -------
+        default_miriad_variables : list of str
+            list of default miriad variables
+        other_miriad_variables: list of str
+            list of other miriad variables
+        extra_miriad_variables: list of str
+            list of extra, non-standard variables
+        check_variables: dict
+            dict of extra miriad variables to add to extra_keywords parameter.
 
+        """
         # load miriad variables
         (default_miriad_variables, other_miriad_variables,
          extra_miriad_variables) = self._load_miriad_variables(uv)
@@ -903,15 +1012,22 @@ class Miriad(UVData):
 
     def _load_miriad_variables(self, uv):
         """
-        Load miriad variables from an aipy.miriad UV descriptor.
-
-        Args:
-            uv: aipy.miriad.UV instance
+        Load miriad variables from an aipy.miriad UV descriptor onto self.
+
+        Parameters
+        ----------
+        uv : aipy.miriad.UV
+            aipy object to load variables from.
+
+        Returns
+        -------
+        default_miriad_variables : list of str
+            list of default miriad variables
+        other_miriad_variables: list of str
+            list of other miriad variables
+        extra_miriad_variables: list of str
+            list of extra, non-standard variables
 
-        Returns:
-            default_miriad_variables: list of default miriad variables
-            other_miriad_varialbes: list of other miriad varialbes
-            extra_miriad_variables: list of extra, non-standard variables
         """
         # list of miriad variables always read
         # NB: this includes variables in try/except (i.e. not all variables are
@@ -996,12 +1112,16 @@ class Miriad(UVData):
 
     def _load_telescope_coords(self, uv, correct_lat_lon=True):
         """
-        Load telescope lat, lon alt coordinates from aipy.miriad UV descriptor.
+        Load telescope lat, lon, alt coordinates from aipy.miriad UV descriptor.
+
+        Parameters
+        ----------
+        uv : aipy.miriad.UV
+            aipy object to load lat, lon, alt coordinates from.
+        correct_lat_lon : bool
+            Option to update the latitude and longitude from the known_telescopes
+            list if the altitude is missing.
 
-        Args:
-            uv: aipy.miriad.UV instance
-            correct_lat_lon: flag -- that only matters if altitude is missing --
-                to update the latitude and longitude from the known_telescopes list
         """
         # check if telescope name is present
         if self.telescope_name is None:
@@ -1063,15 +1183,20 @@ class Miriad(UVData):
                               'set using antenna positions.'
                               .format(telescope_name=self.telescope_name))
 
-    def _load_antpos(self, uv, sorted_unique_ants=[], correct_lat_lon=True):
+    def _load_antpos(self, uv, sorted_unique_ants=None, correct_lat_lon=True):
         """
         Load antennas and their positions from a Miriad UV descriptor.
 
-        Args:
-            uv: aipy.miriad.UV instance.
-            sorted_unique_ants: list of unique antennas
-            correct_lat_lon: flag -- that only matters if altitude is missing --
-                to update the latitude and longitude from the known_telescopes list
+        Parameters
+        ----------
+        uv : aipy.miriad.UV
+            aipy object to antennas and positions from.
+        sorted_unique_ants : list of ints, optional
+            List of unique antennas.
+        correct_lat_lon : bool
+            Option to update the latitude and longitude from the known_telescopes
+            list if the altitude is missing.
+
         """
         # check if telescope coords exist
         if self.telescope_location_lat_lon_alt is None:
@@ -1222,7 +1347,10 @@ class Miriad(UVData):
                 # take the union of the antennas with good positions (good_antpos)
                 # and the antennas that have visisbilities (sorted_unique_ants)
                 # if there are antennas with visibilities but zeroed positions we issue a warning below
-                ants_use = set(good_antpos).union(sorted_unique_ants)
+                if sorted_unique_ants is not None:
+                    ants_use = set(good_antpos).union(sorted_unique_ants)
+                else:
+                    ants_use = set(good_antpos)
                 # ants_use are the antennas we'll keep track of in the UVData
                 # object, so they dictate Nants_telescope
                 self.Nants_telescope = len(ants_use)
@@ -1246,8 +1374,9 @@ class Miriad(UVData):
             # there are no antenna_numbers or antenna_positions, so just use
             # the antennas present in the visibilities
             # (Nants_data will therefore match Nants_telescope)
-            self.antenna_numbers = np.array(sorted_unique_ants)
-            self.Nants_telescope = len(self.antenna_numbers)
+            if sorted_unique_ants is not None:
+                self.antenna_numbers = np.array(sorted_unique_ants)
+                self.Nants_telescope = len(self.antenna_numbers)
 
         # antenna names is a foreign concept in miriad but required in other formats.
         try:
diff --git a/pyuvdata/ms.py b/pyuvdata/ms.py
index ebc0df8a..817724e6 100644
--- a/pyuvdata/ms.py
+++ b/pyuvdata/ms.py
@@ -4,6 +4,7 @@
 
 """
 Class for reading and writing casa measurement sets.
+
 Requires casacore.
 """
 from __future__ import absolute_import, division, print_function
@@ -36,6 +37,7 @@ polDict = {1: 1, 2: 2, 3: 3, 4: 4, 5: -1, 6: -3,
 class MS(UVData):
     """
     Defines a class for reading and writing casa measurement sets.
+
     Attributes
     ----------
     ms_required_extra : list of str
@@ -103,25 +105,46 @@ class MS(UVData):
         writing ms is not yet supported
         '''
 
-    def read_ms(self, filepath, run_check=True, check_extra=True,
-                run_check_acceptability=True,
-                data_column='DATA', pol_order='AIPS'):
-        '''
+    def read_ms(self, filepath, data_column='DATA', pol_order='AIPS',
+                run_check=True, check_extra=True,
+                run_check_acceptability=True):
+        """
         read in a casa measurement set
 
-        Args:
-            filepath: name of the measurement set folder
-            run_check: Option to check for the existence and proper shapes of
-                parameters after reading in the file. Default is True.
-            check_extra: Option to check optional parameters as well as required
-                ones. Default is True.
-            run_check_acceptability: Option to check the values of parameters
-                after reading in the file. Default is True.
-            data_column: specify which CASA measurement set data column to read from (can be 'DATA','CORRECTED', or 'MODEL')
-            pol_order: use 'AIPS' or 'CASA' ordering of polarizations?
-        '''
+        Parameters
+        ----------
+        filepath : str
+            The measurement set root directory to read from.
+        data_column : str
+            name of CASA data column to read into data_array. Options are:
+            'DATA', 'MODEL', or 'CORRECTED_DATA'
+        pol_order : str
+            Option to specify polarizations order convention, options are 'CASA' or 'AIPS'.
+        run_check : bool
+            Option to check for the existence and proper shapes of parameters
+            after after reading in the file (the default is True,
+            meaning the check will be run).
+        check_extra : bool
+            Option to check optional parameters as well as required ones (the
+            default is True, meaning the optional parameters will be checked).
+        run_check_acceptability : bool
+            Option to check acceptable range of the values of parameters after
+            reading in the file (the default is True, meaning the acceptable
+            range check will be done).
+
+        Raises
+        ------
+        IOError
+            If root file directory doesn't exist.
+        ValueError
+            If the `data_column` is not set to an allowed value.
+            If the data are have multiple subarrays or are multi source or have
+            multiple spectral windows.
+            If the data have multiple data description ID values.
+
+        """
         # make sure user requests a valid data_column
-        if data_column != 'DATA' and data_column != 'CORRECTED_DATA' and data_column != 'MODEL':
+        if data_column not in ['DATA', 'CORRECTED_DATA', 'MODEL']:
             raise ValueError(
                 'Invalid data_column value supplied. Use \'Data\',\'MODEL\' or \'CORRECTED_DATA\'')
         if not os.path.exists(filepath):
diff --git a/pyuvdata/mwa_corr_fits.py b/pyuvdata/mwa_corr_fits.py
index 8e14db18..3f3c59ca 100644
--- a/pyuvdata/mwa_corr_fits.py
+++ b/pyuvdata/mwa_corr_fits.py
@@ -70,7 +70,7 @@ class MWACorrFITS(UVData):
                                   * self.freq_array.reshape(1, self.Nfreqs))[:, :, None]
 
     def read_mwa_corr_fits(self, filelist, use_cotter_flags=False, correct_cable_len=False,
-                           phase_data=False, phase_center=None, run_check=True, check_extra=True,
+                           phase_to_pointing_center=False, run_check=True, check_extra=True,
                            run_check_acceptability=True):
         """
         Read in MWA correlator gpu box files.
@@ -90,16 +90,11 @@ class MWACorrFITS(UVData):
             multiple files are passed.
         use_cotter_flags : bool
             Option to use cotter output mwaf flag files. Otherwise flagging
-            will only be applied to missing data and bad antennas. Default is
-            False.
+            will only be applied to missing data and bad antennas.
         correct_cable_len : bool
-            Option to apply a cable delay correction. Default is False.
-        phase_data : bool
-            Option to phase data. Default is False.
-        phase_center : tuple, optional
-            A tuple containing the ra and dec coordinates in radians of a
-            specific location to phase data to. If not specified, the
-            observation pointing center will be used when phase_data is True.
+            Option to apply a cable delay correction.
+        phase_to_pointing_center : bool
+            Option to phase to the observation pointing center.
         run_check : bool
             Option to check for the existence and proper shapes of parameters
             after after reading in the file (the default is True,
@@ -111,6 +106,15 @@ class MWACorrFITS(UVData):
             Option to check acceptable range of the values of parameters after
             reading in the file (the default is True, meaning the acceptable
             range check will be done).
+
+        Raises
+        ------
+        ValueError
+            If required files are missing or multiple files metafits files are included in filelist.
+            If files from different observations are included in filelist.
+            If files in fileslist have different fine channel widths
+            If file types other than fits, metafits, and mwaf files are included in filelist.
+
         """
         metafits_file = None
         obs_id = None
@@ -157,8 +161,7 @@ class MWACorrFITS(UVData):
                     if num_fine_chans == 0:
                         num_fine_chans = data[1].header['NAXIS2']
                     elif num_fine_chans != data[1].header['NAXIS2']:
-                        raise ValueError('files submitted have different fine \
-                        channel widths')
+                        raise ValueError('files submitted have different fine channel widths')
 
                 # organize files
                 if 'data' not in file_dict.keys():
@@ -182,7 +185,7 @@ class MWACorrFITS(UVData):
             raise ValueError('no metafits file submitted')
         if 'data' not in file_dict.keys():
             raise ValueError('no data files submitted')
-        if 'flags' not in file_dict.keys() and use_cotter_flags is True:
+        if 'flags' not in file_dict.keys() and use_cotter_flags:
             raise ValueError('no flag files submitted. Rerun with flag files \
                              or use_cotter_flags=False')
 
@@ -211,10 +214,6 @@ class MWACorrFITS(UVData):
             ra_rad = np.pi * ra_deg / 180
             dec_rad = np.pi * dec_deg / 180
 
-            # check if a different pointing center has been specified
-            if phase_center is None:
-                phase_center = (ra_rad, dec_rad)
-
             # get parameters from header
             # this assumes no averaging by this code so will need to be updated
             self.channel_width = float(meta_hdr.pop('FINECHAN') * 1000)
@@ -483,7 +482,7 @@ class MWACorrFITS(UVData):
         self.nsample_array = self.nsample_array.reshape((self.Nblts, self.Nfreqs, self.Npols))
 
         # cable delay corrections
-        if correct_cable_len is True:
+        if correct_cable_len:
             self.correct_cable_length(cable_lens)
 
         # add spectral window index
@@ -500,9 +499,8 @@ class MWACorrFITS(UVData):
         self.reorder_pols()
 
         # phasing
-        if phase_data is True:
-            (ra, dec) = phase_center
-            self.phase(ra, dec)
+        if phase_to_pointing_center:
+            self.phase(ra_rad, dec_rad)
 
-        if use_cotter_flags is True:
+        if use_cotter_flags:
             raise NotImplementedError('reading in cotter flag files is not yet available')
diff --git a/pyuvdata/uvdata.py b/pyuvdata/uvdata.py
index 71ec7c09..7d1abf83 100644
--- a/pyuvdata/uvdata.py
+++ b/pyuvdata/uvdata.py
@@ -800,7 +800,8 @@ class UVData(UVBase):
         self.phase_center_epoch = None
         self.set_drift()
 
-    def phase(self, ra, dec, epoch='J2000', phase_frame='icrs', use_ant_pos=False):
+    def phase(self, ra, dec, epoch='J2000', phase_frame='icrs',
+              use_ant_pos=False, allow_rephase=True, orig_phase_frame=None):
         """
         Phase a drift scan dataset to a single ra/dec at a particular epoch.
 
@@ -829,18 +830,37 @@ class UVData(UVBase):
         use_ant_pos : bool
             If True, calculate the uvws directly from the antenna positions
             rather than from the existing uvws.
+        allow_rephase : bool
+            If True, allow unphasing and rephasing if this object is already
+            phased.
+        orig_phase_frame : str
+            The original phase frame of this object (to use in unphasing). Only
+            used if the object is already phased, `allow_rephase` is True and
+            the phase_center_ra/dec of the object does not match `ra` and `dec`.
+            Defaults to using the 'phase_center_frame' attribute or 'icrs' if
+            that attribute is None.
 
         Raises
         ------
         ValueError
             If the phase_type is not 'drift'
+
         """
         if self.phase_type == 'drift':
             pass
         elif self.phase_type == 'phased':
-            raise ValueError('The data is already phased; can only phase '
-                             'drift scan data. Use unphase_to_drift to '
-                             'convert to a drift scan.')
+            if allow_rephase:
+                if (not np.isclose(self.phase_center_ra, ra,
+                                   rtol=self._phase_center_ra.tols[0],
+                                   atol=self._phase_center_ra.tols[1])
+                    or not np.isclose(self.phase_center_dec, dec,
+                                      rtol=self._phase_center_dec.tols[0],
+                                      atol=self._phase_center_dec.tols[1])):
+                    self.unphase_to_drift(phase_frame=orig_phase_frame,
+                                          use_ant_pos=use_ant_pos)
+            else:
+                raise ValueError('The data is already phased; set allow_rephase'
+                                 ' to True to unphase and rephase.')
         else:
             raise ValueError('The phasing type of the data is unknown. '
                              'Set the phase_type to "drift" or "phased" to '
@@ -950,7 +970,8 @@ class UVData(UVBase):
         self.phase_center_frame = phase_frame
         self.set_phased()
 
-    def phase_to_time(self, time, phase_frame='icrs', use_ant_pos=False):
+    def phase_to_time(self, time, phase_frame='icrs', use_ant_pos=False,
+                      allow_rephase=True, orig_phase_frame=None):
         """
         Phase a drift scan dataset to the ra/dec of zenith at a particular time.
 
@@ -967,6 +988,15 @@ class UVData(UVBase):
         use_ant_pos : bool
             If True, calculate the uvws directly from the antenna positions
             rather than from the existing uvws.
+        allow_rephase : bool
+            If True, allow unphasing and rephasing if this object is already
+            phased.
+        orig_phase_frame : str
+            The original phase frame of this object (to use in unphasing). Only
+            used if the object is already phased, `allow_rephase` is True and
+            the phase_center_ra/dec of the object does not match `ra` and `dec`.
+            Defaults to using the 'phase_center_frame' attribute or 'icrs' if
+            that attribute is None.
 
         Raises
         ------
@@ -975,38 +1005,29 @@ class UVData(UVBase):
         TypeError
             If time is not an astropy.time.Time object or Julian Date as a float
         """
-        if self.phase_type == 'drift':
-            pass
-        elif self.phase_type == 'phased':
-            raise ValueError('The data is already phased; can only phase '
-                             'drift scanning data.')
-        else:
-            raise ValueError('The phasing type of the data is unknown. '
-                             'Set the phase_type to drift or phased to '
-                             'reflect the phasing status of the data')
-
         if isinstance(time, (float, np.float32)):
             time = Time(time, format='jd')
 
         if not isinstance(time, Time):
-            raise TypeError("time must be an astropy.time.Time object or a float")
+            raise TypeError(
+                "time must be an astropy.time.Time object or a float")
 
-        # Generate ra/dec of zenith at time in the phase_frame coordinate system
-        # to use for phasing
-        telescope_location = EarthLocation.from_geocentric(self.telescope_location[0],
-                                                           self.telescope_location[1],
-                                                           self.telescope_location[2],
-                                                           unit='m')
+        # Generate ra/dec of zenith at time in the phase_frame coordinate
+        # system to use for phasing
+        telescope_location = EarthLocation.from_geocentric(
+            *self.telescope_location, unit='m')
 
-        zenith_coord = SkyCoord(alt=Angle(90 * units.deg), az=Angle(0 * units.deg),
-                                obstime=time, frame='altaz', location=telescope_location)
+        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(phase_frame)
         zenith_ra = obs_zenith_coord.ra
         zenith_dec = obs_zenith_coord.dec
 
-        self.phase(zenith_ra, zenith_dec, epoch='J2000', phase_frame=phase_frame,
-                   use_ant_pos=use_ant_pos)
+        self.phase(zenith_ra, zenith_dec, epoch='J2000',
+                   phase_frame=phase_frame, use_ant_pos=use_ant_pos,
+                   allow_rephase=allow_rephase, orig_phase_frame=orig_phase_frame)
 
     def set_uvws_from_antenna_positions(self, allow_phasing=False,
                                         orig_phase_frame=None,
@@ -1060,9 +1081,8 @@ class UVData(UVBase):
                                  'Use unphase_to_drift or set '
                                  'allow_phasing=True.'
                                  )
-        antenna_locs_ENU = uvutils.ENU_from_ECEF(
-            (self.antenna_positions + self.telescope_location),
-            *self.telescope_location_lat_lon_alt)
+        antenna_locs_ENU, _ = self.get_ENU_antpos(center=False)
+
         uvw_array = np.zeros((self.baseline_array.size, 3))
         for baseline in list(set(self.baseline_array)):
             baseline_inds = np.where(self.baseline_array == baseline)[0]
@@ -1524,7 +1544,11 @@ class UVData(UVBase):
                                 run_check_acceptability=run_check_acceptability,
                                 inplace=inplace)
 
-    def __add__(self, other, run_check=True, check_extra=True,
+    def __add__(self, other, phase_center_radec=None,
+                unphase_to_drift=False, phase_frame='icrs',
+                orig_phase_frame=None,
+                use_ant_pos=False,
+                run_check=True, check_extra=True,
                 run_check_acceptability=True, inplace=False):
         """
         Combine two UVData objects along frequency, polarization and/or baseline-time.
@@ -1533,6 +1557,28 @@ class UVData(UVBase):
         ----------
         other : UVData object
             Another UVData object which will be added to self.
+        phase_center_radec : array_like of float
+            The phase center to phase the files to before adding the objects in
+            radians (in the ICRS frame). Note that if this keyword is not set
+            and the two UVData objects are phased to different phase centers
+            or if one is phased and one is drift, this method will error
+            because the objects are not compatible.
+        unphase_to_drift : bool
+            If True, unphase the objects to drift before combining them.
+        phase_frame : str
+            The astropy frame to phase to. Either 'icrs' or 'gcrs'.
+            'gcrs' accounts for precession & nutation,
+            'icrs' accounts for precession, nutation & abberation.
+            Only used if `phase_center_radec` is set.
+        orig_phase_frame : str
+            The original phase frame of the data (if it is already phased). Used
+            for unphasing, only if `unphase_to_drift` or `phase_center_radec`
+            are set. Defaults to using the 'phase_center_frame' attribute or
+            'icrs' if that attribute is None.
+        use_ant_pos : bool
+            If True, calculate the phased or unphased uvws directly from the
+            antenna positions rather than from the existing uvws.
+            Only used if `unphase_to_drift` or `phase_center_radec` are set.
         run_check : bool
             Option to check for the existence and proper shapes of parameters
             after combining objects.
@@ -1549,7 +1595,12 @@ class UVData(UVBase):
         ------
         ValueError
             If other is not a UVData object, self and other are not compatible
-            or if data in self and other overlap.
+            or if data in self and other overlap. One way they can not be
+            compatible if if they have different phasing, in that case set
+            `unphase_to_drift` or `phase_center_radec`to (un)phase them so they
+            are compatible.
+            If phase_center_radec is not None and is not length 2.
+
         """
         if inplace:
             this = self
@@ -1557,12 +1608,65 @@ class UVData(UVBase):
             this = copy.deepcopy(self)
 
         # Check that both objects are UVData and valid
-        this.check(check_extra=check_extra, run_check_acceptability=run_check_acceptability)
+        this.check(check_extra=check_extra,
+                   run_check_acceptability=run_check_acceptability)
         if not issubclass(other.__class__, this.__class__):
             if not issubclass(this.__class__, other.__class__):
                 raise ValueError('Only UVData (or subclass) objects can be '
                                  'added to a UVData (or subclass) object')
-        other.check(check_extra=check_extra, run_check_acceptability=run_check_acceptability)
+        other.check(check_extra=check_extra,
+                    run_check_acceptability=run_check_acceptability)
+
+        if phase_center_radec is not None and unphase_to_drift:
+            raise ValueError('phase_center_radec cannot be set if '
+                             'unphase_to_drift is True.')
+
+        if unphase_to_drift:
+            if (this.phase_type != 'drift'):
+                warnings.warn("Unphasing this UVData object to drift")
+                this.unphase_to_drift(phase_frame=orig_phase_frame,
+                                      use_ant_pos=use_ant_pos)
+
+            if (other.phase_type != 'drift'):
+                warnings.warn("Unphasing other UVData object to drift")
+                other.unphase_to_drift(phase_frame=orig_phase_frame,
+                                       use_ant_pos=use_ant_pos)
+
+        if phase_center_radec is not None:
+            if np.array(phase_center_radec).size != 2:
+                raise ValueError('phase_center_radec should have length 2.')
+
+            # If this object is not phased or is not phased close to
+            # phase_center_radec, (re)phase it.
+            # Close is defined using the phase_center_ra/dec tolerances.
+            if (this.phase_type == 'drift'
+                or (not np.isclose(this.phase_center_ra, phase_center_radec[0],
+                                   rtol=this._phase_center_ra.tols[0],
+                                   atol=this._phase_center_ra.tols[1])
+                    or not np.isclose(this.phase_center_dec, phase_center_radec[1],
+                                      rtol=this._phase_center_dec.tols[0],
+                                      atol=this._phase_center_dec.tols[1]))):
+                warnings.warn("Phasing this UVData object to phase_center_radec")
+                this.phase(phase_center_radec[0], phase_center_radec[1],
+                           phase_frame=phase_frame,
+                           orig_phase_frame=orig_phase_frame,
+                           use_ant_pos=use_ant_pos, allow_rephase=True)
+
+            # If other object is not phased or is not phased close to
+            # phase_center_radec, (re)phase it.
+            # Close is defined using the phase_center_ra/dec tolerances.
+            if (other.phase_type == 'drift'
+                or (not np.isclose(other.phase_center_ra, phase_center_radec[0],
+                                   rtol=other._phase_center_ra.tols[0],
+                                   atol=other._phase_center_ra.tols[1])
+                    or not np.isclose(other.phase_center_dec, phase_center_radec[1],
+                                      rtol=other._phase_center_dec.tols[0],
+                                      atol=other._phase_center_dec.tols[1]))):
+                warnings.warn("Phasing other UVData object to phase_center_radec")
+                other.phase(phase_center_radec[0], phase_center_radec[1],
+                            phase_frame=phase_frame,
+                            orig_phase_frame=orig_phase_frame,
+                            use_ant_pos=use_ant_pos, allow_rephase=True)
 
         # Define parameters that must be the same to add objects
         # But phase_center should be the same, even if in drift (empty parameters)
@@ -1807,7 +1911,10 @@ class UVData(UVBase):
         if not inplace:
             return this
 
-    def __iadd__(self, other):
+    def __iadd__(self, other, phase_center_radec=None, unphase_to_drift=False,
+                 phase_frame='icrs', orig_phase_frame=None, use_ant_pos=False,
+                 run_check=True, check_extra=True,
+                 run_check_acceptability=True):
         """
         In place add.
 
@@ -1815,6 +1922,36 @@ class UVData(UVBase):
         ----------
         other : UVData object
             Another UVData object which will be added to self.
+        phase_center_radec : array_like of float
+            The phase center to phase the files to before adding the objects in
+            radians (in the ICRS frame). Note that if this keyword is not set
+            and the two UVData objects are phased to different phase centers
+            or if one is phased and one is drift, this method will error
+            because the objects are not compatible.
+        unphase_to_drift : bool
+            If True, unphase the objects to drift before combining them.
+        phase_frame : str
+            The astropy frame to phase to. Either 'icrs' or 'gcrs'.
+            'gcrs' accounts for precession & nutation,
+            'icrs' accounts for precession, nutation & abberation.
+            Only used if `phase_center_radec` is set.
+        orig_phase_frame : str
+            The original phase frame of the data (if it is already phased). Used
+            for unphasing, only if `unphase_to_drift` or `phase_center_radec`
+            are set. Defaults to using the 'phase_center_frame' attribute or
+            'icrs' if that attribute is None.
+        use_ant_pos : bool
+            If True, calculate the phased or unphased uvws directly from the
+            antenna positions rather than from the existing uvws.
+            Only used if `unphase_to_drift` or `phase_center_radec` are set.
+        run_check : bool
+            Option to check for the existence and proper shapes of parameters
+            after combining objects.
+        check_extra : bool
+            Option to check optional parameters as well as required ones.
+        run_check_acceptability : bool
+            Option to check acceptable range of the values of parameters after
+            combining objects.
 
         Raises
         ------
@@ -1822,10 +1959,19 @@ class UVData(UVBase):
             If other is not a UVData object, self and other are not compatible
             or if data in self and other overlap.
         """
-        self.__add__(other, inplace=True)
+        self.__add__(other, phase_center_radec=phase_center_radec,
+                     unphase_to_drift=unphase_to_drift,
+                     phase_frame=phase_frame, orig_phase_frame=orig_phase_frame,
+                     use_ant_pos=use_ant_pos,
+                     run_check=run_check, check_extra=check_extra,
+                     run_check_acceptability=run_check_acceptability,
+                     inplace=True)
         return self
 
-    def fast_concat(self, other, axis, run_check=True, check_extra=True,
+    def fast_concat(self, other, axis, phase_center_radec=None,
+                    unphase_to_drift=False,
+                    phase_frame='icrs', orig_phase_frame=None, use_ant_pos=False,
+                    run_check=True, check_extra=True,
                     run_check_acceptability=True, inplace=False):
         """
         Concatenate two UVData objects along specified axis with almost no checking of metadata.
@@ -1843,6 +1989,28 @@ class UVData(UVBase):
             Axis to concatenate files along. This enables fast concatenation
             along the specified axis without the normal checking that all other
             metadata agrees. Allowed values are: 'blt', 'freq', 'polarization'.
+        phase_center_radec : array_like of float
+            The phase center to phase the files to before adding the objects in
+            radians (in the ICRS frame). Note that if this keyword is not set
+            and the two UVData objects are phased to different phase centers
+            or if one is phased and one is drift, this method will error
+            because the objects are not compatible.
+        unphase_to_drift : bool
+            If True, unphase the objects to drift before combining them.
+        phase_frame : str
+            The astropy frame to phase to. Either 'icrs' or 'gcrs'.
+            'gcrs' accounts for precession & nutation,
+            'icrs' accounts for precession, nutation & abberation.
+            Only used if `phase_center_radec` is set.
+        orig_phase_frame : str
+            The original phase frame of the data (if it is already phased). Used
+            for unphasing, only if `unphase_to_drift` or `phase_center_radec`
+            are set. Defaults to using the 'phase_center_frame' attribute or
+            'icrs' if that attribute is None.
+        use_ant_pos : bool
+            If True, calculate the phased or unphased uvws directly from the
+            antenna positions rather than from the existing uvws.
+            Only used if `unphase_to_drift` or `phase_center_radec` are set.
         run_check : bool
             Option to check for the existence and proper shapes of parameters
             after combining objects.
@@ -1866,12 +2034,65 @@ class UVData(UVBase):
         else:
             this = copy.deepcopy(self)
         # Check that both objects are UVData and valid
-        this.check(check_extra=check_extra, run_check_acceptability=run_check_acceptability)
+        this.check(check_extra=check_extra,
+                   run_check_acceptability=run_check_acceptability)
         if not issubclass(other.__class__, this.__class__):
             if not issubclass(this.__class__, other.__class__):
                 raise ValueError('Only UVData (or subclass) objects can be '
                                  'added to a UVData (or subclass) object')
-        other.check(check_extra=check_extra, run_check_acceptability=run_check_acceptability)
+        other.check(check_extra=check_extra,
+                    run_check_acceptability=run_check_acceptability)
+
+        if phase_center_radec is not None and unphase_to_drift:
+            raise ValueError('phase_center_radec cannot be set if '
+                             'unphase_to_drift is True.')
+
+        if unphase_to_drift:
+            if (this.phase_type != 'drift'):
+                warnings.warn("Unphasing this UVData object to drift")
+                this.unphase_to_drift(phase_frame=orig_phase_frame,
+                                      use_ant_pos=use_ant_pos)
+
+            if (other.phase_type != 'drift'):
+                warnings.warn("Unphasing other UVData object to drift")
+                other.unphase_to_drift(phase_frame=orig_phase_frame,
+                                       use_ant_pos=use_ant_pos)
+
+        if phase_center_radec is not None:
+            if np.array(phase_center_radec).size != 2:
+                raise ValueError('phase_center_radec should have length 2.')
+
+            # If this object is not phased or is not phased close to
+            # phase_center_radec, (re)phase it.
+            # Close is defined using the phase_center_ra/dec tolerances.
+            if (this.phase_type == 'drift'
+                or (not np.isclose(this.phase_center_ra, phase_center_radec[0],
+                                   rtol=this._phase_center_ra.tols[0],
+                                   atol=this._phase_center_ra.tols[1])
+                    or not np.isclose(this.phase_center_dec, phase_center_radec[1],
+                                      rtol=this._phase_center_dec.tols[0],
+                                      atol=this._phase_center_dec.tols[1]))):
+                warnings.warn("Phasing this UVData object to phase_center_radec")
+                this.phase(phase_center_radec[0], phase_center_radec[1],
+                           phase_frame=phase_frame,
+                           orig_phase_frame=orig_phase_frame,
+                           use_ant_pos=use_ant_pos, allow_rephase=True)
+
+            # If other object is not phased or is not phased close to
+            # phase_center_radec, (re)phase it.
+            # Close is defined using the phase_center_ra/dec tolerances.
+            if (other.phase_type == 'drift'
+                or (not np.isclose(other.phase_center_ra, phase_center_radec[0],
+                                   rtol=other._phase_center_ra.tols[0],
+                                   atol=other._phase_center_ra.tols[1])
+                    or not np.isclose(other.phase_center_dec, phase_center_radec[1],
+                                      rtol=other._phase_center_dec.tols[0],
+                                      atol=other._phase_center_dec.tols[1]))):
+                warnings.warn("Phasing other UVData object to phase_center_radec")
+                other.phase(phase_center_radec[0], phase_center_radec[1],
+                            phase_frame=phase_frame,
+                            orig_phase_frame=orig_phase_frame,
+                            use_ant_pos=use_ant_pos, allow_rephase=True)
 
         allowed_axes = ['blt', 'freq', 'polarization']
         if axis not in allowed_axes:
@@ -1997,8 +2218,8 @@ class UVData(UVBase):
             `keep_all_metadata` is False). This cannot be provided if
             `antenna_nums` is also provided.
         bls : list of tuple, optional
-            A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-            baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
             to keep in the object. For length-2 tuples, the ordering of the numbers
             within the tuple does not matter. For length-3 tuples, the polarization
             string is in the order of the two antennas. If length-3 tuples are
@@ -2010,7 +2231,7 @@ class UVData(UVBase):
             '1_2', '1x_2y').  See tutorial for more examples of valid strings and
             the behavior of different forms for ant_str.
             If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-            be kept for both baselines (1,2) and (2,3) to return a valid
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
             pyuvdata object.
             An ant_str cannot be passed in addition to any of `antenna_nums`,
             `antenna_names`, `bls` args or the `polarizations` parameters,
@@ -2378,8 +2599,8 @@ class UVData(UVBase):
             `keep_all_metadata` is False). This cannot be provided if
             `antenna_nums` is also provided.
         bls : list of tuple, optional
-            A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-            baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
             to keep in the object. For length-2 tuples, the ordering of the numbers
             within the tuple does not matter. For length-3 tuples, the polarization
             string is in the order of the two antennas. If length-3 tuples are
@@ -2391,7 +2612,7 @@ class UVData(UVBase):
             '1_2', '1x_2y').  See tutorial for more examples of valid strings and
             the behavior of different forms for ant_str.
             If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-            be kept for both baselines (1,2) and (2,3) to return a valid
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
             pyuvdata object.
             An ant_str cannot be passed in addition to any of `antenna_nums`,
             `antenna_names`, `bls` args or the `polarizations` parameters,
@@ -2547,18 +2768,22 @@ class UVData(UVBase):
             setattr(other_obj, p, param)
         return other_obj
 
-    def read_uvfits(self, filename, axis=None, antenna_nums=None, antenna_names=None,
-                    ant_str=None, bls=None, frequencies=None,
-                    freq_chans=None, times=None, polarizations=None, blt_inds=None,
+    def read_uvfits(self, filename, axis=None, antenna_nums=None,
+                    antenna_names=None, ant_str=None, bls=None,
+                    frequencies=None, freq_chans=None, times=None,
+                    polarizations=None, blt_inds=None,
                     keep_all_metadata=True, read_data=True, read_metadata=True,
-                    run_check=True, check_extra=True, run_check_acceptability=True):
+                    run_check=True, check_extra=True,
+                    run_check_acceptability=True):
         """
-        Read in header, metadata and data from uvfits file(s).
+        Read in header, metadata and data from a single uvfits file.
 
         Parameters
         ----------
-        filename : str or list of str
-            The uvfits file or list of files to read from.
+        filename : str
+            The uvfits file to read from. Support for a list of files will be
+            deprecated in version 2.0 in favor of a call to the generic
+            `read` method.
         axis : str
             Axis to concatenate files along. This enables fast concatenation
             along the specified axis without the normal checking that all other
@@ -2577,8 +2802,8 @@ class UVData(UVBase):
             unless `keep_all_metadata` is False). This cannot be provided if
             `antenna_nums` is also provided. Ignored if read_data is False.
         bls : list of tuple, optional
-            A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-            baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
             to include when reading data into the object. For length-2 tuples,
             the ordering of the numbers within the tuple does not matter. For
             length-3 tuples, the polarization string is in the order of the two
@@ -2591,7 +2816,7 @@ class UVData(UVBase):
             and polarizations (e.g. '1', '1_2', '1x_2y').  See tutorial for more
             examples of valid strings and the behavior of different forms for ant_str.
             If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-            be kept for both baselines (1,2) and (2,3) to return a valid
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
             pyuvdata object.
             An ant_str cannot be passed in addition to any of `antenna_nums`,
             `antenna_names`, `bls` args or the `polarizations` parameters,
@@ -2618,14 +2843,16 @@ class UVData(UVBase):
             that do not have data associated with them after the select option.
         read_data : bool
             Read in the visibility and flag data. If set to false, only the
-            basic header info and metadata (if read_metadata is True) will be
-            read in. Setting read_data to False results in an incompletely
-            defined object (check will not pass).
-        read_metadata: : bool
+            basic header info and metadata read in. Setting read_data to False
+            results in a metdata only object.
+        read_metadata : bool
+            Deprecated, will be removed in version 2.0, after which metadata
+            will always be read along with header data.
             Read in metadata (times, baselines, uvws) as well as basic header
             info. Only used if read_data is False (metadata will be read if data
             is read). If both read_data and read_metadata are false, only basic
-            header info is read in.
+            header info is read in, which will result in an incompletely
+            defined object -- check will not pass.
         run_check : bool
             Option to check for the existence and proper shapes of parameters
             after after reading in the file (the default is True,
@@ -2638,113 +2865,52 @@ class UVData(UVBase):
             Option to check acceptable range of the values of parameters after
             reading in the file (the default is True, meaning the acceptable
             range check will be done). Ignored if read_data is False.
+
+        Raises
+        ------
+        IOError
+            If filename doesn't exist.
+        ValueError
+            If incompatible select keywords are set (e.g. `ant_str` with other
+            antenna selectors, `times` and `time_range`) or select keywords
+            exclude all data or if keywords are set to the wrong type.
+            If the data are multi source or have multiple
+            spectral windows.
+            If the metadata are not internally consistent or missing.
+
         """
         from . import uvfits
-        # work out what function should be called depending on what's
-        # already defined on the object
-        if self.freq_array is not None:
-            hdr_loaded = True
-        else:
-            hdr_loaded = False
-        if self.data_array is not None:
-            data_loaded = True
-        else:
-            data_loaded = False
-
-        if not read_data and not read_metadata:
-            # not reading data or metadata, use read_uvfits to get header
-            func = 'read_uvfits'
-        elif not read_data:
-            # reading metadata but not data
-            if hdr_loaded:
-                # header already read, use read_uvfits_metadata
-                # (which will error if the data have already been read)
-                func = 'read_uvfits_metadata'
-            else:
-                # header not read, use read_uvfits
-                func = 'read_uvfits'
-        else:
-            # reading data
-            if hdr_loaded and not data_loaded:
-                # header already read, data not read, use read_uvfits_data
-                # (which will read metadata if it doesn't exist)
-                func = 'read_uvfits_data'
-            else:
-                # header not read or object already fully defined,
-                # use read_uvfits to get a new object
-                func = 'read_uvfits'
-
-        if isinstance(filename, (list, tuple)):
-            if not read_data and not read_metadata:
-                raise ValueError('A list of files cannot be used when just '
-                                 'reading the header (read_data and read_metadata are False)')
-            if func == 'read_uvfits_data':
-                raise ValueError('A list of files cannot be used when just '
-                                 'reading data (metadata already exists)')
-
-            self.read_uvfits(filename[0], antenna_nums=antenna_nums,
-                             antenna_names=antenna_names, ant_str=ant_str,
-                             bls=bls, frequencies=frequencies,
-                             freq_chans=freq_chans, times=times,
-                             polarizations=polarizations, blt_inds=blt_inds,
-                             read_data=read_data, read_metadata=read_metadata,
-                             run_check=run_check, check_extra=check_extra,
-                             run_check_acceptability=run_check_acceptability,
-                             keep_all_metadata=keep_all_metadata)
-            if len(filename) > 1:
-                for f in filename[1:]:
-                    uv2 = UVData()
-                    uv2.read_uvfits(f, antenna_nums=antenna_nums,
-                                    antenna_names=antenna_names, ant_str=ant_str,
-                                    bls=bls, frequencies=frequencies,
-                                    freq_chans=freq_chans, times=times,
-                                    polarizations=polarizations, blt_inds=blt_inds,
-                                    read_data=read_data, read_metadata=read_metadata,
-                                    run_check=run_check, check_extra=check_extra,
-                                    run_check_acceptability=run_check_acceptability,
-                                    keep_all_metadata=keep_all_metadata)
-                    if axis is not None:
-                        self.fast_concat(uv2, axis, run_check=run_check,
-                                         check_extra=check_extra,
-                                         run_check_acceptability=run_check_acceptability,
-                                         inplace=True)
-                    else:
-                        self += uv2
-                del(uv2)
-        else:
-            if func == 'read_uvfits':
-                uvfits_obj = uvfits.UVFITS()
-                uvfits_obj.read_uvfits(filename, antenna_nums=antenna_nums,
-                                       antenna_names=antenna_names, ant_str=ant_str,
-                                       bls=bls, frequencies=frequencies,
-                                       freq_chans=freq_chans, times=times,
-                                       polarizations=polarizations, blt_inds=blt_inds,
-                                       read_data=read_data, read_metadata=read_metadata,
-                                       run_check=run_check, check_extra=check_extra,
-                                       run_check_acceptability=run_check_acceptability,
-                                       keep_all_metadata=keep_all_metadata)
-                self._convert_from_filetype(uvfits_obj)
-                del(uvfits_obj)
-            elif func == 'read_uvfits_metadata':
-                # can only be one file, it would have errored earlier otherwise
-                uvfits_obj = self._convert_to_filetype('uvfits')
-                uvfits_obj.read_uvfits_metadata(
-                    filename, run_check_acceptability=run_check_acceptability)
-                self._convert_from_filetype(uvfits_obj)
-                del(uvfits_obj)
-            elif func == 'read_uvfits_data':
-                # can only be one file, it would have errored earlier otherwise
-                uvfits_obj = self._convert_to_filetype('uvfits')
-                uvfits_obj.read_uvfits_data(filename, antenna_nums=antenna_nums,
-                                            antenna_names=antenna_names, ant_str=ant_str,
-                                            bls=bls, frequencies=frequencies,
-                                            freq_chans=freq_chans, times=times,
-                                            polarizations=polarizations, blt_inds=blt_inds,
-                                            run_check=run_check, check_extra=check_extra,
-                                            run_check_acceptability=run_check_acceptability,
-                                            keep_all_metadata=keep_all_metadata)
-                self._convert_from_filetype(uvfits_obj)
-                del(uvfits_obj)
+
+        if isinstance(filename, (list, tuple, np.ndarray)):
+            warnings.warn('Please use the generic `read` '
+                          'method to read multiple uvfits files. Support for '
+                          'reading multiple files with this method will be '
+                          'removed in version 2.0.', DeprecationWarning)
+
+            self.read(filename, file_type='uvfits', axis=axis,
+                      antenna_nums=antenna_nums,
+                      antenna_names=antenna_names, ant_str=ant_str,
+                      bls=bls, frequencies=frequencies,
+                      freq_chans=freq_chans, times=times,
+                      polarizations=polarizations, blt_inds=blt_inds,
+                      read_data=read_data, read_metadata=read_metadata,
+                      run_check=run_check, check_extra=check_extra,
+                      run_check_acceptability=run_check_acceptability,
+                      keep_all_metadata=keep_all_metadata)
+            return
+
+        uvfits_obj = uvfits.UVFITS()
+        uvfits_obj.read_uvfits(filename, antenna_nums=antenna_nums,
+                               antenna_names=antenna_names, ant_str=ant_str,
+                               bls=bls, frequencies=frequencies,
+                               freq_chans=freq_chans, times=times,
+                               polarizations=polarizations, blt_inds=blt_inds,
+                               read_data=read_data, read_metadata=read_metadata,
+                               run_check=run_check, check_extra=check_extra,
+                               run_check_acceptability=run_check_acceptability,
+                               keep_all_metadata=keep_all_metadata)
+        self._convert_from_filetype(uvfits_obj)
+        del(uvfits_obj)
 
     def write_uvfits(self, filename, spoof_nonessential=False, write_lst=True,
                      force_phase=False, run_check=True, check_extra=True,
@@ -2775,6 +2941,21 @@ class UVData(UVBase):
             Option to check acceptable range of the values of parameters before
             writing the file (the default is True, meaning the acceptable
             range check will be done).
+
+        Raises
+        ------
+        ValueError
+            The `phase_type` of the object is "drift" and the `force_phase` keyword is not set.
+            The `phase_type` of the object is "unknown".
+            If the frequencies are not evenly spaced or are separated by more
+            than their channel width.
+            The polarization values are not evenly spaced.
+            Any of ['antenna_positions', 'gst0', 'rdate', 'earth_omega', 'dut1',
+            'timesys'] are not set on the object and `spoof_nonessential` is False.
+            If the `timesys` parameter is not set to "UTC".
+        TypeError
+            If any entry in extra_keywords is not a single string or number.
+
         """
         uvfits_obj = self._convert_to_filetype('uvfits')
         uvfits_obj.write_uvfits(filename, spoof_nonessential=spoof_nonessential,
@@ -2790,8 +2971,11 @@ class UVData(UVBase):
 
         Parameters
         ----------
-        filepath : str or list of str
-            The measurement set file directory or list of directories to read from.
+        filepath : str
+            The measurement set root directory to read from.
+            Support for a list/array of file directories will be
+            deprecated in version 2.0 in favor of a call to the generic
+            `read` method.
         axis : str
             Axis to concatenate files along. This enables fast concatenation
             along the specified axis without the normal checking that all other
@@ -2815,34 +2999,39 @@ class UVData(UVBase):
             Option to check acceptable range of the values of parameters after
             reading in the file (the default is True, meaning the acceptable
             range check will be done).
+
+        Raises
+        ------
+        IOError
+            If root file directory doesn't exist.
+        ValueError
+            If the `data_column` is not set to an allowed value.
+            If the data are have multiple subarrays or are multi source or have
+            multiple spectral windows.
+            If the data have multiple data description ID values.
+
         """
         from . import ms
 
-        if isinstance(filepath, (list, tuple)):
-            self.read_ms(filepath[0], run_check=run_check, check_extra=check_extra,
-                         run_check_acceptability=run_check_acceptability,
-                         data_column=data_column, pol_order=pol_order)
-            if len(filepath) > 1:
-                for f in filepath[1:]:
-                    uv2 = UVData()
-                    uv2.read_ms(f, run_check=run_check, check_extra=check_extra,
-                                run_check_acceptability=run_check_acceptability,
-                                data_column=data_column, pol_order=pol_order)
-                    if axis is not None:
-                        self.fast_concat(uv2, axis, run_check=run_check,
-                                         check_extra=check_extra,
-                                         run_check_acceptability=run_check_acceptability,
-                                         inplace=True)
-                    else:
-                        self += uv2
-                del(uv2)
-        else:
-            ms_obj = ms.MS()
-            ms_obj.read_ms(filepath, run_check=run_check, check_extra=check_extra,
-                           run_check_acceptability=run_check_acceptability,
-                           data_column=data_column, pol_order=pol_order)
-            self._convert_from_filetype(ms_obj)
-            del(ms_obj)
+        if isinstance(filepath, (list, tuple, np.ndarray)):
+
+            warnings.warn('Please use the generic `read` '
+                          'method to read multiple ms files. Support for '
+                          'reading multiple files with this method will be '
+                          'removed in version 2.0.', DeprecationWarning)
+
+            self.read(filepath, file_type='ms', axis=axis, run_check=run_check,
+                      check_extra=check_extra,
+                      run_check_acceptability=run_check_acceptability,
+                      data_column=data_column, pol_order=pol_order)
+            return
+
+        ms_obj = ms.MS()
+        ms_obj.read_ms(filepath, run_check=run_check, check_extra=check_extra,
+                       run_check_acceptability=run_check_acceptability,
+                       data_column=data_column, pol_order=pol_order)
+        self._convert_from_filetype(ms_obj)
+        del(ms_obj)
 
     def read_fhd(self, filelist, use_model=False, axis=None,
                  run_check=True, check_extra=True, run_check_acceptability=True):
@@ -2851,10 +3040,12 @@ class UVData(UVBase):
 
         Parameters
         ----------
-        filelist : list of str
-            The list of FHD save files to read from. Must include at least one
-            polarization file, a params file and a flag file. Can also be a list
-            of lists to read multiple data sets.
+        filelist : array_like of str
+            The list/array of FHD save files to read from. Must include at
+            least one polarization file, a params file and a flag file.
+            Support for a list of lists of files for multiple data sets will be
+            deprecated in version 2.0 in favor of a call to the generic
+            `read` method.
         use_model : bool
             Option to read in the model visibilities rather than the dirty
             visibilities (the default is False, meaning the dirty visibilities
@@ -2877,33 +3068,34 @@ class UVData(UVBase):
             Option to check acceptable range of the values of parameters after
             reading in the file (the default is True, meaning the acceptable
             range check will be done).
+
+        Raises
+        ------
+        ValueError
+            If required files are missing or multiple files for any polarization
+            are included in filelist.
+            If there is no recognized key for visibility weights in the flags_file.
+
         """
         from . import fhd
-        if isinstance(filelist[0], (list, tuple)):
-            self.read_fhd(filelist[0], use_model=use_model, run_check=run_check,
-                          check_extra=check_extra,
-                          run_check_acceptability=run_check_acceptability)
-            if len(filelist) > 1:
-                for f in filelist[1:]:
-                    uv2 = UVData()
-                    uv2.read_fhd(f, use_model=use_model, run_check=run_check,
-                                 check_extra=check_extra,
-                                 run_check_acceptability=run_check_acceptability)
-                    if axis is not None:
-                        self.fast_concat(uv2, axis, run_check=run_check,
-                                         check_extra=check_extra,
-                                         run_check_acceptability=run_check_acceptability,
-                                         inplace=True)
-                    else:
-                        self += uv2
-                del(uv2)
-        else:
-            fhd_obj = fhd.FHD()
-            fhd_obj.read_fhd(filelist, use_model=use_model, run_check=run_check,
-                             check_extra=check_extra,
-                             run_check_acceptability=run_check_acceptability)
-            self._convert_from_filetype(fhd_obj)
-            del(fhd_obj)
+        if isinstance(filelist[0], (list, tuple, np.ndarray)):
+            warnings.warn('Please use the generic `read` '
+                          'method to read multiple fhd files. Support for '
+                          'reading multiple files with this method will be '
+                          'removed in version 2.0.', DeprecationWarning)
+
+            self.read(filelist, file_type='fhd', axis=axis, use_model=use_model,
+                      run_check=run_check,
+                      check_extra=check_extra,
+                      run_check_acceptability=run_check_acceptability)
+            return
+
+        fhd_obj = fhd.FHD()
+        fhd_obj.read_fhd(filelist, use_model=use_model, run_check=run_check,
+                         check_extra=check_extra,
+                         run_check_acceptability=run_check_acceptability)
+        self._convert_from_filetype(fhd_obj)
+        del(fhd_obj)
 
     def read_miriad(self, filepath, axis=None, antenna_nums=None, ant_str=None,
                     bls=None, polarizations=None, time_range=None, read_data=True,
@@ -2914,8 +3106,11 @@ class UVData(UVBase):
 
         Parameters
         ----------
-        filepath : str or list of str
-            The miriad file directory or list of directories to read from.
+        filepath : str
+            The miriad root directory to read from.
+            Support for a list/array of file directories will be
+            deprecated in version 2.0 in favor of a call to the generic
+            `read` method.
         axis : str
             Axis to concatenate files along. This enables fast concatenation
             along the specified axis without the normal checking that all other
@@ -2926,8 +3121,8 @@ class UVData(UVBase):
         antenna_nums : array_like of int, optional
             The antennas numbers to read into the object.
         bls : list of tuple, optional
-            A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-            baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
             to include when reading data into the object. For length-2 tuples,
             the ordering of the numbers within the tuple does not matter. For
             length-3 tuples, the polarization string is in the order of the two
@@ -2940,7 +3135,7 @@ class UVData(UVBase):
             and polarizations (e.g. '1', '1_2', '1x_2y').  See tutorial for more
             examples of valid strings and the behavior of different forms for ant_str.
             If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-            be kept for both baselines (1,2) and (2,3) to return a valid
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
             pyuvdata object.
             An ant_str cannot be passed in addition to any of `antenna_nums`,
             `bls` or `polarizations` parameters, if it is a ValueError will be raised.
@@ -2973,51 +3168,45 @@ class UVData(UVBase):
             Option to check acceptable range of the values of parameters after
             reading in the file (the default is True, meaning the acceptable
             range check will be done). Ignored if read_data is False.
+
+        Raises
+        ------
+        IOError
+            If root file directory doesn't exist.
+        ValueError
+            If incompatible select keywords are set (e.g. `ant_str` with other
+            antenna selectors, `times` and `time_range`) or select keywords
+            exclude all data or if keywords are set to the wrong type.
+            If the data are multi source or have multiple
+            spectral windows.
+            If the metadata are not internally consistent.
         """
         from . import miriad
-        if isinstance(filepath, (list, tuple)):
-            self.read_miriad(filepath[0], correct_lat_lon=correct_lat_lon,
-                             run_check=run_check, check_extra=check_extra,
-                             run_check_acceptability=run_check_acceptability,
-                             phase_type=phase_type, antenna_nums=antenna_nums,
-                             ant_str=ant_str, bls=bls,
-                             polarizations=polarizations, time_range=time_range)
-            if len(filepath) > 1:
-                for f in filepath[1:]:
-                    uv2 = UVData()
-                    uv2.read_miriad(f, correct_lat_lon=correct_lat_lon,
-                                    run_check=run_check, check_extra=check_extra,
-                                    run_check_acceptability=run_check_acceptability,
-                                    phase_type=phase_type, antenna_nums=antenna_nums,
-                                    ant_str=ant_str, bls=bls,
-                                    polarizations=polarizations, time_range=time_range)
-                    if axis is not None:
-                        self.fast_concat(uv2, axis, run_check=run_check,
-                                         check_extra=check_extra,
-                                         run_check_acceptability=run_check_acceptability,
-                                         inplace=True)
-                    else:
-                        self += uv2
-                del(uv2)
-        else:
-            # work out what function should be called
-            if read_data:
-                # reading data, use read_miriad
-                miriad_obj = miriad.Miriad()
-                miriad_obj.read_miriad(filepath, correct_lat_lon=correct_lat_lon,
-                                       run_check=run_check, check_extra=check_extra,
-                                       run_check_acceptability=run_check_acceptability,
-                                       phase_type=phase_type, antenna_nums=antenna_nums,
-                                       ant_str=ant_str, bls=bls,
-                                       polarizations=polarizations, time_range=time_range)
-                self._convert_from_filetype(miriad_obj)
-                del(miriad_obj)
-            else:
-                # not reading data. Will error if data_array is already defined.
-                miriad_obj = self._convert_to_filetype('miriad')
-                miriad_obj.read_miriad_metadata(filepath, correct_lat_lon=correct_lat_lon)
-                self._convert_from_filetype(miriad_obj)
-                del(miriad_obj)
+        if isinstance(filepath, (list, tuple, np.ndarray)):
+            warnings.warn('Please use the generic `read` '
+                          'method to read multiple miriad files. Support for '
+                          'reading multiple files with this method will be '
+                          'removed in version 2.0.', DeprecationWarning)
+
+            self.read(filepath, file_type='miriad', axis=axis,
+                      correct_lat_lon=correct_lat_lon,
+                      run_check=run_check, check_extra=check_extra,
+                      run_check_acceptability=run_check_acceptability,
+                      phase_type=phase_type, antenna_nums=antenna_nums,
+                      ant_str=ant_str, bls=bls,
+                      polarizations=polarizations, time_range=time_range)
+            return
+
+        miriad_obj = miriad.Miriad()
+        miriad_obj.read_miriad(filepath, correct_lat_lon=correct_lat_lon,
+                               run_check=run_check, check_extra=check_extra,
+                               run_check_acceptability=run_check_acceptability,
+                               read_data=read_data,
+                               phase_type=phase_type, antenna_nums=antenna_nums,
+                               ant_str=ant_str, bls=bls,
+                               polarizations=polarizations, time_range=time_range)
+        self._convert_from_filetype(miriad_obj)
+        del(miriad_obj)
 
     def write_miriad(self, filepath, run_check=True, check_extra=True,
                      run_check_acceptability=True, clobber=False, no_antnums=False):
@@ -3027,7 +3216,7 @@ class UVData(UVBase):
         Parameters
         ----------
         filename : str
-            The miriad file directory to write to.
+            The miriad root directory to write to.
         run_check : bool
             Option to check for the existence and proper shapes of parameters
             after before writing the file (the default is True,
@@ -3044,6 +3233,16 @@ class UVData(UVBase):
         no_antnums : bool
             Option to not write the antnums variable to the file.
             Should only be used for testing purposes.
+
+        Raises
+        ------
+        ValueError
+            If the frequencies are not evenly spaced or are separated by more
+            than their channel width.
+            The `phase_type` of the object is "unknown".
+        TypeError
+            If any entry in extra_keywords is not a single string or number.
+
         """
         miriad_obj = self._convert_to_filetype('miriad')
         miriad_obj.write_miriad(filepath, run_check=run_check, check_extra=check_extra,
@@ -3052,8 +3251,8 @@ class UVData(UVBase):
         del(miriad_obj)
 
     def read_mwa_corr_fits(self, filelist, axis=None, use_cotter_flags=False,
-                           correct_cable_len=False, phase_data=False,
-                           phase_center=None, run_check=True,
+                           correct_cable_len=False, phase_to_pointing_center=False,
+                           phase_data=None, phase_center=None, run_check=True,
                            check_extra=True, run_check_acceptability=True):
         """
         Read in MWA correlator gpu box files.
@@ -3063,7 +3262,9 @@ class UVData(UVBase):
         filelist : list of str
             The list of MWA correlator files to read from. Must include at
             least one fits file and only one metafits file per data set.
-            Can also be a list of lists to read multiple data sets.
+            Support for a list of lists of files for multiple data sets will be
+            deprecated in version 2.0 in favor of a call to the generic
+            `read` method.
         axis : str
             Axis to concatenate files along. This enables fast concatenation
             along the specified axis without the normal checking that all other
@@ -3073,16 +3274,18 @@ class UVData(UVBase):
             multiple files are passed.
         use_cotter_flags : bool
             Option to use cotter output mwaf flag files. Otherwise flagging
-            will only be applied to missing data and bad antennas. Default is
-            False.
+            will only be applied to missing data and bad antennas.
         correct_cable_len : bool
-            Option to apply a cable delay correction. Default is False.
+            Option to apply a cable delay correction.
+        phase_to_pointing_center : bool
+            Option to phase to the observation pointing center.
         phase_data : bool
-            Option to phase data. Default is False.
+            Deprecated, use phase_to_pointing_center. Option to phase data,
+            default is no phasing.
         phase_center : tuple, optional
+            Deprecated, use the `read` method to phase to arbitrary locations.
             A tuple containing the ra and dec coordinates in radians of a
-            specific location to phase data to. If not specified, the
-            observation pointing center will be used when phase_data is True.
+            specific location to phase data to.
         run_check : bool
             Option to check for the existence and proper shapes of parameters
             after after reading in the file (the default is True,
@@ -3094,40 +3297,65 @@ class UVData(UVBase):
             Option to check acceptable range of the values of parameters after
             reading in the file (the default is True, meaning the acceptable
             range check will be done).
+
+        Raises
+        ------
+        ValueError
+            If required files are missing or multiple files metafits files are included in filelist.
+            If files from different observations are included in filelist.
+            If files in fileslist have different fine channel widths
+            If file types other than fits, metafits, and mwaf files are included in filelist.
+
         """
         from . import mwa_corr_fits
 
-        if isinstance(filelist[0], (list, tuple)):
-            self.read_mwa_corr_fits(filelist[0], use_cotter_flags=use_cotter_flags,
+        call_read = False
+        if phase_center is not None:
+            warnings.warn('The `phase_center` keyword is deprecated. '
+                          'Please use the generic `read` '
+                          'method to phase to an arbitrary phase center. '
+                          'Support for the `phase_center` keyword will be '
+                          'removed in version 2.0.', DeprecationWarning)
+            call_read = True
+
+        if phase_data is not None:
+            warnings.warn('The `phase_data` keyword is deprecated. '
+                          'Please use the `phase_to_pointing_center` '
+                          'keyword to phase to the pointing center. '
+                          'Support for the `phase_data` keyword will be '
+                          'removed in version 2.0.', DeprecationWarning)
+            if phase_center is None:
+                phase_to_pointing_center = True
+            else:
+                phase_to_pointing_center = False
+
+        if isinstance(filelist[0], (list, tuple, np.ndarray)):
+            warnings.warn('Please use the generic `read` '
+                          'method to read multiple mwa_corr_fits file sets. '
+                          'Support for '
+                          'reading multiple file sets with this method will be '
+                          'removed in version 2.0.', DeprecationWarning)
+            call_read = True
+
+        if call_read:
+
+            self.read(filelist, file_type='mwa_corr_fits', axis=axis,
+                      use_cotter_flags=use_cotter_flags,
+                      correct_cable_len=correct_cable_len,
+                      phase_to_pointing_center=phase_to_pointing_center,
+                      phase_center_radec=phase_center, run_check=run_check,
+                      check_extra=check_extra,
+                      run_check_acceptability=run_check_acceptability)
+            return
+
+        corr_obj = mwa_corr_fits.MWACorrFITS()
+        corr_obj.read_mwa_corr_fits(filelist, use_cotter_flags=use_cotter_flags,
                                     correct_cable_len=correct_cable_len,
-                                    phase_data=phase_data, phase_center=phase_center,
+                                    phase_to_pointing_center=phase_to_pointing_center,
                                     run_check=run_check, check_extra=check_extra,
                                     run_check_acceptability=run_check_acceptability)
-            if len(filelist) > 1:
-                for f in filelist[1:]:
-                    uv2 = UVData()
-                    uv2.read_mwa_corr_fits(f, use_cotter_flags=use_cotter_flags,
-                                           correct_cable_len=correct_cable_len,
-                                           phase_data=phase_data, phase_center=phase_center,
-                                           run_check=run_check, check_extra=check_extra,
-                                           run_check_acceptability=run_check_acceptability)
-                    if axis is not None:
-                        self.fast_concat(uv2, axis, run_check=run_check,
-                                         check_extra=check_extra,
-                                         run_check_acceptability=run_check_acceptability,
-                                         inplace=True)
-                    else:
-                        self += uv2
-                del(uv2)
-        else:
-            corr_obj = mwa_corr_fits.MWACorrFITS()
-            corr_obj.read_mwa_corr_fits(filelist, use_cotter_flags=use_cotter_flags,
-                                        correct_cable_len=correct_cable_len,
-                                        phase_data=phase_data, phase_center=phase_center,
-                                        run_check=run_check, check_extra=check_extra,
-                                        run_check_acceptability=run_check_acceptability)
-            self._convert_from_filetype(corr_obj)
-            del(corr_obj)
+        self._convert_from_filetype(corr_obj)
+        del(corr_obj)
 
     def read_uvh5(self, filename, axis=None, antenna_nums=None, antenna_names=None,
                   ant_str=None, bls=None, frequencies=None, freq_chans=None,
@@ -3139,8 +3367,11 @@ class UVData(UVBase):
 
         Parameters
         ----------
-        filename : str or list of str
-             The UVH5 file or list of files to read from.
+        filename : str
+             The UVH5 file to read from.
+             Support for a list/array of files will be
+             deprecated in version 2.0 in favor of a call to the generic
+             `read` method.
         axis : str
             Axis to concatenate files along. This enables fast concatenation
             along the specified axis without the normal checking that all other
@@ -3159,8 +3390,8 @@ class UVData(UVBase):
             unless `keep_all_metadata` is False). This cannot be provided if
             `antenna_nums` is also provided. Ignored if read_data is False.
         bls : list of tuple, optional
-            A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-            baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
             to include when reading data into the object. For length-2 tuples,
             the ordering of the numbers within the tuple does not matter. For
             length-3 tuples, the polarization string is in the order of the two
@@ -3173,7 +3404,7 @@ class UVData(UVBase):
             and polarizations (e.g. '1', '1_2', '1x_2y').  See tutorial for more
             examples of valid strings and the behavior of different forms for ant_str.
             If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-            be kept for both baselines (1,2) and (2,3) to return a valid
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
             pyuvdata object.
             An ant_str cannot be passed in addition to any of `antenna_nums`,
             `antenna_names`, `bls` args or the `polarizations` parameters,
@@ -3219,51 +3450,48 @@ class UVData(UVBase):
             Option to check acceptable range of the values of parameters after
             reading in the file (the default is True, meaning the acceptable
             range check will be done). Ignored if read_data is False.
+
+        Raises
+        ------
+        IOError
+            If filename doesn't exist.
+        ValueError
+            If the data_array_dtype is not a complex dtype.
+            If incompatible select keywords are set (e.g. `ant_str` with other
+            antenna selectors, `times` and `time_range`) or select keywords
+            exclude all data or if keywords are set to the wrong type.
+
         """
         from . import uvh5
-        if isinstance(filename, (list, tuple)):
+        if isinstance(filename, (list, tuple, np.ndarray)):
+            warnings.warn('Please use the generic `read` '
+                          'method to read multiple uvh5 files. Support for '
+                          'reading multiple files with this method will be '
+                          'removed in version 2.0.', DeprecationWarning)
+
+            self.read(filename, file_type='uvh5', axis=axis,
+                      antenna_nums=antenna_nums,
+                      antenna_names=antenna_names, ant_str=ant_str, bls=bls,
+                      frequencies=frequencies, freq_chans=freq_chans, times=times,
+                      polarizations=polarizations, blt_inds=blt_inds,
+                      read_data=read_data, run_check=run_check,
+                      check_extra=check_extra,
+                      run_check_acceptability=run_check_acceptability,
+                      data_array_dtype=data_array_dtype,
+                      keep_all_metadata=keep_all_metadata)
+            return
 
-            self.read_uvh5(filename[0], antenna_nums=antenna_nums,
+        uvh5_obj = uvh5.UVH5()
+        uvh5_obj.read_uvh5(filename, antenna_nums=antenna_nums,
                            antenna_names=antenna_names, ant_str=ant_str, bls=bls,
                            frequencies=frequencies, freq_chans=freq_chans, times=times,
                            polarizations=polarizations, blt_inds=blt_inds,
-                           read_data=read_data, run_check=run_check,
-                           check_extra=check_extra,
+                           read_data=read_data, run_check=run_check, check_extra=check_extra,
                            run_check_acceptability=run_check_acceptability,
                            data_array_dtype=data_array_dtype,
                            keep_all_metadata=keep_all_metadata)
-            if len(filename) > 1:
-                for f in filename[1:]:
-                    uv2 = UVData()
-                    uv2.read_uvh5(f, axis=axis, antenna_nums=antenna_nums,
-                                  antenna_names=antenna_names, ant_str=ant_str, bls=bls,
-                                  frequencies=frequencies, freq_chans=freq_chans,
-                                  times=times, polarizations=polarizations,
-                                  blt_inds=blt_inds, read_data=read_data,
-                                  run_check=run_check, check_extra=check_extra,
-                                  run_check_acceptability=run_check_acceptability,
-                                  data_array_dtype=data_array_dtype,
-                                  keep_all_metadata=keep_all_metadata)
-                    if axis is not None:
-                        self.fast_concat(uv2, axis, run_check=run_check,
-                                         check_extra=check_extra,
-                                         run_check_acceptability=run_check_acceptability,
-                                         inplace=True)
-                    else:
-                        self += uv2
-                del(uv2)
-        else:
-            uvh5_obj = uvh5.UVH5()
-            uvh5_obj.read_uvh5(filename, antenna_nums=antenna_nums,
-                               antenna_names=antenna_names, ant_str=ant_str, bls=bls,
-                               frequencies=frequencies, freq_chans=freq_chans, times=times,
-                               polarizations=polarizations, blt_inds=blt_inds,
-                               read_data=read_data, run_check=run_check, check_extra=check_extra,
-                               run_check_acceptability=run_check_acceptability,
-                               data_array_dtype=data_array_dtype,
-                               keep_all_metadata=keep_all_metadata)
-            self._convert_from_filetype(uvh5_obj)
-            del(uvh5_obj)
+        self._convert_from_filetype(uvh5_obj)
+        del(uvh5_obj)
 
     def write_uvh5(self, filename, run_check=True, check_extra=True,
                    run_check_acceptability=True, clobber=False,
@@ -3392,8 +3620,8 @@ class UVData(UVBase):
             (antenna positions and names for the removed antennas will be retained).
             This cannot be provided if `antenna_nums` is also provided.
         bls : list of tuple, optional
-            A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-            baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
             to include when writing data into the file. For length-2 tuples,
             the ordering of the numbers within the tuple does not matter. For
             length-3 tuples, the polarization string is in the order of the two
@@ -3406,7 +3634,7 @@ class UVData(UVBase):
             and polarizations (e.g. '1', '1_2', '1x_2y').  See tutorial for more
             examples of valid strings and the behavior of different forms for ant_str.
             If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-            be kept for both baselines (1,2) and (2,3) to return a valid
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
             pyuvdata object.
             An ant_str cannot be passed in addition to any of `antenna_nums`,
             `antenna_names`, `bls` args or the `polarizations` parameters,
@@ -3443,21 +3671,26 @@ class UVData(UVBase):
                                  add_to_history=add_to_history)
         del(uvh5_obj)
 
-    def read(self, filename, axis=None, file_type=None,
+    def read(self, filename, axis=None, file_type=None, allow_rephase=True,
+             phase_center_radec=None, unphase_to_drift=False, phase_frame='icrs',
+             orig_phase_frame=None, phase_use_ant_pos=False,
              antenna_nums=None, antenna_names=None, ant_str=None, bls=None,
              frequencies=None, freq_chans=None, times=None, polarizations=None,
              blt_inds=None, time_range=None, keep_all_metadata=True,
              read_metadata=True, read_data=True,
              phase_type=None, correct_lat_lon=True, use_model=False,
-             data_column='DATA', pol_order='AIPS', data_array_dtype=np.complex128,
+             data_column='DATA', pol_order='AIPS',
+             data_array_dtype=np.complex128,
+             use_cotter_flags=False, correct_cable_len=False,
+             phase_to_pointing_center=False,
              run_check=True, check_extra=True, run_check_acceptability=True):
         """
         Read a generic file into a UVData object.
 
         Parameters
         ----------
-        filename : str or list of str
-            The file(s) or list(s) of files to read from.
+        filename : str or array_like of str
+            The file(s) or list(s) (or array(s)) of files to read from.
         file_type : str
             One of ['uvfits', 'miriad', 'fhd', 'ms', 'uvh5'] or None.
             If None, the code attempts to guess what the file type is.
@@ -3473,6 +3706,30 @@ class UVData(UVBase):
             objects. Please see the docstring for fast_concat for details.
             Allowed values are: 'blt', 'freq', 'polarization'. Only used if
             multiple files are passed.
+        allow_rephase :  bool
+            Allow rephasing of phased file data so that data from files with
+            different phasing can be combined.
+        phase_center_radec : array_like of float
+            The phase center to phase the files to before adding the objects in
+            radians (in the ICRS frame). If set to None and multiple files are
+            read with different phase centers, the phase center of the first
+            file will be used.
+        unphase_to_drift : bool
+            Unphase the data from the files before combining them.
+        phase_frame : str
+            The astropy frame to phase to. Either 'icrs' or 'gcrs'.
+            'gcrs' accounts for precession & nutation,
+            'icrs' accounts for precession, nutation & abberation.
+            Only used if `phase_center_radec` is set.
+        orig_phase_frame : str
+            The original phase frame of the data (if it is already phased). Used
+            for unphasing, only if `unphase_to_drift` or `phase_center_radec`
+            are set. Defaults to using the 'phase_center_frame' attribute or
+            'icrs' if that attribute is None.
+        phase_use_ant_pos : bool
+            If True, calculate the phased or unphased uvws directly from the
+            antenna positions rather than from the existing uvws.
+            Only used if `unphase_to_drift` or `phase_center_radec` are set.
         antenna_nums : array_like of int, optional
             The antennas numbers to include when reading data into the object
             (antenna positions and names for the removed antennas will be retained
@@ -3484,8 +3741,8 @@ class UVData(UVBase):
             unless `keep_all_metadata` is False). This cannot be provided if
             `antenna_nums` is also provided. Ignored if read_data is False.
         bls : list of tuple, optional
-            A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-            baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
             to include when reading data into the object. For length-2 tuples,
             the ordering of the numbers within the tuple does not matter. For
             length-3 tuples, the polarization string is in the order of the two
@@ -3498,7 +3755,7 @@ class UVData(UVBase):
             and polarizations (e.g. '1', '1_2', '1x_2y').  See tutorial for more
             examples of valid strings and the behavior of different forms for ant_str.
             If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-            be kept for both baselines (1,2) and (2,3) to return a valid
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
             pyuvdata object.
             An ant_str cannot be passed in addition to any of `antenna_nums`,
             `antenna_names`, `bls` args or the `polarizations` parameters,
@@ -3528,6 +3785,8 @@ class UVData(UVBase):
             Option to keep all the metadata associated with antennas, even those
             that do not have data associated with them after the select option.
         read_metadata : bool
+            Deprecated, will be removed in version 2.0, after which metadata
+            will always be read along with header data.
             Option to read in metadata (times, baselines, uvws) as well as
             basic header info. Only used if file_type is 'uvfits' and read_data
             is False (metadata will be read if data is read). If file_type is
@@ -3536,9 +3795,8 @@ class UVData(UVBase):
         read_data : bool
             Read in the data. Only used if file_type is 'uvfits',
             'miriad' or 'uvh5'. If set to False, only the metadata will be
-            read in (for uvfits, this can be further restricted to just the
-            header if read_metadata is False). Setting read_data to False
-            results in an incompletely defined object (check will not pass).
+            read in. Setting read_data to False results in a metdata only
+            object.
         phase_type : str, optional
             Option to specify the phasing status of the data. Only used if
             file_type is 'miriad'. Options are 'drift', 'phased' or None.
@@ -3563,6 +3821,14 @@ class UVData(UVBase):
             np.complex64 (single-precision real and imaginary) or np.complex128 (double-
             precision real and imaginary). Only used if the datatype of the visibility
             data on-disk is not 'c8' or 'c16'. Only used if file_type is 'uvh5'.
+        use_cotter_flags : bool
+            Flag to apply cotter flags. Only used if file_type is 'mwa_corr_fits'.
+        correct_cable_len : bool
+            Flag to apply cable length correction. Only used if file_type is
+            'mwa_corr_fits'.
+        phase_to_pointing_center : bool
+            Flag to phase to the pointing center. Only used if file_type is
+            'mwa_corr_fits'. Cannot be set if phase_center_radec is not None.
         run_check : bool
             Option to check for the existence and proper shapes of parameters
             after after reading in the file (the default is True,
@@ -3575,17 +3841,42 @@ class UVData(UVBase):
             Option to check acceptable range of the values of parameters after
             reading in the file (the default is True, meaning the acceptable
             range check will be done). Ignored if read_data is False.
+
+        Raises
+        ------
+        ValueError
+            If the file_type is not set and cannot be determined from the file name.
+            If incompatible select keywords are set (e.g. `ant_str` with other
+            antenna selectors, `times` and `time_range`) or select keywords
+            exclude all data or if keywords are set to the wrong type.
+            If the data are multi source or have multiple
+            spectral windows.
+            If phase_center_radec is not None and is not length 2.
+
         """
-        if isinstance(filename, (list, tuple)):
-            # this is either a list of separate files to read or a list of FHD files
-            if isinstance(filename[0], (list, tuple)):
-                # this must be a list of lists for FHD
-                file_type = 'fhd'
+        if isinstance(filename, (list, tuple, np.ndarray)):
+            # this is either a list of separate files to read or a list of
+            # FHD files or MWA correlator FITS files
+            if isinstance(filename[0], (list, tuple, np.ndarray)):
+                if file_type is None:
+                    # this must be a list of lists of FHD or MWA correlator FITS
+                    basename, extension = os.path.splitext(filename[0][0])
+                    if extension == '.sav' or extension == '.txt':
+                        file_type = 'fhd'
+                    elif (extension == '.fits' or extension == '.metafits'
+                          or extension == '.mwaf'):
+                        file_type = 'mwa_corr_fits'
                 multi = True
             else:
-                basename, extension = os.path.splitext(filename[0])
-                if extension == '.sav' or extension == '.txt':
-                    file_type = 'fhd'
+                if file_type is None:
+                    basename, extension = os.path.splitext(filename[0])
+                    if extension == '.sav' or extension == '.txt':
+                        file_type = 'fhd'
+                    elif (extension == '.fits' or extension == '.metafits'
+                          or extension == '.mwaf'):
+                        file_type = 'mwa_corr_fits'
+
+                if file_type == 'fhd' or file_type == 'mwa_corr_fits':
                     multi = False
                 else:
                     multi = True
@@ -3614,7 +3905,8 @@ class UVData(UVBase):
                     file_type = 'uvh5'
 
         if file_type is None:
-            raise ValueError('File type could not be determined.')
+            raise ValueError('File type could not be determined, use the '
+                             'file_type keyword to specify the type.')
 
         if time_range is not None:
             if times is not None:
@@ -3622,150 +3914,314 @@ class UVData(UVBase):
                     'Only one of times and time_range can be provided.')
 
         if antenna_names is not None and antenna_nums is not None:
-            raise ValueError('Only one of antenna_nums and antenna_names can be provided.')
-
-        if file_type == 'uvfits':
-            if time_range is not None:
-                select = True
-                warnings.warn('Warning: "time_range" keyword is set which is not '
-                              'supported by read_uvfits. This select will be '
-                              'done after reading the file.')
-            else:
-                select = False
-
-            self.read_uvfits(filename, antenna_nums=antenna_nums,
-                             antenna_names=antenna_names, ant_str=ant_str,
-                             bls=bls, frequencies=frequencies,
-                             freq_chans=freq_chans, times=times,
-                             polarizations=polarizations, blt_inds=blt_inds,
-                             read_data=read_data, read_metadata=read_metadata,
-                             run_check=run_check, check_extra=check_extra,
-                             run_check_acceptability=run_check_acceptability,
-                             keep_all_metadata=keep_all_metadata, axis=axis)
-
-            if select:
-                unique_times = np.unique(self.time_array)
-                times_to_keep = unique_times[np.where((unique_times >= np.min(time_range))
-                                                      & (unique_times <= np.max(time_range)))]
-                self.select(times=times_to_keep, run_check=run_check, check_extra=check_extra,
-                            run_check_acceptability=run_check_acceptability,
-                            keep_all_metadata=keep_all_metadata)
-
-        elif file_type == 'miriad':
-            if (antenna_names is not None or frequencies is not None or freq_chans is not None
-                    or times is not None or blt_inds is not None):
-
-                if blt_inds is not None:
-                    if (antenna_nums is not None or ant_str is not None
-                            or bls is not None or time_range is not None):
-                        warnings.warn('Warning: blt_inds is set along with select '
-                                      'on read keywords that are supported by '
-                                      'read_miriad and may downselect blts. '
-                                      'This may result in incorrect results '
-                                      'because the select on read will happen '
-                                      'before the blt_inds selection so the '
-                                      'indices may not match the expected locations.')
-                else:
-                    warnings.warn('Warning: a select on read keyword is set that is not '
-                                  'supported by read_miriad. This select will be '
-                                  'done after reading the file.')
-                select = True
-            else:
-                select = False
+            raise ValueError('Only one of antenna_nums and antenna_names can '
+                             'be provided.')
+
+        if multi:
+            if file_type == 'uvfits':
+                if not read_data and not read_metadata:
+                    raise ValueError('A list of files cannot be used when just '
+                                     'reading the header (read_data and '
+                                     'read_metadata are both False)')
+
+            self.read(filename[0], file_type=file_type,
+                      antenna_nums=antenna_nums, antenna_names=antenna_names,
+                      ant_str=ant_str, bls=bls,
+                      frequencies=frequencies, freq_chans=freq_chans,
+                      times=times, polarizations=polarizations,
+                      blt_inds=blt_inds, time_range=time_range,
+                      keep_all_metadata=keep_all_metadata,
+                      read_metadata=read_metadata, read_data=read_data,
+                      phase_type=phase_type, correct_lat_lon=correct_lat_lon,
+                      use_model=use_model,
+                      data_column=data_column, pol_order=pol_order,
+                      data_array_dtype=data_array_dtype,
+                      run_check=run_check, check_extra=check_extra,
+                      run_check_acceptability=run_check_acceptability)
+
+            if (allow_rephase and phase_center_radec is None
+                    and not unphase_to_drift and self.phase_type == 'phased'):
+                # set the phase center to be the phase center of the first file
+                phase_center_radec = [self.phase_center_ra,
+                                      self.phase_center_dec]
 
-            self.read_miriad(filename, antenna_nums=antenna_nums, ant_str=ant_str,
-                             bls=bls, polarizations=polarizations,
-                             time_range=time_range, read_data=read_data,
-                             phase_type=phase_type, correct_lat_lon=correct_lat_lon,
+            if len(filename) > 1:
+                for f in filename[1:]:
+                    uv2 = UVData()
+                    uv2.read(f, file_type=file_type,
+                             phase_center_radec=phase_center_radec,
+                             antenna_nums=antenna_nums,
+                             antenna_names=antenna_names,
+                             ant_str=ant_str, bls=bls,
+                             frequencies=frequencies, freq_chans=freq_chans,
+                             times=times, polarizations=polarizations,
+                             blt_inds=blt_inds, time_range=time_range,
+                             keep_all_metadata=keep_all_metadata,
+                             read_metadata=read_metadata, read_data=read_data,
+                             phase_type=phase_type,
+                             correct_lat_lon=correct_lat_lon,
+                             use_model=use_model,
+                             data_column=data_column, pol_order=pol_order,
+                             data_array_dtype=data_array_dtype,
                              run_check=run_check, check_extra=check_extra,
-                             run_check_acceptability=run_check_acceptability,
-                             axis=axis)
-
-            if select:
-                self.select(antenna_names=antenna_names, frequencies=frequencies,
-                            freq_chans=freq_chans, times=times,
-                            blt_inds=blt_inds, run_check=run_check, check_extra=check_extra,
-                            run_check_acceptability=run_check_acceptability,
-                            keep_all_metadata=keep_all_metadata)
-
-        elif file_type == 'fhd':
-            if (antenna_nums is not None or antenna_names is not None
-                    or ant_str is not None or bls is not None
-                    or frequencies is not None or freq_chans is not None
-                    or times is not None or polarizations is not None
-                    or blt_inds is not None):
-                select = True
-                warnings.warn('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')
-            else:
-                select = False
-
-            self.read_fhd(filename, use_model=use_model, run_check=run_check,
-                          check_extra=check_extra,
-                          run_check_acceptability=run_check_acceptability,
-                          axis=axis)
-
-            if select:
-                self.select(antenna_nums=antenna_nums, antenna_names=antenna_names,
-                            ant_str=ant_str, bls=bls, frequencies=frequencies,
-                            freq_chans=freq_chans, times=times,
-                            polarizations=polarizations, blt_inds=blt_inds,
+                             run_check_acceptability=run_check_acceptability)
+                    if axis is not None:
+                        self.fast_concat(
+                            uv2, axis, phase_center_radec=phase_center_radec,
+                            unphase_to_drift=unphase_to_drift,
+                            phase_frame=phase_frame,
+                            orig_phase_frame=orig_phase_frame,
+                            use_ant_pos=phase_use_ant_pos,
                             run_check=run_check, check_extra=check_extra,
                             run_check_acceptability=run_check_acceptability,
-                            keep_all_metadata=keep_all_metadata)
-        elif file_type == 'ms':
-            if (antenna_nums is not None or antenna_names is not None
-                    or ant_str is not None or bls is not None
-                    or frequencies is not None or freq_chans is not None
-                    or times is not None or polarizations is not None
-                    or blt_inds is not None):
-                select = True
-                warnings.warn('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')
-            else:
-                select = False
+                            inplace=True)
+                    else:
+                        self.__iadd__(
+                            uv2, phase_center_radec=phase_center_radec,
+                            unphase_to_drift=unphase_to_drift,
+                            phase_frame=phase_frame,
+                            orig_phase_frame=orig_phase_frame,
+                            use_ant_pos=phase_use_ant_pos,
+                            run_check=run_check, check_extra=check_extra,
+                            run_check_acceptability=run_check_acceptability)
 
-            self.read_ms(filename, run_check=run_check, check_extra=check_extra,
-                         run_check_acceptability=run_check_acceptability,
-                         data_column=data_column, pol_order=pol_order, axis=axis)
+                del(uv2)
+        else:
+            # reading a single "file". Call the appropriate file-type read
+            if file_type == 'uvfits':
+                if time_range is not None:
+                    select = True
+                    warnings.warn(
+                        'Warning: "time_range" keyword is set which is not '
+                        'supported by read_uvfits. This select will be '
+                        'done after reading the file.')
+                else:
+                    select = False
+
+                self.read_uvfits(
+                    filename, antenna_nums=antenna_nums,
+                    antenna_names=antenna_names, ant_str=ant_str,
+                    bls=bls, frequencies=frequencies,
+                    freq_chans=freq_chans, times=times,
+                    polarizations=polarizations, blt_inds=blt_inds,
+                    read_data=read_data, read_metadata=read_metadata,
+                    run_check=run_check, check_extra=check_extra,
+                    run_check_acceptability=run_check_acceptability,
+                    keep_all_metadata=keep_all_metadata)
+
+            elif file_type == 'miriad':
+                if (antenna_names is not None or frequencies is not None
+                        or freq_chans is not None
+                        or times is not None or blt_inds is not None):
+
+                    if blt_inds is not None:
+                        if (antenna_nums is not None or ant_str is not None
+                                or bls is not None or time_range is not None):
+                            warnings.warn(
+                                'Warning: blt_inds is set along with select '
+                                'on read keywords that are supported by '
+                                'read_miriad and may downselect blts. '
+                                'This may result in incorrect results '
+                                'because the select on read will happen '
+                                'before the blt_inds selection so the indices '
+                                'may not match the expected locations.')
+                    else:
+                        warnings.warn(
+                            'Warning: a select on read keyword is set that is '
+                            'not supported by read_miriad. This select will '
+                            'be done after reading the file.')
+                    select = True
+                else:
+                    select = False
+
+                self.read_miriad(
+                    filename, antenna_nums=antenna_nums, ant_str=ant_str,
+                    bls=bls, polarizations=polarizations,
+                    time_range=time_range, read_data=read_data,
+                    phase_type=phase_type, correct_lat_lon=correct_lat_lon,
+                    run_check=run_check, check_extra=check_extra,
+                    run_check_acceptability=run_check_acceptability)
 
-            if select:
-                self.select(antenna_nums=antenna_nums, antenna_names=antenna_names,
-                            ant_str=ant_str, bls=bls, frequencies=frequencies,
-                            freq_chans=freq_chans, times=times,
-                            polarizations=polarizations, blt_inds=blt_inds,
-                            run_check=run_check, check_extra=check_extra,
-                            run_check_acceptability=run_check_acceptability,
-                            keep_all_metadata=keep_all_metadata)
-        elif file_type == 'uvh5':
-            if (time_range is not None):
-                select = True
-                warnings.warn('Warning: "time_range" keyword is set which is not '
-                              'supported by read_uvh5. This select will be '
-                              'done after reading the file.')
-            else:
-                select = False
+            elif file_type == 'mwa_corr_fits':
+                if (antenna_nums is not None or antenna_names is not None
+                        or ant_str is not None or bls is not None
+                        or frequencies is not None or freq_chans is not None
+                        or times is not None or time_range is not None
+                        or polarizations is not None
+                        or blt_inds is not None):
+                    select = True
+                    warnings.warn(
+                        'Warning: select on read keyword set, but '
+                        'file_type is "mwa_corr_fits" which does not support select '
+                        'on read. Entire file will be read and then select '
+                        'will be performed')
+                else:
+                    select = False
+
+                self.read_mwa_corr_fits(
+                    filename, run_check=run_check,
+                    use_cotter_flags=use_cotter_flags,
+                    correct_cable_len=correct_cable_len,
+                    phase_to_pointing_center=phase_to_pointing_center,
+                    check_extra=check_extra,
+                    run_check_acceptability=run_check_acceptability)
 
-            self.read_uvh5(filename, antenna_nums=antenna_nums,
-                           antenna_names=antenna_names, ant_str=ant_str, bls=bls,
-                           frequencies=frequencies, freq_chans=freq_chans, times=times,
-                           polarizations=polarizations, blt_inds=blt_inds,
-                           read_data=read_data, run_check=run_check, check_extra=check_extra,
-                           run_check_acceptability=run_check_acceptability,
-                           data_array_dtype=data_array_dtype,
-                           keep_all_metadata=keep_all_metadata, axis=axis)
+            elif file_type == 'fhd':
+                if (antenna_nums is not None or antenna_names is not None
+                        or ant_str is not None or bls is not None
+                        or frequencies is not None or freq_chans is not None
+                        or times is not None or time_range is not None
+                        or polarizations is not None
+                        or blt_inds is not None):
+                    select = True
+                    warnings.warn(
+                        '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')
+                else:
+                    select = False
+
+                self.read_fhd(filename, use_model=use_model,
+                              run_check=run_check, check_extra=check_extra,
+                              run_check_acceptability=run_check_acceptability)
+
+            elif file_type == 'ms':
+                if (antenna_nums is not None or antenna_names is not None
+                        or ant_str is not None or bls is not None
+                        or frequencies is not None or freq_chans is not None
+                        or times is not None or time_range is not None
+                        or polarizations is not None
+                        or blt_inds is not None):
+                    select = True
+                    warnings.warn(
+                        'Warning: select on read keyword set, but '
+                        'file_type is "ms" which does not support select '
+                        'on read. Entire file will be read and then select '
+                        'will be performed')
+                else:
+                    select = False
+
+                self.read_ms(filename, run_check=run_check,
+                             check_extra=check_extra,
+                             run_check_acceptability=run_check_acceptability,
+                             data_column=data_column, pol_order=pol_order)
+
+            elif file_type == 'uvh5':
+                if (time_range is not None):
+                    select = True
+                    warnings.warn(
+                        'Warning: "time_range" keyword is set which is not '
+                        'supported by read_uvh5. This select will be '
+                        'done after reading the file.')
+                else:
+                    select = False
+
+                self.read_uvh5(
+                    filename, antenna_nums=antenna_nums,
+                    antenna_names=antenna_names, ant_str=ant_str, bls=bls,
+                    frequencies=frequencies, freq_chans=freq_chans,
+                    times=times,
+                    polarizations=polarizations, blt_inds=blt_inds,
+                    read_data=read_data, run_check=run_check,
+                    check_extra=check_extra,
+                    run_check_acceptability=run_check_acceptability,
+                    data_array_dtype=data_array_dtype,
+                    keep_all_metadata=keep_all_metadata)
 
             if select:
-                unique_times = np.unique(self.time_array)
-                times_to_keep = unique_times[np.where((unique_times >= np.min(time_range))
-                                                      & (unique_times <= np.max(time_range)))]
-                self.select(times=times_to_keep, run_check=run_check, check_extra=check_extra,
-                            run_check_acceptability=run_check_acceptability,
-                            keep_all_metadata=keep_all_metadata)
+                if file_type in ['fhd', 'ms', 'mwa_corr_fits']:
+                    # these file types do not have select on read, so set all
+                    # select parameters
+                    select_antenna_nums = antenna_nums
+                    select_antenna_names = antenna_names
+                    select_ant_str = ant_str
+                    select_bls = bls
+                    select_frequencies = frequencies
+                    select_freq_chans = freq_chans
+                    select_times = times
+                    select_polarizations = polarizations
+                    select_blt_inds = blt_inds
+
+                    if time_range is not None:
+                        unique_times = np.unique(self.time_array)
+                        select_times = unique_times[
+                            np.where((unique_times >= np.min(time_range))
+                                     & (unique_times <= np.max(time_range)))]
+
+                elif file_type in ['uvfits', 'uvh5']:
+                    # these are all done by partial read, so set to None here
+                    select_antenna_nums = None
+                    select_antenna_names = None
+                    select_ant_str = None
+                    select_bls = None
+                    select_frequencies = None
+                    select_freq_chans = None
+                    select_times = None
+                    select_polarizations = None
+                    select_blt_inds = None
+
+                    # this isn't supported by partial read, so do it here
+                    if time_range is not None:
+                        unique_times = np.unique(self.time_array)
+                        select_times = unique_times[
+                            np.where((unique_times >= np.min(time_range))
+                                     & (unique_times <= np.max(time_range)))]
+                elif file_type in ['miriad']:
+                    # these are all done by partial read, so set to None here
+                    select_antenna_nums = None
+                    select_ant_str = None
+                    select_bls = None
+                    select_polarizations = None
+
+                    # these aren't supported by partial read, so do it here
+                    select_antenna_names = antenna_names
+                    select_frequencies = frequencies
+                    select_freq_chans = freq_chans
+                    select_times = times
+                    select_blt_inds = blt_inds
+
+                self.select(
+                    antenna_nums=select_antenna_nums,
+                    antenna_names=select_antenna_names,
+                    ant_str=select_ant_str,
+                    bls=select_bls,
+                    frequencies=select_frequencies,
+                    freq_chans=select_freq_chans,
+                    times=select_times,
+                    polarizations=select_polarizations,
+                    blt_inds=select_blt_inds,
+                    run_check=run_check,
+                    check_extra=check_extra,
+                    run_check_acceptability=run_check_acceptability,
+                    keep_all_metadata=keep_all_metadata)
+
+            if unphase_to_drift:
+                if (self.phase_type != 'drift'):
+                    warnings.warn("Unphasing this UVData object to drift")
+                    self.unphase_to_drift(phase_frame=orig_phase_frame,
+                                          use_ant_pos=phase_use_ant_pos)
+
+            if phase_center_radec is not None:
+                if np.array(phase_center_radec).size != 2:
+                    raise ValueError('phase_center_radec should have length 2.')
+
+                # If this object is not phased or is not phased close to
+                # phase_center_radec, (re)phase it.
+                # Close is defined using the phase_center_ra/dec tolerances.
+                if (self.phase_type == 'drift'
+                    or (not np.isclose(self.phase_center_ra, phase_center_radec[0],
+                                       rtol=self._phase_center_ra.tols[0],
+                                       atol=self._phase_center_ra.tols[1])
+                        or not np.isclose(self.phase_center_dec, phase_center_radec[1],
+                                          rtol=self._phase_center_dec.tols[0],
+                                          atol=self._phase_center_dec.tols[1]))):
+                    warnings.warn("Phasing this UVData object to phase_center_radec")
+                    self.phase(phase_center_radec[0], phase_center_radec[1],
+                               phase_frame=phase_frame,
+                               orig_phase_frame=orig_phase_frame,
+                               use_ant_pos=phase_use_ant_pos,
+                               allow_rephase=True)
 
     def get_ants(self):
         """
@@ -3954,7 +4410,7 @@ class UVData(UVBase):
                 Return all times for that baseline, pol. pol may be a string.
 
         Returns
-        ----------
+        -------
         blt_ind1 : ndarray of int
             blt indices for antenna pair.
         blt_ind2 : ndarray of int
@@ -3966,6 +4422,7 @@ class UVData(UVBase):
             complete conjugation mapping.
         pol_ind : tuple of ndarray of int
             polarization indices for blt_ind1 and blt_ind2
+
         """
         key = uvutils._get_iterable(key)
         if type(key) is str:
@@ -4642,9 +5099,10 @@ class UVData(UVBase):
         samples.
 
         Returns
-        ----------
+        -------
         int_time : int
             integration time in seconds to be assigned to all samples in the data.
+
         """
         # The time_array is in units of days, and integration_time has units of
         # seconds, so we need to convert.
@@ -4691,10 +5149,11 @@ class UVData(UVBase):
             Only returned if include_conjugates is True
 
         Notes
-        ----
+        -----
         If use_antpos is set, then this function will find all redundant baseline groups
         for this telescope, under the u>0 antenna ordering convention. If use_antpos is not set,
         this function will look for redundant groups in the data.
+
         """
         if use_antpos:
             antpos, numbers = self.get_ENU_antpos(center=False)
diff --git a/pyuvdata/uvfits.py b/pyuvdata/uvfits.py
index 1e37aa9d..b9c0e388 100644
--- a/pyuvdata/uvfits.py
+++ b/pyuvdata/uvfits.py
@@ -133,13 +133,8 @@ class UVFITS(UVData):
         """
         Read just the visibility and flag data of the uvfits file.
 
-        Separated from full read so that header, metadata and data can be read
-        independently.
+        Separated from full read so header and metadata can be read without data.
         """
-        if self.time_array is None or read_metadata:
-            # first read in random group parameters
-            self._get_parameter_data(vis_hdu, run_check_acceptability)
-
         # figure out what data to read in
         blt_inds, freq_inds, pol_inds, history_update_string = \
             self._select_preprocess(antenna_nums, antenna_names, ant_str, bls,
@@ -250,66 +245,98 @@ class UVFITS(UVData):
 
         Supports reading only selected portions of the data.
 
-        Args:
-            filename: The uvfits file to read from.
-            antenna_nums: The antennas numbers to include when reading data into
-                the object (antenna positions and names for the excluded antennas
-                will be retained). This cannot be provided if antenna_names is
-                also provided. Ignored if read_data is False.
-            antenna_names: The antennas names to include when reading data into
-                the object (antenna positions and names for the excluded antennas
-                will be retained). This cannot be provided if antenna_nums is
-                also provided. Ignored if read_data is False.
-            bls: A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-                baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
-                to keep in the object. For length-2 tuples, the  ordering of the numbers
-                within the tuple does not matter. For length-3 tuples, the polarization
-                string is in the order of the two antennas. If length-3 tuples are provided,
-                the polarizations argument below must be None. Ignored if read_data is False.
-            ant_str: A string containing information about what antenna numbers
-                and polarizations to include when reading data into the object.
-                Can be 'auto', 'cross', 'all', or combinations of antenna numbers
-                and polarizations (e.g. '1', '1_2', '1x_2y').
-                See tutorial for more examples of valid strings and
-                the behavior of different forms for ant_str.
-                If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-                be kept for both baselines (1,2) and (2,3) to return a valid
-                pyuvdata object.
-                An ant_str cannot be passed in addition to any of the above antenna
-                args or the polarizations arg.
-                Ignored if read_data is False.
-            frequencies: The frequencies to include when reading data into the
-                object. Ignored if read_data is False.
-            freq_chans: The frequency channel numbers to include when reading
-                data into the object. Ignored if read_data is False.
-            times: The times to include when reading data into the object.
-                Ignored if read_data is False.
-            polarizations: The polarizations to include when reading data into
-                the object. Ignored if read_data is False.
-            blt_inds: The baseline-time indices to include when reading data into
-                the object. This is not commonly used. Ignored if read_data is False.
-            read_data: Read in the visibility and flag data. If set to false,
-                only the basic header info and metadata (if read_metadata is True)
-                will be read in. Results in an incompletely defined object
-                (check will not pass). Default True.
-            read_metadata: Read in metadata (times, baselines, uvws) as well as
-                basic header info. Only used if read_data is False
-                (metadata will be read if data is read). If both read_data and
-                read_metadata are false, only basic header info is read in. Default True.
-            run_check: Option to check for the existence and proper shapes of
-                parameters after reading in the file. Default is True.
-                Ignored if read_data is False.
-            check_extra: Option to check optional parameters as well as required
-                ones. Default is True. Ignored if read_data is False.
-            run_check_acceptability: Option to check acceptable range of the values of
-                parameters after reading in the file. Default is True.
-                Ignored if read_data is False.
-            keep_all_metadata: Option to keep all the metadata associated with antennas,
-                even those that do not remain after the select option. Default is True.
-        """
-        if not read_data:
-            run_check = False
+        Parameters
+        ----------
+        filename : str
+            The uvfits file to read from.
+        antenna_nums : array_like of int, optional
+            The antennas numbers to include when reading data into the object
+            (antenna positions and names for the removed antennas will be retained
+            unless `keep_all_metadata` is False). This cannot be provided if
+            `antenna_names` is also provided. Ignored if read_data is False.
+        antenna_names : array_like of str, optional
+            The antennas names to include when reading data into the object
+            (antenna positions and names for the removed antennas will be retained
+            unless `keep_all_metadata` is False). This cannot be provided if
+            `antenna_nums` is also provided. Ignored if read_data is False.
+        bls : list of tuple, optional
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
+            to include when reading data into the object. For length-2 tuples,
+            the ordering of the numbers within the tuple does not matter. For
+            length-3 tuples, the polarization string is in the order of the two
+            antennas. If length-3 tuples are provided, `polarizations` must be
+            None. Ignored if read_data is False.
+        ant_str : str, optional
+            A string containing information about what antenna numbers
+            and polarizations to include when reading data into the object.
+            Can be 'auto', 'cross', 'all', or combinations of antenna numbers
+            and polarizations (e.g. '1', '1_2', '1x_2y').  See tutorial for more
+            examples of valid strings and the behavior of different forms for ant_str.
+            If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
+            pyuvdata object.
+            An ant_str cannot be passed in addition to any of `antenna_nums`,
+            `antenna_names`, `bls` args or the `polarizations` parameters,
+            if it is a ValueError will be raised. Ignored if read_data is False.
+        frequencies : array_like of float, optional
+            The frequencies to include when reading data into the object, each
+            value passed here should exist in the freq_array. Ignored if
+            read_data is False.
+        freq_chans : array_like of int, optional
+            The frequency channel numbers to include when reading data into the
+            object. Ignored if read_data is False.
+        times : array_like of float, optional
+            The times to include when reading data into the object, each value
+            passed here should exist in the time_array. Ignored if read_data is False.
+        polarizations : array_like of int, optional
+            The polarizations numbers to include when reading data into the
+            object, each value passed here should exist in the polarization_array.
+            Ignored if read_data is False.
+        blt_inds : array_like of int, optional
+            The baseline-time indices to include when reading data into the
+            object. This is not commonly used. Ignored if read_data is False.
+        keep_all_metadata : bool
+            Option to keep all the metadata associated with antennas, even those
+            that do not have data associated with them after the select option.
+        read_data : bool
+            Read in the visibility and flag data. If set to false, only the
+            basic header info and metadata read in. Setting read_data to False
+            results in a metdata only object.
+        read_metadata : bool
+            Deprecated, will be removed in version 2.0, after which metadata
+            will always be read along with header data.
+            Read in metadata (times, baselines, uvws) as well as basic header
+            info. Only used if read_data is False (metadata will be read if data
+            is read). If both read_data and read_metadata are false, only basic
+            header info is read in, which will result in an incompletely
+            defined object -- check will not pass.
+        run_check : bool
+            Option to check for the existence and proper shapes of parameters
+            after after reading in the file (the default is True,
+            meaning the check will be run). Ignored if read_data is False.
+        check_extra : bool
+            Option to check optional parameters as well as required ones (the
+            default is True, meaning the optional parameters will be checked).
+            Ignored if read_data is False.
+        run_check_acceptability : bool
+            Option to check acceptable range of the values of parameters after
+            reading in the file (the default is True, meaning the acceptable
+            range check will be done). Ignored if read_data is False.
+
+        Raises
+        ------
+        IOError
+            If filename doesn't exist.
+        ValueError
+            If incompatible select keywords are set (e.g. `ant_str` with other
+            antenna selectors, `times` and `time_range`) or select keywords
+            exclude all data or if keywords are set to the wrong type.
+            If the data are multi source or have multiple
+            spectral windows.
+            If the metadata are not internally consistent or missing.
 
+        """
         with fits.open(filename, memmap=True) as hdu_list:
             vis_hdu = hdu_list[0]  # assumes the visibilities are in the primary hdu
             vis_hdr = vis_hdu.header.copy()
@@ -484,14 +511,19 @@ class UVFITS(UVData):
             if not read_data and not read_metadata:
                 # don't read in the data or metadata. This means the object is incomplete,
                 # but that may not matter for many purposes.
+                warnings.warn('Support for reading only the header '
+                              '(by setting the read_metadata keyword to False) is '
+                              'deprecated and will be removed in version 2.0.',
+                              DeprecationWarning)
+                run_check = False
                 return
 
             # Now read in the random parameter info
             self._get_parameter_data(vis_hdu, run_check_acceptability)
 
             if not read_data:
-                # don't read in the data. This means the object is incomplete,
-                # but that may not matter for many purposes.
+                # don't read in the data. This means the object is a metadata
+                # only object but that may not matter for many purposes.
                 return
 
             # Now read in the data
@@ -502,7 +534,10 @@ class UVFITS(UVData):
 
     def read_uvfits_metadata(self, filename, run_check_acceptability=True):
         """
-        Read in metadata (random parameter info) but not data from a uvfits file.
+        Deprecated: Read in metadata (random parameter info) but not data from a uvfits file.
+
+        Deprecated in favor of `read_uvfits` with `read_data=False` because
+        reading header data without the metadata is deprecated.
 
         This is useful when an object already has the associated header info and
         full visibility data isn't needed.
@@ -514,8 +549,11 @@ class UVFITS(UVData):
         run_check_acceptability : bool
             Option to check acceptable range of the values of parameters after
             reading in the file. Default is True.
-
         """
+        warnings.warn('The read_uvfits_metadata method is deprecated in favor of '
+                      'read_uvfits` with `read_data=False`. This '
+                      'function will be removed in version 2.0', DeprecationWarning)
+
         if self.data_array is not None:
             raise ValueError('data_array is already defined, cannot read metadata')
 
@@ -533,7 +571,10 @@ class UVFITS(UVData):
                          check_extra=True, run_check_acceptability=True,
                          keep_all_metadata=True):
         """
-        Read in data but not header info from a uvfits file.
+        Deprecated: Read in data but not header info from a uvfits file.
+
+        Deprecated in favor of `read_uvfits` because reading data into
+        an existing metadata only object is deprecated.
 
         Useful for an object that already has the associated header info.
 
@@ -547,8 +588,8 @@ class UVFITS(UVData):
                 the object (antenna positions and names for the excluded antennas
                 will be retained). This cannot be provided if antenna_nums is
                 also provided.
-            bls: A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-                baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            bls: A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+                baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
                 to keep in the object. For length-2 tuples, the  ordering of the numbers
                 within the tuple does not matter. For length-3 tuples, the polarization
                 string is in the order of the two antennas. If length-3 tuples are provided,
@@ -560,7 +601,7 @@ class UVFITS(UVData):
                 See tutorial for more examples of valid strings and
                 the behavior of different forms for ant_str.
                 If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-                be kept for both baselines (1,2) and (2,3) to return a valid
+                be kept for both baselines (1, 2) and (2, 3) to return a valid
                 pyuvdata object.
                 An ant_str cannot be passed in addition to any of the above antenna
                 args or the polarizations arg.
@@ -583,8 +624,11 @@ class UVFITS(UVData):
                 parameters after reading in the file. Default is True.
             keep_all_metadata: Option to keep all the metadata associated with antennas,
                 even those that do not remain after the select option. Default is True.
-
         """
+        warnings.warn('The read_uvfits_data method is deprecated in favor of '
+                      'read_uvfits`. This '
+                      'function will be removed in version 2.0', DeprecationWarning)
+
         with fits.open(filename, memmap=True) as hdu_list:
             vis_hdu = hdu_list[0]  # assumes the visibilities are in the primary hdu
 
@@ -622,6 +666,20 @@ class UVFITS(UVData):
             Option to check acceptable range of the values of parameters before
             writing the file.
 
+        Raises
+        ------
+        ValueError
+            The `phase_type` of the object is "drift" and the `force_phase` keyword is not set.
+            The `phase_type` of the object is "unknown".
+            If the frequencies are not evenly spaced or are separated by more
+            than their channel width.
+            The polarization values are not evenly spaced.
+            Any of ['antenna_positions', 'gst0', 'rdate', 'earth_omega', 'dut1',
+            'timesys'] are not set on the object and `spoof_nonessential` is False.
+            If the `timesys` parameter is not set to "UTC".
+        TypeError
+            If any entry in extra_keywords is not a single string or number.
+
         """
         if run_check:
             self.check(check_extra=check_extra,
diff --git a/pyuvdata/uvh5.py b/pyuvdata/uvh5.py
index cca3c234..701a2e64 100644
--- a/pyuvdata/uvh5.py
+++ b/pyuvdata/uvh5.py
@@ -433,69 +433,100 @@ class UVH5(UVData):
         """
         Read in data from a UVH5 file.
 
-        Args:
-            filename: The file name to read.
-            antenna_nums: The antennas numbers to include when reading data into
-                the object (antenna positions and names for the excluded antennas
-                will be retained). This cannot be provided if antenna_names is
-                also provided. Ignored if read_data is False.
-            antenna_names: The antennas names to include when reading data into
-                the object (antenna positions and names for the excluded antennas
-                will be retained). This cannot be provided if antenna_nums is
-                also provided. Ignored if read_data is False.
-            bls: A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-                baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
-                to keep in the object. For length-2 tuples, the  ordering of the numbers
-                within the tuple does not matter. For length-3 tuples, the polarization
-                string is in the order of the two antennas. If length-3 tuples are provided,
-                the polarizations argument below must be None. Ignored if read_data is False.
-            ant_str: A string containing information about what antenna numbers
-                and polarizations to include when reading data into the object.
-                Can be 'auto', 'cross', 'all', or combinations of antenna numbers
-                and polarizations (e.g. '1', '1_2', '1x_2y').
-                See tutorial for more examples of valid strings and
-                the behavior of different forms for ant_str.
-                If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-                be kept for both baselines (1,2) and (2,3) to return a valid
-                pyuvdata object.
-                An ant_str cannot be passed in addition to any of the above antenna
-                args or the polarizations arg.
-                Ignored if read_data is False.
-            frequencies: The frequencies to include when reading data into the
-                object. Ignored if read_data is False.
-            freq_chans: The frequency channel numbers to include when reading
-                data into the object. Ignored if read_data is False.
-            times: The times to include when reading data into the object.
-                Ignored if read_data is False.
-            polarizations: The polarizations to include when reading data into
-                the object. Ignored if read_data is False.
-            blt_inds: The baseline-time indices to include when reading data into
-                the object. This is not commonly used. Ignored if read_data is False.
-            read_data: Read in the visibility and flag data. If set to false,
-                only the header info and metadata will be read in. Results in an
-                incompletely defined object (check will not pass). Default True.
-            run_check: Option to check for the existence and proper shapes of
-                parameters after reading in the file. Default is True.
-            check_extra: Option to check optional parameters as well as required
-                ones. Default is True.
-            run_check_acceptability: Option to check acceptable range of the values of
-                parameters after reading in the file. Default is True.
-            data_array_dtype: Datatype to store the output data_array as. Must be either
-                np.complex64 (single-precision real and imaginary) or np.complex128 (double-
-                precision real and imaginary). Only used if the datatype of the visibility
-                data on-disk is not 'c8' or 'c16'. Default is np.complex128.
-            keep_all_metadata: Option to keep all the metadata associated with antennas,
-                even those that do not remain after the select option. Default is True.
-
-        Returns:
+        Parameters
+        ----------
+        filename : str
+             The UVH5 file to read from.
+        antenna_nums : array_like of int, optional
+            The antennas numbers to include when reading data into the object
+            (antenna positions and names for the removed antennas will be retained
+            unless `keep_all_metadata` is False). This cannot be provided if
+            `antenna_names` is also provided. Ignored if read_data is False.
+        antenna_names : array_like of str, optional
+            The antennas names to include when reading data into the object
+            (antenna positions and names for the removed antennas will be retained
+            unless `keep_all_metadata` is False). This cannot be provided if
+            `antenna_nums` is also provided. Ignored if read_data is False.
+        bls : list of tuple, optional
+            A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+            baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
+            to include when reading data into the object. For length-2 tuples,
+            the ordering of the numbers within the tuple does not matter. For
+            length-3 tuples, the polarization string is in the order of the two
+            antennas. If length-3 tuples are provided, `polarizations` must be
+            None. Ignored if read_data is False.
+        ant_str : str, optional
+            A string containing information about what antenna numbers
+            and polarizations to include when reading data into the object.
+            Can be 'auto', 'cross', 'all', or combinations of antenna numbers
+            and polarizations (e.g. '1', '1_2', '1x_2y').  See tutorial for more
+            examples of valid strings and the behavior of different forms for ant_str.
+            If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
+            be kept for both baselines (1, 2) and (2, 3) to return a valid
+            pyuvdata object.
+            An ant_str cannot be passed in addition to any of `antenna_nums`,
+            `antenna_names`, `bls` args or the `polarizations` parameters,
+            if it is a ValueError will be raised. Ignored if read_data is False.
+        frequencies : array_like of float, optional
+            The frequencies to include when reading data into the object, each
+            value passed here should exist in the freq_array. Ignored if
+            read_data is False.
+        freq_chans : array_like of int, optional
+            The frequency channel numbers to include when reading data into the
+            object. Ignored if read_data is False.
+        times : array_like of float, optional
+            The times to include when reading data into the object, each value
+            passed here should exist in the time_array. Ignored if read_data is False.
+        polarizations : array_like of int, optional
+            The polarizations numbers to include when reading data into the
+            object, each value passed here should exist in the polarization_array.
+            Ignored if read_data is False.
+        blt_inds : array_like of int, optional
+            The baseline-time indices to include when reading data into the
+            object. This is not commonly used. Ignored if read_data is False.
+        keep_all_metadata : bool
+            Option to keep all the metadata associated with antennas, even those
+            that do not have data associated with them after the select option.
+        read_data : bool
+            Read in the visibility and flag data. If set to false, only the
+            basic header info and metadata will be read in. Setting read_data to
+            False results in an incompletely defined object (check will not pass).
+        data_array_dtype : numpy dtype
+            Datatype to store the output data_array as. Must be either
+            np.complex64 (single-precision real and imaginary) or np.complex128 (double-
+            precision real and imaginary). Only used if the datatype of the visibility
+            data on-disk is not 'c8' or 'c16'.
+        run_check : bool
+            Option to check for the existence and proper shapes of parameters
+            after after reading in the file (the default is True,
+            meaning the check will be run). Ignored if read_data is False.
+        check_extra : bool
+            Option to check optional parameters as well as required ones (the
+            default is True, meaning the optional parameters will be checked).
+            Ignored if read_data is False.
+        run_check_acceptability : bool
+            Option to check acceptable range of the values of parameters after
+            reading in the file (the default is True, meaning the acceptable
+            range check will be done). Ignored if read_data is False.
+
+        Returns
+        -------
             None
+
+        Raises
+        ------
+        IOError
+            If filename doesn't exist.
+        ValueError
+            If the data_array_dtype is not a complex dtype.
+            If incompatible select keywords are set (e.g. `ant_str` with other
+            antenna selectors, `times` and `time_range`) or select keywords
+            exclude all data or if keywords are set to the wrong type.
+
         """
         if not os.path.exists(filename):
             raise IOError(filename + ' not found')
 
-        if not read_data:
-            run_check = False
-
         # open hdf5 file for reading
         with h5py.File(filename, 'r') as f:
             # extract header information
@@ -854,8 +885,8 @@ class UVH5(UVData):
                 the object (antenna positions and names for the excluded antennas
                 will be retained). This cannot be provided if antenna_nums is
                 also provided.
-            bls: A list of antenna number tuples (e.g. [(0,1), (3,2)]) or a list of
-                baseline 3-tuples (e.g. [(0,1,'xx'), (2,3,'yy')]) specifying baselines
+            bls: A list of antenna number tuples (e.g. [(0, 1), (3, 2)]) or a list of
+                baseline 3-tuples (e.g. [(0, 1, 'xx'), (2, 3, 'yy')]) specifying baselines
                 to write to the file. For length-2 tuples, the ordering of the numbers
                 within the tuple does not matter. For length-3 tuples, the polarization
                 string is in the order of the two antennas. If length-3 tuples are provided,
@@ -867,7 +898,7 @@ class UVH5(UVData):
                 See tutorial for more examples of valid strings and
                 the behavior of different forms for ant_str.
                 If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will
-                be written for both baselines (1,2) and (2,3) to reflect a valid
+                be written for both baselines (1, 2) and (2, 3) to reflect a valid
                 pyuvdata object.
                 An ant_str cannot be passed in addition to any of the above antenna
                 args or the polarizations arg.


Test patch:
diff --git a/pyuvdata/tests/conftest.py b/pyuvdata/tests/conftest.py
index 0490b86f..684be9f5 100644
--- a/pyuvdata/tests/conftest.py
+++ b/pyuvdata/tests/conftest.py
@@ -6,6 +6,8 @@
 from __future__ import absolute_import, division, print_function
 
 import os
+import shutil
+
 import pytest
 from astropy.utils import iers
 from astropy.time import Time
@@ -37,3 +39,5 @@ def setup_and_teardown_package():
     yield
 
     iers.conf.auto_max_age = 30
+
+    shutil.rmtree(testdir)
diff --git a/pyuvdata/tests/test_fhd.py b/pyuvdata/tests/test_fhd.py
index ee4eb2c8..68d84b25 100644
--- a/pyuvdata/tests/test_fhd.py
+++ b/pyuvdata/tests/test_fhd.py
@@ -39,7 +39,7 @@ def test_ReadFHDWriteReadUVFits():
     """
     fhd_uv = UVData()
     uvfits_uv = UVData()
-    uvtest.checkWarnings(fhd_uv.read, [testfiles], known_warning='fhd')
+    uvtest.checkWarnings(fhd_uv.read_fhd, [testfiles], known_warning='fhd')
 
     fhd_uv.write_uvfits(os.path.join(DATA_PATH, 'test/outtest_FHD_1061316296.uvfits'),
                         spoof_nonessential=True)
@@ -77,7 +77,7 @@ def test_ReadFHDWriteReadUVFits_no_layout():
     fhd_uv = UVData()
     uvfits_uv = UVData()
     files_use = testfiles[:-3] + [testfiles[-2]]
-    uvtest.checkWarnings(fhd_uv.read_fhd, [files_use], nwarnings=2,
+    uvtest.checkWarnings(fhd_uv.read, [files_use], nwarnings=2,
                          message=['No layout file', 'antenna_positions are not defined'],
                          category=DeprecationWarning)
 
@@ -146,7 +146,7 @@ def test_ReadFHDWriteReadUVFits_fix_layout_bad_obs_loc():
                  layout_fixed_file, testfiles[7]]
     messages = ['Telescope location derived from obs',
                 'tile_names from obs structure does not match']
-    uvtest.checkWarnings(fhd_uv.read_fhd, [files_use], message=messages,
+    uvtest.checkWarnings(fhd_uv.read, [files_use], message=messages,
                          nwarnings=2)
 
     fhd_uv.write_uvfits(os.path.join(DATA_PATH, 'test/outtest_FHD_1061316296.uvfits'),
@@ -169,7 +169,7 @@ def test_ReadFHDWriteReadUVFits_bad_obs_loc():
                  testfiles[6], testfiles[7]]
     messages = ['Telescope location derived from obs',
                 'tile_names from obs structure does not match']
-    uvtest.checkWarnings(fhd_uv.read_fhd, [files_use], message=messages,
+    uvtest.checkWarnings(fhd_uv.read, [files_use], message=messages,
                          nwarnings=2)
 
     fhd_uv.write_uvfits(os.path.join(DATA_PATH, 'test/outtest_FHD_1061316296.uvfits'),
@@ -209,11 +209,11 @@ def test_ReadFHDWriteReadUVFits_no_settings():
     fhd_uv = UVData()
     uvfits_uv = UVData()
     messages = ['No settings', 'Telescope location derived from obs']
-    uvtest.checkWarnings(fhd_uv.read_fhd, [testfiles[:-2]], message=messages,
+    uvtest.checkWarnings(fhd_uv.read, [testfiles[:-2]], message=messages,
                          nwarnings=2)
 
     # Check only pyuvdata history with no settings file
-    assert fhd_uv.history == fhd_uv.pyuvdata_version_str  # Check empty history with no settings
+    assert fhd_uv.history == fhd_uv.pyuvdata_version_str
 
     fhd_uv.write_uvfits(os.path.join(DATA_PATH, 'test/outtest_FHD_1061316296.uvfits'),
                         spoof_nonessential=True)
@@ -224,17 +224,26 @@ def test_ReadFHDWriteReadUVFits_no_settings():
 def test_breakReadFHD():
     """Try various cases of incomplete file lists."""
     fhd_uv = UVData()
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles[1:])  # Missing flags
-    del(fhd_uv)
-    fhd_uv = UVData()
+    # missing flags
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles[1:])
+    assert str(cm.value).startswith('No flags file included in file list')
+
+    # Missing params
     subfiles = [item for sublist in [testfiles[0:2], testfiles[3:]] for item in sublist]
-    pytest.raises(Exception, fhd_uv.read_fhd, subfiles)  # Missing params
-    del(fhd_uv)
-    fhd_uv = UVData()
-    pytest.raises(Exception, fhd_uv.read_fhd, ['foo'])  # No data files
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(subfiles)
+    assert str(cm.value).startswith('No params file included in file list')
+
+    # No data files
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(['foo.sav'])
+    assert str(cm.value).startswith('No data files included in file list')
     del(fhd_uv)
 
-    # test warnings with various broken inputs
+
+def test_ReadFHD_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],
@@ -244,31 +253,39 @@ def test_breakReadFHD():
                      'Telescope location derived from obs',
                      'Telescope foo is not in known_telescopes.']
     fhd_uv = UVData()
-    uvtest.checkWarnings(fhd_uv.read_fhd, [bad_filelist], {'run_check': False},
+    uvtest.checkWarnings(fhd_uv.read, [bad_filelist], {'run_check': False},
                          nwarnings=5, message=warn_messages)
 
     # bad flag file
     broken_flag_file = testdir + testfile_prefix + 'broken_flags.sav'
     bad_filelist = testfiles[1:] + [broken_flag_file]
     fhd_uv = UVData()
-    pytest.raises(ValueError, fhd_uv.read_fhd, bad_filelist)
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(bad_filelist)
+    assert str(cm.value).startswith('No recognized key for visibility weights in flags_file.')
 
     # try cases with extra files of each type
     extra_xx_file = testdir + testfile_prefix + 'extra_vis_XX.sav'
     copyfile(testfiles[1], extra_xx_file)
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles + [extra_xx_file])
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles + [extra_xx_file])
+    assert str(cm.value).startswith('multiple xx datafiles in filelist')
     os.remove(extra_xx_file)
 
     extra_yy_file = testdir + testfile_prefix + 'extra_vis_YY.sav'
     copyfile(testfiles[3], extra_yy_file)
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles + [extra_yy_file])
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles + [extra_yy_file])
+    assert str(cm.value).startswith('multiple yy datafiles in filelist')
     os.remove(extra_yy_file)
 
     xy_file = testdir + testfile_prefix + 'vis_XY.sav'
     extra_xy_file = testdir + testfile_prefix + 'extra_vis_XY.sav'
     copyfile(testfiles[1], xy_file)
     copyfile(testfiles[1], extra_xy_file)
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles + [xy_file, extra_xy_file])
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles + [xy_file, extra_xy_file])
+    assert str(cm.value).startswith('multiple xy datafiles in filelist')
     os.remove(xy_file)
     os.remove(extra_xy_file)
 
@@ -276,28 +293,38 @@ def test_breakReadFHD():
     extra_yx_file = testdir + testfile_prefix + 'extra_vis_YX.sav'
     copyfile(testfiles[1], yx_file)
     copyfile(testfiles[1], extra_yx_file)
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles + [yx_file, extra_yx_file])
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles + [yx_file, extra_yx_file])
+    assert str(cm.value).startswith('multiple yx datafiles in filelist')
     os.remove(yx_file)
     os.remove(extra_yx_file)
 
     extra_params_file = testdir + testfile_prefix + 'extra_params.sav'
     copyfile(testfiles[2], extra_params_file)
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles + [extra_params_file])
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles + [extra_params_file])
+    assert str(cm.value).startswith('multiple params files in filelist')
     os.remove(extra_params_file)
 
     extra_flags_file = testdir + testfile_prefix + 'extra_flags.sav'
     copyfile(testfiles[0], extra_flags_file)
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles + [extra_flags_file])
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles + [extra_flags_file])
+    assert str(cm.value).startswith('multiple flags files in filelist')
     os.remove(extra_flags_file)
 
     extra_layout_file = testdir + testfile_prefix + 'extra_layout.sav'
     copyfile(testfiles[6], extra_layout_file)
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles + [extra_layout_file])
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles + [extra_layout_file])
+    assert str(cm.value).startswith('multiple layout files in filelist')
     os.remove(extra_layout_file)
 
     extra_settings_file = testdir + testfile_prefix + 'extra_settings.txt'
     copyfile(testfiles[7], extra_settings_file)
-    pytest.raises(Exception, fhd_uv.read_fhd, testfiles + [extra_settings_file])
+    with pytest.raises(ValueError) as cm:
+        fhd_uv.read(testfiles + [extra_settings_file])
+    assert str(cm.value).startswith('multiple settings files in filelist')
     os.remove(extra_settings_file)
 
 
@@ -314,16 +341,17 @@ def test_ReadFHD_model():
 
 
 def test_multi_files():
-    """
-    Reading multiple files at once.
-    """
+    """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]])
-    uvtest.checkWarnings(fhd_uv1.read, [[test1, test2]], {'use_model': True},
-                         message=['Telescope location derived from obs'],
-                         nwarnings=2)
+    uvtest.checkWarnings(
+        fhd_uv1.read_fhd, func_args=[np.array([test1, test2])],
+        func_kwargs={'use_model': True},
+        message=(['Please use the generic']
+                 + 2 * ['Telescope location derived from obs']),
+        category=[DeprecationWarning] + 2 * [UserWarning], nwarnings=3)
 
     uvtest.checkWarnings(fhd_uv2.read, [testfiles], {'use_model': True}, known_warning='fhd')
 
@@ -336,9 +364,7 @@ def test_multi_files():
 
 
 def test_multi_files_axis():
-    """
-    Reading multiple files at once with axis keyword.
-    """
+    """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]])
diff --git a/pyuvdata/tests/test_miriad.py b/pyuvdata/tests/test_miriad.py
index a2faaefd..8885f751 100644
--- a/pyuvdata/tests/test_miriad.py
+++ b/pyuvdata/tests/test_miriad.py
@@ -1366,29 +1366,57 @@ def test_readWriteReadMiriad_partial_metadata_only():
     uv_in.read(testfile)
     uv_in.select(freq_chans=np.arange(10))
     uv_in2 = UVData()
-    uv_in2.read([write_file, write_file2])
+    uv_in2.read(np.array([write_file, write_file2]))
 
     assert uv_in.history != uv_in2.history
     uv_in2.history = uv_in.history
     assert uv_in == uv_in2
 
-    # test exceptions
-    # read-in when data already exists
-    del uv_in, new_uv
+    # cleanup
+    del uv_in, new_uv, uv_in2
     gc.collect()
     shutil.rmtree(write_file)
     shutil.rmtree(write_file2)
 
+
+@pytest.mark.parametrize(
+    "input",
+    [os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA"),
+     aipy_extracts.UV(os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA"))],
+)
+def test_read_miriad_metadata(input):
+    # check that it can be initiated with a string filename or an aipy_extracts.UV object
+    testfile = os.path.join(DATA_PATH, "zen.2456865.60537.xy.uvcRREAA")
+    uv_in = UVData()
+    miriad_uv = uv_in._convert_to_filetype('miriad')
+    uvtest.checkWarnings(
+        miriad_uv.read_miriad_metadata, func_args=[input],
+        message=['The read_miriad_metadata method is deprecated',
+                 'Altitude is not present in Miriad file'],
+        nwarnings=2,
+        category=[DeprecationWarning, UserWarning])
+    uv_in._convert_from_filetype(miriad_uv)
+
+    uv_in2 = UVData()
+    uv_in2.read(testfile, read_data=False)
+    assert uv_in == uv_in2
+
     uv_in = UVData()
     uv_in.read(testfile)
+    miriad_uv = uv_in._convert_to_filetype('miriad')
     with pytest.raises(ValueError) as cm:
-        uv_in.read(testfile, read_data=False)
+        uvtest.checkWarnings(
+            miriad_uv.read_miriad_metadata, func_args=[input],
+            message=['The read_miriad_metadata method is deprecated',
+                     'Altitude is not present in Miriad file'],
+            nwarnings=2,
+            category=[DeprecationWarning, UserWarning])
     assert str(cm.value).startswith(
         "data_array is already defined, cannot read metadata"
     )
 
     # cleanup
-    del uv_in, uv_in2
+    del uv_in, uv_in2, miriad_uv
     gc.collect()
 
 
@@ -1403,7 +1431,7 @@ def test_readMSWriteMiriad_CASAHistory():
     ms_file = os.path.join(DATA_PATH, "day2_TDEM0003_10s_norx_1src_1spw.ms")
     testfile = os.path.join(DATA_PATH, "test/outtest_miriad")
     uvtest.checkWarnings(
-        ms_uv.read_ms, [ms_file], message="Telescope EVLA is not", nwarnings=0
+        ms_uv.read, [ms_file], message="Telescope EVLA is not", nwarnings=0
     )
     ms_uv.write_miriad(testfile, clobber=True)
     uvtest.checkWarnings(miriad_uv.read, [testfile], message="Telescope EVLA is not")
@@ -1507,6 +1535,7 @@ def test_multi_files():
     testfile1 = os.path.join(DATA_PATH, "test/uv1")
     testfile2 = os.path.join(DATA_PATH, "test/uv2")
     uv_full.read_uvfits(uvfits_file)
+    # rename telescope to avoid name warning
     uvtest.checkWarnings(
         uv_full.unphase_to_drift,
         category=DeprecationWarning,
@@ -1522,7 +1551,11 @@ def test_multi_files():
     uv2.write_miriad(testfile2, clobber=True)
     del uv1
     uv1 = UVData()
-    uv1.read([testfile1, testfile2])
+    uvtest.checkWarnings(uv1.read_miriad, func_args=[[testfile1, testfile2]],
+                         message=(['Please use the generic']
+                                  + 2 * ['Telescope EVLA is not']),
+                         category=[DeprecationWarning] + 2 * [UserWarning],
+                         nwarnings=3)
     # Check history is correct, before replacing and doing a full object check
     assert uvutils._check_histories(
         uv_full.history + "  Downselected to "
diff --git a/pyuvdata/tests/test_ms.py b/pyuvdata/tests/test_ms.py
index 5abaecb9..34cb8ee4 100644
--- a/pyuvdata/tests/test_ms.py
+++ b/pyuvdata/tests/test_ms.py
@@ -126,7 +126,7 @@ def test_readMSreadUVFITS():
     ms_file = os.path.join(DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.ms')
     uvfits_file = os.path.join(
         DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
-    uvtest.checkWarnings(uvfits_uv.read_uvfits, [uvfits_file],
+    uvtest.checkWarnings(uvfits_uv.read, [uvfits_file],
                          message='Telescope EVLA is not')
     ms_uv.read(ms_file)
     # set histories to identical blank strings since we do not expect
@@ -169,10 +169,10 @@ def test_readMSWriteUVFITS():
     ms_uv = UVData()
     uvfits_uv = UVData()
     ms_file = os.path.join(DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.ms')
-    testfile = os.path.join(DATA_PATH, 'test/outtest_uvfits')
+    testfile = os.path.join(DATA_PATH, 'test/outtest.uvfits')
     ms_uv.read(ms_file)
     ms_uv.write_uvfits(testfile, spoof_nonessential=True)
-    uvtest.checkWarnings(uvfits_uv.read_uvfits, [testfile],
+    uvtest.checkWarnings(uvfits_uv.read, [testfile],
                          message='Telescope EVLA is not')
 
     assert uvfits_uv == ms_uv
@@ -193,7 +193,7 @@ def test_readMSWriteMiriad():
     testfile = os.path.join(DATA_PATH, 'test/outtest_miriad')
     ms_uv.read(ms_file)
     ms_uv.write_miriad(testfile, clobber=True)
-    uvtest.checkWarnings(miriad_uv.read_miriad, [testfile],
+    uvtest.checkWarnings(miriad_uv.read, [testfile],
                          message='Telescope EVLA is not')
 
     assert miriad_uv == ms_uv
@@ -208,10 +208,12 @@ def test_multi_files():
     uv_multi = UVData()
     uvfits_file = os.path.join(
         DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
-    uvtest.checkWarnings(uv_full.read_uvfits, [uvfits_file], message='Telescope EVLA is not')
+    uvtest.checkWarnings(uv_full.read, [uvfits_file], message='Telescope EVLA is not')
     testfile1 = os.path.join(DATA_PATH, 'multi_1.ms')
     testfile2 = os.path.join(DATA_PATH, 'multi_2.ms')
-    uv_multi.read([testfile1, testfile2])
+    uvtest.checkWarnings(
+        uv_multi.read_ms, func_args=[np.array([testfile1, testfile2])],
+        message=['Please use the generic'], category=DeprecationWarning)
     # Casa scrambles the history parameter. Replace for now.
     uv_multi.history = uv_full.history
 
@@ -249,7 +251,7 @@ def test_multi_files_axis():
     uv_multi = UVData()
     uvfits_file = os.path.join(
         DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
-    uvtest.checkWarnings(uv_full.read_uvfits, [uvfits_file], message='Telescope EVLA is not')
+    uvtest.checkWarnings(uv_full.read, [uvfits_file], message='Telescope EVLA is not')
     testfile1 = os.path.join(DATA_PATH, 'multi_1.ms')
     testfile2 = os.path.join(DATA_PATH, 'multi_2.ms')
     uv_multi.read([testfile1, testfile2], axis='freq')
diff --git a/pyuvdata/tests/test_mwa_corr_fits.py b/pyuvdata/tests/test_mwa_corr_fits.py
index 027d53ae..ef7e7027 100644
--- a/pyuvdata/tests/test_mwa_corr_fits.py
+++ b/pyuvdata/tests/test_mwa_corr_fits.py
@@ -32,17 +32,52 @@ def test_ReadMWAWriteUVFits():
     """
     mwa_uv = UVData()
     uvfits_uv = UVData()
-    messages = ['telescope_location is not set',
+    messages = ['The `phase_data` keyword is deprecated.',
+                'telescope_location is not set',
                 'some coarse channel files were not submitted']
+    category = [DeprecationWarning] + [UserWarning] * 2
     uvtest.checkWarnings(mwa_uv.read_mwa_corr_fits, func_args=[filelist[0:2]],
                          func_kwargs={'correct_cable_len': True, 'phase_data': True},
-                         nwarnings=2, message=messages)
+                         nwarnings=3, message=messages, category=category)
+    testfile = os.path.join(DATA_PATH, 'test/outtest_MWAcorr.uvfits')
+    mwa_uv.write_uvfits(testfile, spoof_nonessential=True)
+    uvfits_uv.read_uvfits(testfile)
+    assert mwa_uv == uvfits_uv
+
+    phase_center = (mwa_uv.phase_center_ra + 0.01, mwa_uv.phase_center_dec + 0.01)
+
+    messages = ['The `phase_center` keyword is deprecated.',
+                'The `phase_data` keyword is deprecated.',
+                'telescope_location is not set',
+                'some coarse channel files were not submitted',
+                'Phasing this UVData object to phase_center_radec']
+    category = [DeprecationWarning] * 2 + [UserWarning] * 3
+    uvtest.checkWarnings(mwa_uv.read_mwa_corr_fits, func_args=[filelist[0:2]],
+                         func_kwargs={'correct_cable_len': True,
+                                      'phase_data': True,
+                                      'phase_center': phase_center},
+                         nwarnings=5, message=messages, category=category)
     testfile = os.path.join(DATA_PATH, 'test/outtest_MWAcorr.uvfits')
     mwa_uv.write_uvfits(testfile, spoof_nonessential=True)
     uvfits_uv.read_uvfits(testfile)
     assert mwa_uv == uvfits_uv
 
 
+def test_select_on_read():
+    mwa_uv = UVData()
+    mwa_uv2 = UVData()
+    mwa_uv.read_mwa_corr_fits(filelist[0:2], correct_cable_len=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)
+    mwa_uv2.read(filelist[0:2], correct_cable_len=True,
+                 time_range=[np.min(mwa_uv.time_array), np.mean(mwa_uv.time_array)])
+    assert mwa_uv == mwa_uv2
+
+
+@pytest.mark.filterwarnings("ignore:telescope_location is not set. ")
+@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
 def test_ReadMWA_ReadCotter():
     """
     Pyuvdata and cotter equality test.
@@ -53,7 +88,7 @@ def test_ReadMWA_ReadCotter():
     mwa_uv = UVData()
     cotter_uv = UVData()
     # cotter data has cable correction and is unphased
-    mwa_uv.read_mwa_corr_fits(filelist[0:2], correct_cable_len=True)
+    mwa_uv.read(filelist[0:2], correct_cable_len=True)
     cotter_uv.read(filelist[6])
     # cotter doesn't record the auto xy polarizations
     # due to a possible bug in cotter, the auto yx polarizations are conjugated
@@ -79,8 +114,9 @@ def test_ReadMWAWriteUVFits_meta_mod():
     messages = ['telescope_location is not set',
                 'some coarse channel files were not submitted']
     files = [filelist[1], filelist[5]]
-    uvtest.checkWarnings(mwa_uv.read_mwa_corr_fits, func_args=[files],
-                         func_kwargs={'correct_cable_len': True, 'phase_data': True},
+    uvtest.checkWarnings(mwa_uv.read, func_args=[files],
+                         func_kwargs={'correct_cable_len': True,
+                                      'phase_to_pointing_center': True},
                          nwarnings=2, message=messages)
     testfile = os.path.join(DATA_PATH, 'test/outtest_MWAcorr.uvfits')
     mwa_uv.write_uvfits(testfile, spoof_nonessential=True)
@@ -88,20 +124,32 @@ def test_ReadMWAWriteUVFits_meta_mod():
     assert mwa_uv == uvfits_uv
 
 
+@pytest.mark.filterwarnings("ignore:telescope_location is not set. ")
+@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
+@pytest.mark.filterwarnings("ignore:Combined frequencies are not contiguous")
 def test_ReadMWA_multi():
     """Test reading in two sets of files."""
     set1 = filelist[0:2]
     set2 = [filelist[0], filelist[2]]
     mwa_uv = UVData()
-    messages = ['telescope_location is not set',
+    mwa_uv.read([set1, set2])
+
+    mwa_uv2 = UVData()
+    messages = ['Please use the generic `read` method',
+                'telescope_location is not set',
                 'some coarse channel files were not submitted',
                 'telescope_location is not set',
                 'some coarse channel files were not submitted',
-                'Combined frequencies are not contiguous. This will make it impossible to write this data out to some file types.']
-    uvtest.checkWarnings(mwa_uv.read_mwa_corr_fits, func_args=[[[set1], [set2]]],
-                         nwarnings=5, message=messages)
+                'Combined frequencies are not contiguous']
+    category = [DeprecationWarning] + [UserWarning] * 5
+    uvtest.checkWarnings(mwa_uv2.read_mwa_corr_fits, func_args=[[set1, set2]],
+                         nwarnings=6, message=messages, category=category)
 
+    assert(mwa_uv == mwa_uv2)
 
+
+@pytest.mark.filterwarnings("ignore:telescope_location is not set. ")
+@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
 def test_ReadMWA_multi_concat():
     """Test reading in two sets of files with fast concatenation."""
     # modify file so that time arrays are matching
@@ -112,14 +160,24 @@ def test_ReadMWA_multi_concat():
     set1 = filelist[0:2]
     set2 = [filelist[0], mod_mini_6]
     mwa_uv = UVData()
-    messages = ['telescope_location is not set',
+    mwa_uv.read([set1, set2], axis='freq')
+
+    mwa_uv2 = UVData()
+    messages = ['Please use the generic `read` method',
+                'telescope_location is not set',
                 'some coarse channel files were not submitted',
                 'telescope_location is not set',
                 'some coarse channel files were not submitted']
-    uvtest.checkWarnings(mwa_uv.read_mwa_corr_fits, func_args=[[[set1], [set2]]],
-                         func_kwargs={"axis": "freq"}, nwarnings=4, message=messages)
+    category = [DeprecationWarning] + [UserWarning] * 4
+    uvtest.checkWarnings(mwa_uv2.read_mwa_corr_fits, func_args=[[set1, set2]],
+                         func_kwargs={"axis": "freq"}, nwarnings=5,
+                         message=messages, category=category)
+    assert(mwa_uv == mwa_uv2)
+    os.remove(mod_mini_6)
 
 
+@pytest.mark.filterwarnings("ignore:telescope_location is not set. ")
+@pytest.mark.filterwarnings("ignore:some coarse channel files were not submitted")
 def test_ReadMWA_flags():
     """Test handling of flag files."""
     mwa_uv = UVData()
@@ -127,17 +185,17 @@ def test_ReadMWA_flags():
     messages = ['mwaf files submitted with use_cotter_flags=False',
                 'telescope_location is not set',
                 'some coarse channel files were not submitted']
-    uvtest.checkWarnings(mwa_uv.read_mwa_corr_fits, func_args=[subfiles],
+    uvtest.checkWarnings(mwa_uv.read, func_args=[subfiles],
                          nwarnings=3, message=messages)
     del(mwa_uv)
     mwa_uv = UVData()
     with pytest.raises(NotImplementedError) as cm:
-        mwa_uv.read_mwa_corr_fits(subfiles, use_cotter_flags=True)
+        mwa_uv.read(subfiles, use_cotter_flags=True)
     assert str(cm.value).startswith('reading in cotter flag files')
     del(mwa_uv)
     mwa_uv = UVData()
     with pytest.raises(ValueError) as cm:
-        mwa_uv.read_mwa_corr_fits(subfiles[0:2], use_cotter_flags=True)
+        mwa_uv.read(subfiles[0:2], use_cotter_flags=True)
     assert str(cm.value).startswith('no flag files submitted')
     del(mwa_uv)
 
@@ -156,9 +214,9 @@ def test_multiple_coarse():
     messages = ['telescope_location is not set',
                 'coarse channels are not contiguous for this observation',
                 'some coarse channel files were not submitted']
-    uvtest.checkWarnings(mwa_uv1.read_mwa_corr_fits, func_args=[order1],
+    uvtest.checkWarnings(mwa_uv1.read, func_args=[order1],
                          nwarnings=3, message=messages)
-    uvtest.checkWarnings(mwa_uv2.read_mwa_corr_fits, func_args=[order2],
+    uvtest.checkWarnings(mwa_uv2.read, func_args=[order2],
                          nwarnings=3, message=messages)
     assert mwa_uv1 == mwa_uv2
 
@@ -176,7 +234,7 @@ def test_fine_channels():
         mini6[1].data = np.concatenate((mini6[1].data, mini6[1].data))
         mini6.writeto(bad_fine)
     with pytest.raises(ValueError) as cm:
-        mwa_uv.read_mwa_corr_fits([bad_fine, filelist[1]])
+        mwa_uv.read([bad_fine, filelist[1]])
     assert str(cm.value).startswith('files submitted have different fine')
     del(mwa_uv)
 
@@ -190,7 +248,7 @@ def test_break_ReadMWAcorrFITS(files, err_msg):
     """Break read_mwa_corr_fits by submitting files incorrectly."""
     mwa_uv = UVData()
     with pytest.raises(ValueError) as cm:
-        mwa_uv.read_mwa_corr_fits(files)
+        mwa_uv.read(files)
     assert str(cm.value).startswith(err_msg)
     del(mwa_uv)
 
@@ -207,7 +265,7 @@ def test_file_extension():
     with fits.open(filelist[0]) as meta:
         meta.writeto(bad_ext)
     with pytest.raises(ValueError) as cm:
-        mwa_uv.read_mwa_corr_fits(bad_ext)
+        mwa_uv.read(bad_ext, file_type='mwa_corr_fits')
     assert str(cm.value).startswith('only fits, metafits, and mwaf files supported')
     del(mwa_uv)
 
@@ -225,6 +283,6 @@ def test_diff_obs():
         mini6[0].header['OBSID'] = '1131733555'
         mini6.writeto(bad_obs)
     with pytest.raises(ValueError) as cm:
-        mwa_uv.read_mwa_corr_fits([bad_obs, filelist[0], filelist[1]])
+        mwa_uv.read([bad_obs, filelist[0], filelist[1]])
     assert str(cm.value).startswith('files from different observations')
     del(mwa_uv)
diff --git a/pyuvdata/tests/test_uvdata.py b/pyuvdata/tests/test_uvdata.py
index ae710852..2d53a270 100644
--- a/pyuvdata/tests/test_uvdata.py
+++ b/pyuvdata/tests/test_uvdata.py
@@ -2,9 +2,7 @@
 # Copyright (c) 2018 Radio Astronomy Software Group
 # Licensed under the 2-clause BSD License
 
-"""Tests for uvdata object.
-
-"""
+"""Tests for uvdata object."""
 from __future__ import absolute_import, division, print_function
 
 import pytest
@@ -130,8 +128,91 @@ def bda_test_file():
     return
 
 
+@pytest.fixture(scope='function')
+def uvdata_data():
+    uv_object = UVData()
+    testfile = os.path.join(DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
+    uvtest.checkWarnings(uv_object.read_uvfits, [testfile],
+                         message='Telescope EVLA is not')
+
+    class DataHolder():
+        def __init__(self, uv_object):
+            self.uv_object = uv_object
+            self.uv_object2 = copy.deepcopy(uv_object)
+
+    uvdata_data = DataHolder(uv_object)
+    # yields the data we need but will continue to the del call after tests
+    yield uvdata_data
+
+    # some post-test object cleanup
+    del(uvdata_data)
+
+    return
+
+
+@pytest.fixture(scope='function')
+def uvdata_baseline():
+    uv_object = UVData()
+    uv_object.Nants_telescope = 128
+    uv_object2 = UVData()
+    uv_object2.Nants_telescope = 2049
+
+    class DataHolder():
+        def __init__(self, uv_object, uv_object2):
+            self.uv_object = uv_object
+            self.uv_object2 = uv_object2
+
+    uvdata_baseline = DataHolder(uv_object, uv_object2)
+
+    # 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
+def uv1_2_set_uvws():
+    testfile = os.path.join(DATA_PATH, 'zen.2458661.23480.HH.uvh5')
+    uv1 = UVData()
+    uv1.read_uvh5(testfile)
+    # uvws in the file are wrong. reset them.
+    uv1.set_uvws_from_antenna_positions()
+
+    uv2 = uv1.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, 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
+
+
 def test_parameter_iter(uvdata_props):
-    "Test expected parameters."
+    """Test expected parameters."""
     all = []
     for prop in uvdata_props.uv_object:
         all.append(prop)
@@ -140,8 +221,7 @@ def test_parameter_iter(uvdata_props):
 
 
 def test_required_parameter_iter(uvdata_props):
-    "Test expected required parameters."
-
+    """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():
@@ -164,7 +244,7 @@ def test_required_parameter_iter(uvdata_props):
 
 
 def test_extra_parameter_iter(uvdata_props):
-    "Test expected optional parameters."
+    """Test expected optional parameters."""
     extra = []
     for prop in uvdata_props.uv_object.extra():
         extra.append(prop)
@@ -173,7 +253,7 @@ def test_extra_parameter_iter(uvdata_props):
 
 
 def test_unexpected_parameters(uvdata_props):
-    "Test for extra parameters."
+    """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:
@@ -181,7 +261,7 @@ def test_unexpected_parameters(uvdata_props):
 
 
 def test_unexpected_attributes(uvdata_props):
-    "Test for extra attributes."
+    """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] != '_']
@@ -190,7 +270,7 @@ def test_unexpected_attributes(uvdata_props):
 
 
 def test_properties(uvdata_props):
-    "Test that properties can be get and set properly."
+    """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():
@@ -204,28 +284,6 @@ def test_properties(uvdata_props):
             raise
 
 
-@pytest.fixture(scope='function')
-def uvdata_data():
-    uv_object = UVData()
-    testfile = os.path.join(DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
-    uvtest.checkWarnings(uv_object.read_uvfits, [testfile],
-                         message='Telescope EVLA is not')
-
-    class DataHolder():
-        def __init__(self, uv_object):
-            self.uv_object = uv_object
-            self.uv_object2 = copy.deepcopy(uv_object)
-
-    uvdata_data = DataHolder(uv_object)
-    # yields the data we need but will continue to the del call after tests
-    yield uvdata_data
-
-    # some post-test object cleanup
-    del(uvdata_data)
-
-    return
-
-
 def test_metadata_only_property(uvdata_data):
     uvdata_data.uv_object.data_array = None
     assert uvdata_data.uv_object.metadata_only is False
@@ -336,28 +394,6 @@ def test_converttofiletype(uvdata_data):
     assert str(cm.value).startswith("filetype must be uvfits, miriad, fhd, or uvh5")
 
 
-@pytest.fixture(scope='function')
-def uvdata_baseline():
-    uv_object = UVData()
-    uv_object.Nants_telescope = 128
-    uv_object2 = UVData()
-    uv_object2.Nants_telescope = 2049
-
-    class DataHolder():
-        def __init__(self, uv_object, uv_object2):
-            self.uv_object = uv_object
-            self.uv_object2 = uv_object2
-
-    uvdata_baseline = DataHolder(uv_object, uv_object2)
-
-    # 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
-
-
 def test_baseline_to_antnums(uvdata_baseline):
     """Test baseline to antnum conversion for 256 & larger conventions."""
     assert uvdata_baseline.uv_object.baseline_to_antnums(67585) == (0, 0)
@@ -450,27 +486,6 @@ def test_generic_read():
     pytest.raises(ValueError, uv_in.read, 'foo')
 
 
-@pytest.fixture
-def uv_phase_and_raw():
-    testfile = os.path.join(DATA_PATH, 'zen.2458661.23480.HH.uvh5')
-    UV_raw = UVData()
-    # Note the RA/DEC values in the raw file were calculated from the lat/long
-    # in the file, which don't agree with our known_telescopes.
-    # So for this test we use the lat/lon in the file.
-    UV_raw.read_uvh5(testfile)
-    # uvtest.checkWarnings(UV_raw.read_miriad, [testfile], {'correct_lat_lon': False},
-    #                      message='Altitude is not present in file and latitude and '
-    #                              'longitude values do not match')
-    UV_phase = UVData()
-    UV_phase.read_uvh5(testfile)
-
-    yield UV_phase, UV_raw
-
-    del UV_phase, UV_raw
-
-    return
-
-
 @pytest.mark.parametrize(
     "phase_kwargs",
     [
@@ -482,19 +497,19 @@ def uv_phase_and_raw():
 
     ]
 )
-def test_phase_unphaseHERA(uv_phase_and_raw, phase_kwargs):
+def test_phase_unphaseHERA(uv1_2_set_uvws, phase_kwargs):
     """
     Read in drift data, phase to an RA/DEC, unphase and check for object equality.
     """
-    UV_phase, UV_raw = uv_phase_and_raw
-    UV_phase.phase(**phase_kwargs)
-    UV_phase.unphase_to_drift()
+    uv1, UV_raw = uv1_2_set_uvws
+    uv1.phase(**phase_kwargs)
+    uv1.unphase_to_drift()
     # check that phase + unphase gets back to raw
-    assert UV_raw == UV_phase
+    assert UV_raw == uv1
 
 
-def test_phase_unphaseHERA_one_bl(uv_phase_and_raw):
-    UV_phase, UV_raw = uv_phase_and_raw
+def test_phase_unphaseHERA_one_bl(uv1_2_set_uvws):
+    UV_phase, 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 = copy.deepcopy(UV_raw_small)
@@ -503,8 +518,8 @@ def test_phase_unphaseHERA_one_bl(uv_phase_and_raw):
     assert UV_raw_small == UV_phase_small
 
 
-def test_phase_unphaseHERA_antpos(uv_phase_and_raw):
-    UV_phase, UV_raw = uv_phase_and_raw
+def test_phase_unphaseHERA_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),
@@ -537,8 +552,8 @@ def test_phase_unphaseHERA_antpos(uv_phase_and_raw):
     assert UV_raw_new == UV_phase
 
 
-def test_phase_unphaseHERA_zenith_timestamp(uv_phase_and_raw):
-    UV_phase, UV_raw = uv_phase_and_raw
+def test_phase_unphaseHERA_zenith_timestamp(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)
@@ -551,23 +566,23 @@ def test_phase_unphaseHERA_zenith_timestamp(uv_phase_and_raw):
     assert np.allclose(UV_phase_simple_small.uvw_array, UV_raw_small.uvw_array, atol=1e-1)
 
 
-def test_phase_to_time_jd_input(uv_phase_and_raw):
-    UV_phase, UV_raw = uv_phase_and_raw
+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.unphase_to_drift()
     assert UV_phase == UV_raw
 
 
-def test_phase_to_time_error(uv_phase_and_raw):
-    UV_phase, UV_raw = uv_phase_and_raw
+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) as cm:
         UV_phase.phase_to_time('foo')
     assert str(cm.value).startswith("time must be an astropy.time.Time object")
 
 
-def test_unphase_drift_data_error(uv_phase_and_raw):
-    UV_phase, UV_raw = uv_phase_and_raw
+def test_unphase_drift_data_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(ValueError) as cm:
         UV_phase.unphase_to_drift()
@@ -578,58 +593,60 @@ def test_unphase_drift_data_error(uv_phase_and_raw):
     "phase_func,phase_kwargs,err_msg",
     [("unphase_to_drift", {},
       "The phasing type of the data is unknown. Set the phase_type"),
-     ("phase", {"ra": 0, "dec": 0, "epoch": "J2000"},
+     ("phase", {"ra": 0, "dec": 0, "epoch": "J2000", "allow_rephase": False},
       "The phasing type of the data is unknown. Set the phase_type"),
-     ("phase_to_time", {"time": 0},
+     ("phase_to_time", {"time": 0, "allow_rephase": False},
       "The phasing type of the data is unknown. Set the phase_type")
      ]
 )
 def test_unknown_phase_unphaseHERA_errors(
-    uv_phase_and_raw, phase_func, phase_kwargs, err_msg
+    uv1_2_set_uvws, phase_func, phase_kwargs, err_msg
 ):
-    UV_phase, UV_raw = uv_phase_and_raw
+    UV_phase, UV_raw = uv1_2_set_uvws
 
     # Set phase type to unkown on some tests, ignore on others.
-    UV_raw.set_unknown_phase_type()
+    UV_phase.set_unknown_phase_type()
     # 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_raw.time_array in the parametrize
-    if phase_func == "phsae_to_time":
-        phase_kwargs["time"] = UV_raw.time_array[phase_kwargs["time"]]
+    # 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[phase_kwargs["time"]]
 
     with pytest.raises(ValueError) as cm:
-        getattr(UV_raw, phase_func)(**phase_kwargs)
+        getattr(UV_phase, phase_func)(**phase_kwargs)
     assert str(cm.value).startswith(err_msg)
 
 
 @pytest.mark.parametrize(
     "phase_func,phase_kwargs,err_msg",
-    [("phase", {"ra": 0, "dec": 0, "epoch": "J2000"},
+    [("phase", {"ra": 0, "dec": 0, "epoch": "J2000", "allow_rephase": False},
       "The data is already phased;"),
-     ("phase_to_time", {"time": 0},
+     ("phase_to_time", {"time": 0, "allow_rephase": False},
       "The data is already phased;")
      ]
 )
 def test_phase_rephaseHERA_errors(
-    uv_phase_and_raw, phase_func, phase_kwargs, err_msg
+    uv1_2_set_uvws, phase_func, phase_kwargs, err_msg
 ):
-    UV_phase, UV_raw = uv_phase_and_raw
+    UV_phase, UV_raw = uv1_2_set_uvws
 
     # Set phase type to unkown on some tests, ignore on others.
-    UV_raw.phase(0., 0., epoch="J2000")
+    UV_phase.phase(0., 0., epoch="J2000")
     # 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_raw.time_array in the parametrize
-    if phase_func == "phsae_to_time":
-        phase_kwargs["time"] = UV_raw.time_array[phase_kwargs["time"]]
+    # 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[phase_kwargs["time"]]
 
     with pytest.raises(ValueError) as cm:
-        getattr(UV_raw, phase_func)(**phase_kwargs)
+        getattr(UV_phase, phase_func)(**phase_kwargs)
     assert str(cm.value).startswith(err_msg)
 
 
-def test_phase_unphaseHERA_bad_frame(uv_phase_and_raw):
-    UV_phase, UV_raw = uv_phase_and_raw
+def test_phase_unphaseHERA_bad_frame(uv1_2_set_uvws):
+    UV_phase, UV_raw = uv1_2_set_uvws
     # check errors when trying to phase to an unsupported frame
     with pytest.raises(ValueError) as cm:
         UV_phase.phase(0., 0., epoch="J2000", phase_frame='cirs')
@@ -637,7 +654,7 @@ def test_phase_unphaseHERA_bad_frame(uv_phase_and_raw):
 
 
 def test_phasing():
-    """ Use MWA files phased to 2 different places to test phasing. """
+    """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()
@@ -655,29 +672,34 @@ def test_phasing():
     uvd2_drift_antpos = copy.deepcopy(uvd2)
     uvd2_drift_antpos.unphase_to_drift(phase_frame='gcrs', use_ant_pos=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
+    # 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)
 
-    uvd2_rephase = copy.deepcopy(uvd2_drift)
+    uvd2_rephase = uvd2.copy()
     uvd2_rephase.phase(uvd1.phase_center_ra,
                        uvd1.phase_center_dec,
                        uvd1.phase_center_epoch,
+                       orig_phase_frame='gcrs',
                        phase_frame='gcrs')
-    uvd2_rephase_antpos = copy.deepcopy(uvd2_drift_antpos)
+    uvd2_rephase_antpos = uvd2.copy()
     uvd2_rephase_antpos.phase(uvd1.phase_center_ra,
                               uvd1.phase_center_dec,
                               uvd1.phase_center_epoch,
+                              orig_phase_frame='gcrs',
                               phase_frame='gcrs',
                               use_ant_pos=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
+    # 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
+    # rephase the drift objects to the original pointing and verify that they
+    # match
     uvd1_drift.phase(uvd1.phase_center_ra, uvd1.phase_center_dec,
                      uvd1.phase_center_epoch, phase_frame='gcrs')
     uvd1_drift_antpos.phase(uvd1.phase_center_ra, uvd1.phase_center_dec,
@@ -685,8 +707,9 @@ def test_phasing():
                             use_ant_pos=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
+    # 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)
 
@@ -697,8 +720,9 @@ def test_phasing():
                             use_ant_pos=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
+    # 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)
 
@@ -2142,6 +2166,213 @@ def test_break_add():
     pytest.raises(ValueError, uv1.__iadd__, uv2)
 
 
+@pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
+@pytest.mark.parametrize("test_func,extra_kwargs",
+                         [("__add__", {}),
+                          ("fast_concat", {"axis": "blt"})
+                          ]
+                         )
+def test_add_error_drift_and_rephase(test_func, extra_kwargs):
+    uv_full = UVData()
+    testfile = os.path.join(DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
+    uv_full.read_uvfits(testfile)
+
+    with pytest.raises(ValueError) as cm:
+        getattr(uv_full, test_func)(uv_full, phase_center_radec=(0, 45),
+                                    unphase_to_drift=True,
+                                    **extra_kwargs
+                                    )
+    assert str(cm.value).startswith('phase_center_radec cannot be set if '
+                                    'unphase_to_drift is True.')
+
+
+@pytest.mark.parametrize("test_func,extra_kwargs",
+                         [("__add__", {}),
+                          ("fast_concat", {"axis": "blt"})
+                          ]
+                         )
+def test_add_this_phased_unphase_to_drift(
+    uv_phase_time_split, test_func, extra_kwargs
+):
+    (
+        uv_phase_1, uv_phase_2, uv_phase, uv_raw_1, uv_raw_2, uv_raw
+    ) = uv_phase_time_split
+
+    func_kwargs = {"unphase_to_drift": True,
+                   "inplace": False,
+                   }
+    func_kwargs.update(extra_kwargs)
+    uv_out = uvtest.checkWarnings(
+        getattr(uv_phase_1, test_func),
+        func_args=[uv_raw_2],
+        func_kwargs=func_kwargs,
+        message=['Unphasing this UVData object to drift']
+    )
+    # 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 uv_out.phase_type == 'drift'
+    assert uv_out == uv_raw
+
+
+@pytest.mark.parametrize("test_func,extra_kwargs",
+                         [("__add__", {}),
+                          ("fast_concat", {"axis": "blt"})
+                          ]
+                         )
+def test_add_other_phased_unphase_to_drift(
+    uv_phase_time_split, test_func, extra_kwargs
+):
+    (
+        uv_phase_1, uv_phase_2, uv_phase, uv_raw_1, uv_raw_2, uv_raw
+    ) = uv_phase_time_split
+
+    func_kwargs = {"unphase_to_drift": True,
+                   "inplace": False,
+                   }
+    func_kwargs.update(extra_kwargs)
+    uv_out = uvtest.checkWarnings(
+        getattr(uv_raw_1, test_func),
+        func_args=[uv_phase_2],
+        func_kwargs=func_kwargs,
+        message=['Unphasing other UVData object to drift']
+    )
+    # 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 uv_out.phase_type == 'drift'
+    assert uv_out == uv_raw
+
+
+@pytest.mark.parametrize("test_func,extra_kwargs",
+                         [("__add__", {}),
+                          ("fast_concat", {"axis": "blt"})
+                          ]
+                         )
+def test_add_this_rephase_new_phase_center(
+    uv_phase_time_split, test_func, extra_kwargs
+):
+    (
+        uv_phase_1, uv_phase_2, uv_phase, uv_raw_1, uv_raw_2, uv_raw
+    ) = uv_phase_time_split
+
+    phase_center_radec = (Angle('0d').rad, Angle('-30d').rad)
+
+    # phase each half to different spots
+    uv_raw_1.phase(ra=0,
+                   dec=0,
+                   use_ant_pos=True,
+                   )
+    uv_raw_2.phase(ra=phase_center_radec[0],
+                   dec=phase_center_radec[1],
+                   use_ant_pos=True
+                   )
+    # phase original to phase_center_radec
+    uv_raw.phase(ra=phase_center_radec[0],
+                 dec=phase_center_radec[1],
+                 use_ant_pos=True
+                 )
+
+    func_kwargs = {"inplace": False,
+                   "phase_center_radec": phase_center_radec,
+                   "use_ant_pos": True
+                   }
+    func_kwargs.update(extra_kwargs)
+    uv_out = uvtest.checkWarnings(
+        getattr(uv_raw_1, test_func),
+        func_args=[uv_raw_2],
+        func_kwargs=func_kwargs,
+        message=['Phasing this UVData object to phase_center_radec']
+    )
+    # 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 (uv_out.phase_center_ra, uv_out.phase_center_dec) == phase_center_radec
+    assert uv_out == uv_raw
+
+
+@pytest.mark.parametrize("test_func,extra_kwargs",
+                         [("__add__", {}),
+                          ("fast_concat", {"axis": "blt"})
+                          ]
+                         )
+def test_add_other_rephase_new_phase_center(
+    uv_phase_time_split, test_func, extra_kwargs
+):
+    (
+        uv_phase_1, uv_phase_2, uv_phase, uv_raw_1, uv_raw_2, uv_raw
+    ) = uv_phase_time_split
+
+    phase_center_radec = (Angle('0d').rad, Angle('-30d').rad)
+
+    # phase each half to different spots
+    uv_raw_1.phase(ra=phase_center_radec[0],
+                   dec=phase_center_radec[1],
+                   use_ant_pos=True,
+                   )
+    uv_raw_2.phase(ra=0,
+                   dec=0,
+                   use_ant_pos=True,
+                   )
+    # phase original to phase_center_radec
+    uv_raw.phase(ra=phase_center_radec[0],
+                 dec=phase_center_radec[1],
+                 use_ant_pos=True,
+                 )
+
+    func_kwargs = {"inplace": False,
+                   "phase_center_radec": phase_center_radec,
+                   "use_ant_pos": True,
+                   }
+    func_kwargs.update(extra_kwargs)
+    uv_out = uvtest.checkWarnings(
+        getattr(uv_raw_1, test_func),
+        func_args=[uv_raw_2],
+        func_kwargs=func_kwargs,
+        message=['Phasing other UVData object to phase_center_radec']
+    )
+    # 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 uv_out.phase_type == "phased"
+    assert (uv_out.phase_center_ra, uv_out.phase_center_dec) == phase_center_radec
+    assert uv_out == uv_raw
+
+
+@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, uv_raw_1, uv_raw_2, uv_raw
+    ) = 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) as cm:
+        getattr(uv_phase_1, test_func)(uv_phase_2, **func_kwargs)
+    assert str(cm.value).startswith('phase_center_radec should have length 2.')
+
+
 @pytest.mark.filterwarnings("ignore:Telescope EVLA is not")
 def test_fast_concat():
     uv_full = UVData()
@@ -4060,7 +4291,7 @@ def test_overlapping_data_add():
     uv4.write_uvfits(uv4_out)
 
     uvfull = UVData()
-    uvfull.read([uv1_out, uv2_out, uv3_out, uv4_out])
+    uvfull.read(np.array([uv1_out, uv2_out, uv3_out, uv4_out]))
     assert uvutils._check_histories(uvfull.history, uv.history + extra_history)
     uvfull.history = uv.history  # make histories match
     assert uvfull == uv
diff --git a/pyuvdata/tests/test_uvfits.py b/pyuvdata/tests/test_uvfits.py
index e01104fa..e08924f3 100644
--- a/pyuvdata/tests/test_uvfits.py
+++ b/pyuvdata/tests/test_uvfits.py
@@ -25,37 +25,75 @@ from pyuvdata.data import DATA_PATH
 def test_ReadNRAO():
     """Test reading in a CASA tutorial uvfits file."""
     UV = UVData()
-    testfile = os.path.join(DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
+    testfile = os.path.join(DATA_PATH,
+                            'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
     expected_extra_keywords = ['OBSERVER', 'SORTORD', 'SPECSYS',
                                'RESTFREQ', 'ORIGIN']
-    uvtest.checkWarnings(UV.read, [testfile], message='Telescope EVLA is not')
-    assert expected_extra_keywords.sort() == list(UV.extra_keywords.keys()).sort()
+    uvtest.checkWarnings(UV.read_uvfits, func_args=[testfile],
+                         message='Telescope EVLA is not')
+    assert (expected_extra_keywords.sort()
+            == list(UV.extra_keywords.keys()).sort())
 
     # test reading in header data first, then metadata and then data
     UV2 = UVData()
-    uvtest.checkWarnings(UV2.read, [testfile], {'read_data': False, 'read_metadata': False},
-                         message='Telescope EVLA is not')
-    assert expected_extra_keywords.sort() == list(UV2.extra_keywords.keys()).sort()
+    uvtest.checkWarnings(UV2.read, func_args=[testfile],
+                         func_kwargs={'read_data': False,
+                                      'read_metadata': False},
+                         message=['Telescope EVLA is not',
+                                  'Support for reading only the header'],
+                         category=[UserWarning, DeprecationWarning],
+                         nwarnings=2)
+    assert (expected_extra_keywords.sort()
+            == list(UV2.extra_keywords.keys()).sort())
     with pytest.raises(ValueError) as cm:
         UV2.check()
     assert str(cm.value).startswith('Required UVParameter')
 
-    UV2.read(testfile, read_data=False)
+    # do this two ways, with `read` and with deprecated `read_uvfits_metadata`
+    UV3 = UV2.copy()
+    uvtest.checkWarnings(UV2.read, func_args=[testfile],
+                         func_kwargs={'read_data': False},
+                         message='Telescope EVLA is not')
     assert UV2.check()
-    UV2.read(testfile)
+
+    uvfits_obj = UV3._convert_to_filetype('uvfits')
+    uvtest.checkWarnings(uvfits_obj.read_uvfits_metadata, func_args=[testfile],
+                         message='The read_uvfits_metadata method is deprecated',
+                         category=DeprecationWarning)
+    UV3._convert_from_filetype(uvfits_obj)
+    assert UV3 == UV2
+
+    uvtest.checkWarnings(UV2.read, func_args=[testfile],
+                         message='Telescope EVLA is not')
     assert UV == UV2
+
     # test reading in header & metadata first, then data
+    # do this two ways, with `read` and with deprecated `read_uvfits_data`
     UV2 = UVData()
-    uvtest.checkWarnings(UV2.read, [testfile], {'read_data': False},
+    uvtest.checkWarnings(UV2.read, func_args=[testfile],
+                         func_kwargs={'read_data': False},
                          message='Telescope EVLA is not')
-    assert expected_extra_keywords.sort() == list(UV2.extra_keywords.keys()).sort()
+    assert (expected_extra_keywords.sort()
+            == list(UV2.extra_keywords.keys()).sort())
     assert UV2.check()
-    UV2.read(testfile)
+    UV3 = UV2.copy()
+    uvtest.checkWarnings(UV2.read, func_args=[testfile],
+                         message='Telescope EVLA is not')
     assert UV == UV2
 
+    uvfits_obj = UV3._convert_to_filetype('uvfits')
+    uvtest.checkWarnings(uvfits_obj.read_uvfits_data, func_args=[testfile],
+                         message='The read_uvfits_data method is deprecated',
+                         category=DeprecationWarning)
+    UV3._convert_from_filetype(uvfits_obj)
+    assert UV3 == UV2
+
     # check error trying to read metadata after data is already present
+    uvfits_obj = UV3._convert_to_filetype('uvfits')
     with pytest.raises(ValueError) as cm:
-        UV2.read(testfile, read_data=False)
+        uvtest.checkWarnings(uvfits_obj.read_uvfits_metadata, func_args=[testfile],
+                             message='The read_uvfits_metadata method is deprecated',
+                             category=DeprecationWarning)
     assert str(cm.value).startswith('data_array is already defined, cannot read metadata')
 
 
@@ -203,7 +241,9 @@ def test_casa_nonascii_bytes_antenna_names():
     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
-    uv1.read(testfile, run_check=False)
+    uvtest.checkWarnings(uv1.read, func_args=[testfile],
+                         func_kwargs={'run_check': False},
+                         message='Telescope mock-HERA is not in known_telescopes')
     expected_ant_names = [
         'HH0', 'HH1', 'HH2', 'H2', 'H2', 'H2', 'H2', 'H2', 'H2', 'H2',
         'H2', 'HH11', 'HH12', 'HH13', 'HH14', 'H14', 'H14', 'H14', 'H14',
@@ -674,7 +714,11 @@ def test_multi_files():
     uv2.select(freq_chans=np.arange(32, 64))
     uv1.write_uvfits(testfile1)
     uv2.write_uvfits(testfile2)
-    uv1.read([testfile1, testfile2])
+    uvtest.checkWarnings(
+        uv1.read_uvfits, func_args=[np.array([testfile1, testfile2])],
+        message=(['Please use the generic'] + 2 * ['Telescope EVLA is not']),
+        category=[DeprecationWarning] + 2 * [UserWarning],
+        nwarnings=3)
     # 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. '
@@ -716,13 +760,113 @@ def test_multi_files():
     assert str(cm.value).startswith('A list of files cannot be used when just '
                                     'reading the header')
 
-    # check raises error if only reading data on a list of files (metadata already read)
-    uv1 = UVData()
-    uv1.read(uvfits_file, read_data=False)
+
+@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():
+    uv_full = UVData()
+    uv_full2 = UVData()
+    uvfits_file = os.path.join(DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
+    testfile1 = os.path.join(DATA_PATH, 'test/uv1.uvfits')
+    testfile2 = os.path.join(DATA_PATH, 'test/uv2.uvfits')
+    uv_full.read(uvfits_file)
+    uv1 = copy.deepcopy(uv_full)
+    uv2 = copy.deepcopy(uv_full)
+    uv1.select(freq_chans=np.arange(0, 32))
+    uv2.select(freq_chans=np.arange(32, 64))
+    uv1.write_uvfits(testfile1)
+    uv2.write_uvfits(testfile2)
+    uvtest.checkWarnings(
+        uv1.read, func_args=[np.array([testfile1, testfile2])],
+        func_kwargs={'unphase_to_drift': True},
+        message=(['Telescope EVLA is not'] * 2
+                 + ['Unphasing this UVData object to drift',
+                    'The xyz array in ENU_from_ECEF is being interpreted',
+                    'Unphasing other UVData object to drift',
+                    'The xyz array in ENU_from_ECEF is being interpreted']),
+        category=[UserWarning] * 2 + [UserWarning, DeprecationWarning] * 2,
+        nwarnings=6)
+
+    # 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.unphase_to_drift()
+
+    uv1.history = uv_full.history
+    assert uv1 == uv_full
+
+    # check unphasing when reading only one file
+    uvtest.checkWarnings(
+        uv_full2.read, func_args=[uvfits_file],
+        func_kwargs={'unphase_to_drift': True},
+        message=(['Telescope EVLA is not',
+                  'Unphasing this UVData object to drift',
+                  'The xyz array in ENU_from_ECEF is being interpreted']),
+        category=[UserWarning, UserWarning, DeprecationWarning],
+        nwarnings=3)
+    assert uv_full2 == uv_full
+
+
+@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")
+def test_multi_phase_on_read():
+    uv_full = UVData()
+    uv_full2 = UVData()
+    uvfits_file = os.path.join(DATA_PATH, 'day2_TDEM0003_10s_norx_1src_1spw.uvfits')
+    testfile1 = os.path.join(DATA_PATH, 'test/uv1.uvfits')
+    testfile2 = os.path.join(DATA_PATH, 'test/uv2.uvfits')
+    uv_full.read(uvfits_file)
+    phase_center_radec = [uv_full.phase_center_ra + 0.01,
+                          uv_full.phase_center_dec + 0.01]
+    uv1 = copy.deepcopy(uv_full)
+    uv2 = copy.deepcopy(uv_full)
+    uv1.select(freq_chans=np.arange(0, 32))
+    uv2.select(freq_chans=np.arange(32, 64))
+    uv1.write_uvfits(testfile1)
+    uv2.write_uvfits(testfile2)
+    uvtest.checkWarnings(
+        uv1.read, func_args=[np.array([testfile1, testfile2])],
+        func_kwargs={'phase_center_radec': phase_center_radec},
+        message=(['Telescope EVLA is not'] * 2
+                 + ['Phasing this UVData object to phase_center_radec',
+                    'The xyz array in ENU_from_ECEF is being interpreted',
+                    'The enu array in ECEF_from_ENU is being interpreted',
+                    'Phasing this UVData object to phase_center_radec',
+                    'The xyz array in ENU_from_ECEF is being interpreted',
+                    'The enu array in ECEF_from_ENU is being interpreted']),
+        category=([UserWarning] * 2
+                  + [UserWarning, DeprecationWarning, DeprecationWarning] * 2),
+        nwarnings=8)
+
+    # 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)
+    uv1.history = uv_full.history
+    assert uv1 == uv_full
+
+    # check phasing when reading only one file
+    uvtest.checkWarnings(
+        uv_full2.read, func_args=[uvfits_file],
+        func_kwargs={'phase_center_radec': phase_center_radec},
+        message=(['Telescope EVLA is not',
+                  'Phasing this UVData object to phase_center_radec',
+                  'The xyz array in ENU_from_ECEF is being interpreted',
+                  'The enu array in ECEF_from_ENU is being interpreted']),
+        category=[UserWarning, UserWarning, DeprecationWarning, DeprecationWarning],
+        nwarnings=4)
+    assert uv_full2 == uv_full
+
     with pytest.raises(ValueError) as cm:
-        uv1.read([testfile1, testfile2])
-    assert str(cm.value).startswith('A list of files cannot be used when just '
-                                    'reading data (metadata already exists)')
+        uv_full2.read(uvfits_file, phase_center_radec=phase_center_radec[0])
+    assert str(cm.value).startswith('phase_center_radec should have length 2.')
 
 
 @uvtest.skipIf_no_casa
diff --git a/pyuvdata/tests/test_uvh5.py b/pyuvdata/tests/test_uvh5.py
index eb4febee..7c978561 100644
--- a/pyuvdata/tests/test_uvh5.py
+++ b/pyuvdata/tests/test_uvh5.py
@@ -158,7 +158,7 @@ def test_read_miriad_write_uvh5_read_uvh5(uv_miriad):
     # 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(testfile)
+    uv_out.read_uvh5(testfile)
 
     assert uv_in == uv_out
 
@@ -302,7 +302,13 @@ def test_uvh5_read_multiple_files(uv_uvfits):
     uv2.select(freq_chans=np.arange(32, 64))
     uv1.write_uvh5(testfile1, clobber=True)
     uv2.write_uvh5(testfile2, clobber=True)
-    uv1.read([testfile1, testfile2])
+    uvtest.checkWarnings(
+        uv1.read_uvh5,
+        func_args=[np.array([testfile1, testfile2])],
+        message=(['Please use the generic'] + 2 * ['Telescope EVLA is not']),
+        category=[DeprecationWarning] + 2 * [UserWarning],
+        nwarnings=3,
+    )
     # 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. '
@@ -1458,7 +1464,7 @@ def test_uvh5_read_ints(uv_uvh5):
     uv_in.write_uvh5(testfile, clobber=True)
 
     # read it back in to make sure data is the same
-    uv_out.read_uvh5(testfile)
+    uv_out.read(testfile)
     assert uv_in == uv_out
 
     # now read in as np.complex128
@@ -1498,7 +1504,7 @@ def test_uvh5_write_ints(uv_uvh5):
     uv_in.write_uvh5(testfile, clobber=True, data_write_dtype=_hera_corr_dtype)
 
     # read it back in to make sure data is the same
-    uv_out.read_uvh5(testfile)
+    uv_out.read(testfile)
     assert uv_in == uv_out
 
     # also check that the datatype on disk is the right type