def test_shift_to_rest_galaxy():
    """
    This tests storing a spectral coordinate with a specific redshift, and then
    doing basic rest-to-observed-and-back transformations
    """
    z = 5
    rest_line_wls = [5007, 6563] * u.AA

    observed_spc = SpectralCoord(rest_line_wls * (z + 1), redshift=z)
    rest_spc = observed_spc.to_rest()
    # alternatively:
    # rest_spc = observed_spc.with_observer(observed_spec.target)
    # although then it would have to be clearly documented, or the `to_rest`
    # implemented in Spectrum1D?

    assert_quantity_allclose(rest_spc, rest_line_wls)

    # No frames are explicitly defined, so to the user, the observer and
    #  target are not set.
    with pytest.raises(AttributeError):
        assert_frame_allclose(rest_spc.observer, rest_spc.target)

    with pytest.raises(ValueError):
        # *any* observer shift should fail, since we didn't specify one at the
        # outset
        rest_spc._change_observer_to(
            ICRS(CartesianRepresentation([0, 0, 0] * u.au)))
def test_rv_redshift_initialization():

    # Check that setting the redshift sets the radial velocity appropriately,
    # and that the redshift can be recovered
    sc_init = SpectralCoord([4000, 5000] * u.AA, redshift=1)
    assert_quantity_allclose(sc_init.redshift, 1 * u.dimensionless_unscaled)
    assert_quantity_allclose(sc_init.radial_velocity, 0.6 * c)

    # Check that setting the same radial velocity produces the same redshift
    # and that the radial velocity can be recovered
    sc_init2 = SpectralCoord([4000, 5000] * u.AA, radial_velocity=0.6 * c)
    assert_quantity_allclose(sc_init2.redshift, 1 * u.dimensionless_unscaled)
    assert_quantity_allclose(sc_init2.radial_velocity, 0.6 * c)

    # Check that specifying redshift as a quantity works
    sc_init3 = SpectralCoord([4000, 5000] * u.AA, redshift=1 * u.one)
    assert sc_init.redshift == sc_init3.redshift

    # Make sure that both redshift and radial velocity can't be specified at
    # the same time.
    with pytest.raises(ValueError,
                       match='Cannot set both a radial velocity and redshift'):
        SpectralCoord([4000, 5000] * u.AA,
                      radial_velocity=10 * u.km / u.s,
                      redshift=2)
def test_spectral_coord_from_sky_coord_without_distance():
    # see https://github.com/astropy/specutils/issues/658 for issue context
    obs = SkyCoord(0 * u.m, 0 * u.m, 0 * u.m, representation_type='cartesian')
    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        coord = SpectralCoord([1, 2, 3] * u.micron, observer=obs)
    # coord.target = SkyCoord.from_name('m31')  # <- original issue, but below is the same but requires no remote data access
    with pytest.warns(AstropyUserWarning,
                      match='Distance on coordinate object is dimensionless'):
        coord.target = SkyCoord(ra=10.68470833 * u.deg, dec=41.26875 * u.deg)
def test_los_shift(sc_kwargs):
    wl = [4000, 5000] * u.AA
    with nullcontext(
    ) if 'observer' not in sc_kwargs and 'target' not in sc_kwargs else pytest.warns(
            AstropyUserWarning, match='No velocity defined on frame'):
        sc_init = SpectralCoord(wl, **sc_kwargs)

    # these should always work in *all* cases because it's unambiguous that
    # a target shift should behave this way
    new_sc1 = sc_init.with_radial_velocity_shift(.1)
    assert_quantity_allclose(new_sc1, wl * 1.1)
    new_sc2 = sc_init.with_radial_velocity_shift(
        .1 * u.dimensionless_unscaled)  # interpret at redshift
    assert_quantity_allclose(new_sc1, new_sc2)

    new_sc3 = sc_init.with_radial_velocity_shift(-100 * u.km / u.s)
    assert_quantity_allclose(new_sc3, wl * (1 + (-100 * u.km / u.s / c)))

    # now try the cases where observer is specified as well/instead
    if sc_init.observer is None or sc_init.target is None:
        with pytest.raises(ValueError):
            # both must be specified if you're going to mess with observer
            sc_init.with_radial_velocity_shift(observer_shift=.1)

    if sc_init.observer is not None and sc_init.target is not None:
        # redshifting the observer should *blushift* the LOS velocity since
        # its the observer-to-target vector that matters
        new_sc4 = sc_init.with_radial_velocity_shift(observer_shift=.1)
        assert_quantity_allclose(new_sc4, wl / 1.1)

        # an equal shift in both should produce no offset at all
        new_sc5 = sc_init.with_radial_velocity_shift(target_shift=.1,
                                                     observer_shift=.1)
        assert_quantity_allclose(new_sc5, wl)
Example #5
0
def test_spectralcoord_accuracy(specsys):

    # PyYAML is needed to read in the ecsv table
    pytest.importorskip('yaml')

    # This is a test to check the numerical results of transformations between
    # different velocity frames in SpectralCoord. This compares the velocity
    # shifts determined with SpectralCoord to those determined from the rv
    # package in Starlink.

    velocity_frame = EXPECTED_VELOCITY_FRAMES[specsys]

    reference_filename = get_pkg_data_filename('accuracy/data/rv.ecsv')
    reference_table = Table.read(reference_filename, format='ascii.ecsv')

    rest = 550 * u.nm

    with iers.conf.set_temp('auto_download', False):

        for row in reference_table:

            observer = EarthLocation.from_geodetic(
                -row['obslon'], row['obslat']).get_itrs(obstime=row['obstime'])

            with pytest.warns(AstropyUserWarning,
                              match='No velocity defined on frame'):
                sc_topo = SpectralCoord(545 * u.nm,
                                        observer=observer,
                                        target=row['target'])

            # FIXME: A warning is emitted for dates after MJD=57754.0 even
            # though the leap second table should be valid until the end of
            # 2020.
            with nullcontext() if row['obstime'].mjd < 57754 else pytest.warns(
                    AstropyWarning, match='Tried to get polar motions'):
                sc_final = sc_topo.with_observer_stationary_relative_to(
                    velocity_frame)

            delta_vel = (sc_topo.to(u.km / u.s,
                                    doppler_convention='relativistic',
                                    doppler_rest=rest) -
                         sc_final.to(u.km / u.s,
                                     doppler_convention='relativistic',
                                     doppler_rest=rest))

            if specsys == 'galactoc':
                assert_allclose(delta_vel.to_value(u.km / u.s),
                                row[specsys.lower()],
                                atol=30)
            else:
                assert_allclose(delta_vel.to_value(u.km / u.s),
                                row[specsys.lower()],
                                atol=0.02,
                                rtol=0.002)
def test_shift_to_rest_star_withobserver():
    rv = -8.3283011 * u.km / u.s
    rest_line_wls = [5007, 6563] * u.AA

    obstime = time.Time('2018-12-13 9:00')
    eloc = get_greenwich_earthlocation()
    obs = eloc.get_gcrs(obstime)  # always built-in, no download required
    acen = SkyCoord(ra=219.90085 * u.deg,
                    dec=-60.83562 * u.deg,
                    frame='icrs',
                    distance=4.37 * u.lightyear)
    # Note that above the rv is missing from the SkyCoord.
    # That's intended, as it will instead be set in the `SpectralCoord`.  But
    # the SpectralCoord machinery should yield something comparable to test_
    # spectral_coord_alphacen

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        observed_spc = SpectralCoord(rest_line_wls * (rv / c + 1),
                                     observer=obs,
                                     target=acen)

    rest_spc = observed_spc.to_rest()
    assert_quantity_allclose(rest_spc, rest_line_wls)

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        barycentric_spc = observed_spc._change_observer_to(
            ICRS(CartesianRepresentation([0, 0, 0] * u.au)))
    baryrest_spc = barycentric_spc.to_rest()
    assert quantity_allclose(baryrest_spc, rest_line_wls)

    # now make sure the change the barycentric shift did is comparable to the
    # offset rv_correction produces
    # barytarg = SkyCoord(barycentric_spc.target.frame)  # should be this but that doesn't work for unclear reasons
    barytarg = SkyCoord(barycentric_spc.target.data.without_differentials(),
                        frame=barycentric_spc.target.realize_frame(None))
    vcorr = barytarg.radial_velocity_correction(kind='barycentric',
                                                obstime=obstime,
                                                location=eloc)

    drv = baryrest_spc.radial_velocity - observed_spc.radial_velocity

    # note this probably will not work on the first try, but it's ok if this is
    # "good enough", where good enough is estimated below.  But that could be
    # adjusted if we think that's too agressive of a precision target for what
    # the machinery can handle
    # with pytest.raises(AssertionError):
    assert_quantity_allclose(vcorr, drv, atol=10 * u.m / u.s)
def test_relativistic_radial_velocity():

    # Test for when both observer and target have relativistic velocities.
    # This is not yet supported, so the test is xfailed for now.

    sc = SpectralCoord(500 * u.nm,
                       observer=ICRS(0 * u.km,
                                     0 * u.km,
                                     0 * u.km,
                                     -0.5 * c,
                                     -0.5 * c,
                                     -0.5 * c,
                                     representation_type='cartesian',
                                     differential_type='cartesian'),
                       target=ICRS(1 * u.kpc,
                                   1 * u.kpc,
                                   1 * u.kpc,
                                   0.5 * c,
                                   0.5 * c,
                                   0.5 * c,
                                   representation_type='cartesian',
                                   differential_type='cartesian'))

    assert_quantity_allclose(sc.radial_velocity,
                             0.989743318610787 * u.km / u.s)
def test_create_spectral_coord_observer_target(observer, target):

    with nullcontext() if target is None else pytest.warns(
            AstropyUserWarning, match='No velocity defined on frame'):
        coord = SpectralCoord([100, 200, 300] * u.nm,
                              observer=observer,
                              target=target)

    if observer is None:
        assert coord.observer is None
    else:
        assert_frame_allclose(observer, coord.observer)

    if target is None:
        assert coord.target is None
    else:
        assert_frame_allclose(target, coord.target)

    assert coord.doppler_rest is None
    assert coord.doppler_convention is None

    if observer is None or target is None:
        assert quantity_allclose(coord.redshift, 0)
        assert quantity_allclose(coord.radial_velocity, 0 * u.km / u.s)
    elif (any(observer is lsrd for lsrd in LSRD_EQUIV)
          and any(target is lsrd for lsrd in LSRD_DIR_STATIONARY_EQUIV)):
        assert_quantity_allclose(coord.radial_velocity,
                                 -274**0.5 * u.km / u.s,
                                 atol=1e-4 * u.km / u.s)
        assert_quantity_allclose(coord.redshift,
                                 -5.5213158163147646e-05,
                                 atol=1e-9)
    else:
        raise NotImplementedError()
def test_init_quantity():
    sc = SpectralCoord(10 * u.GHz)
    assert sc.value == 10.
    assert sc.unit is u.GHz
    assert sc.doppler_convention is None
    assert sc.doppler_rest is None
    assert sc.observer is None
    assert sc.target is None
def test_create_from_spectral_coord(observer, target):
    """
    Checks that parameters are correctly copied to the new SpectralCoord object
    """
    with nullcontext() if target is None else pytest.warns(
            AstropyUserWarning, match='No velocity defined on frame'):
        spec_coord1 = SpectralCoord([100, 200, 300] * u.nm,
                                    observer=observer,
                                    target=target,
                                    doppler_convention='optical',
                                    doppler_rest=6000 * u.AA)
    spec_coord2 = SpectralCoord(spec_coord1)
    assert spec_coord1.observer == spec_coord2.observer
    assert spec_coord1.target == spec_coord2.target
    assert spec_coord1.radial_velocity == spec_coord2.radial_velocity
    assert spec_coord1.doppler_convention == spec_coord2.doppler_convention
    assert spec_coord1.doppler_rest == spec_coord2.doppler_rest
def test_init_spectral_quantity():
    sc = SpectralCoord(
        SpectralQuantity(10 * u.GHz, doppler_convention='optical'))
    assert sc.value == 10.
    assert sc.unit is u.GHz
    assert sc.doppler_convention == 'optical'
    assert sc.doppler_rest is None
    assert sc.observer is None
    assert sc.target is None
Example #12
0
    def from_tree(cls, node, ctx):
        if isinstance(node, SpectralCoord):
            return node

        unit = UnitType.from_tree(node['unit'], ctx)
        value = node['value']
        observer = node['observer'] if 'observer' in node else None
        target = node['target'] if 'observer' in node else None
        if isinstance(value, NDArrayType):
            value = value._make_array()
        return SpectralCoord(value, unit=unit, observer=observer, target=target)
def test_init_too_many_args():

    with pytest.raises(
            ValueError,
            match='Cannot specify radial velocity or redshift if both'):
        SpectralCoord(10 * u.GHz,
                      observer=LSRD,
                      target=SkyCoord(10, 20, unit='deg'),
                      radial_velocity=1 * u.km / u.s)

    with pytest.raises(
            ValueError,
            match='Cannot specify radial velocity or redshift if both'):
        SpectralCoord(10 * u.GHz,
                      observer=LSRD,
                      target=SkyCoord(10, 20, unit='deg'),
                      redshift=1)

    with pytest.raises(ValueError,
                       match='Cannot set both a radial velocity and redshift'):
        SpectralCoord(10 * u.GHz, radial_velocity=1 * u.km / u.s, redshift=1)
def test_init_wrong_type():

    with pytest.raises(
            TypeError,
            match='observer must be a SkyCoord or coordinate frame instance'):
        SpectralCoord(10 * u.GHz, observer=3.4)

    with pytest.raises(
            TypeError,
            match='target must be a SkyCoord or coordinate frame instance'):
        SpectralCoord(10 * u.GHz, target=3.4)

    with pytest.raises(u.UnitsError,
                       match="Argument 'radial_velocity' to function "
                       "'__new__' must be in units convertible to 'km / s'"):
        SpectralCoord(10 * u.GHz, radial_velocity=1 * u.kg)

    with pytest.raises(TypeError,
                       match="Argument 'radial_velocity' to function "
                       "'__new__' has no 'unit' attribute. You should "
                       "pass in an astropy Quantity instead."):
        SpectralCoord(10 * u.GHz, radial_velocity='banana')

    with pytest.raises(u.UnitsError, match='redshift should be dimensionless'):
        SpectralCoord(10 * u.GHz, redshift=1 * u.m)

    with pytest.raises(
            TypeError,
            match=
            'Cannot parse "banana" as a Quantity. It does not start with a number.'
    ):
        SpectralCoord(10 * u.GHz, redshift='banana')
def test_spectral_coord_jupiter():
    """
    Checks radial velocity between Earth and Jupiter
    """
    obstime = time.Time('2018-12-13 9:00')
    obs = get_greenwich_earthlocation().get_gcrs(obstime)

    pos, vel = get_body_barycentric_posvel('jupiter', obstime)
    jupiter = SkyCoord(pos.with_differentials(CartesianDifferential(vel.xyz)),
                       obstime=obstime)

    spc = SpectralCoord([100, 200, 300] * u.nm, observer=obs, target=jupiter)

    # The velocity should be less than ~43 + a bit extra, which is the
    # maximum possible earth-jupiter relative velocity. We check the exact
    # value here (determined from SpectralCoord, so this serves as a test to
    # check that this value doesn't change - the value is not a ground truth)
    assert_quantity_allclose(spc.radial_velocity, -7.35219854 * u.km / u.s)
def test_spectral_coord_m31():
    """
    Checks radial velocity between Earth and M31
    """
    obstime = time.Time('2018-12-13 9:00')
    obs = get_greenwich_earthlocation().get_gcrs(obstime)

    # Coordinates were obtained from the following then hard-coded to avoid download
    # m31 = SkyCoord.from_name('M31')
    m31 = SkyCoord(ra=10.6847 * u.deg,
                   dec=41.269 * u.deg,
                   distance=710 * u.kpc,
                   radial_velocity=-300 * u.km / u.s)

    spc = SpectralCoord([100, 200, 300] * u.nm, observer=obs, target=m31)

    # The velocity should be less than ~300 + 30 + a bit extra in km/s, which
    # is the maximum possible relative velocity.  We check the exact values
    # here (determined from SpectralCoord, so this serves as a test to check
    # that this value doesn't change - the value is not a ground truth)
    assert_quantity_allclose(spc.radial_velocity, -279.755128 * u.km / u.s)
    assert_allclose(spc.redshift, -0.0009327276702120191)
def test_spectral_coord_alphacen():
    """
    Checks radial velocity between Earth and Alpha Centauri
    """
    obstime = time.Time('2018-12-13 9:00')
    obs = get_greenwich_earthlocation().get_gcrs(obstime)

    # Coordinates were obtained from the following then hard-coded to avoid download
    # acen = SkyCoord.from_name('alpha cen')
    acen = SkyCoord(ra=219.90085 * u.deg,
                    dec=-60.83562 * u.deg,
                    frame='icrs',
                    distance=4.37 * u.lightyear,
                    radial_velocity=-18. * u.km / u.s)

    spc = SpectralCoord([100, 200, 300] * u.nm, observer=obs, target=acen)

    # The velocity should be less than ~18 + 30 + a bit extra, which is the
    # maximum possible relative velocity.  We check the exact value here
    # (determined from SpectralCoord, so this serves as a test to check that
    # this value doesn't change - the value is not a ground truth)
    assert_quantity_allclose(spc.radial_velocity, -26.328301 * u.km / u.s)
def test_observer_init_rv_behavior():
    """
    Test basic initialization behavior or observer/target and redshift/rv
    """

    # Start off by specifying the radial velocity only
    sc_init = SpectralCoord([4000, 5000] * u.AA,
                            radial_velocity=100 * u.km / u.s)
    assert sc_init.observer is None
    assert sc_init.target is None
    assert_quantity_allclose(sc_init.radial_velocity, 100 * u.km / u.s)

    # Next, set the observer, and check that the radial velocity hasn't changed
    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc_init.observer = ICRS(
            CartesianRepresentation([0 * u.km, 0 * u.km, 0 * u.km]))
    assert sc_init.observer is not None
    assert_quantity_allclose(sc_init.radial_velocity, 100 * u.km / u.s)

    # Setting the target should now cause the original radial velocity to be
    # dropped in favor of the automatically computed one
    sc_init.target = SkyCoord(CartesianRepresentation(
        [1 * u.km, 0 * u.km, 0 * u.km]),
                              frame='icrs',
                              radial_velocity=30 * u.km / u.s)
    assert sc_init.target is not None
    assert_quantity_allclose(sc_init.radial_velocity, 30 * u.km / u.s)

    # The observer can only be set if originally None - now that it isn't
    # setting it again should fail
    with pytest.raises(ValueError, match='observer has already been set'):
        sc_init.observer = GCRS(
            CartesianRepresentation([0 * u.km, 1 * u.km, 0 * u.km]))

    # And similarly, changing the target should not be possible
    with pytest.raises(ValueError, match='target has already been set'):
        sc_init.target = GCRS(
            CartesianRepresentation([0 * u.km, 1 * u.km, 0 * u.km]))
def test_los_shift_radial_velocity():

    # Tests to make sure that with_radial_velocity_shift correctly calculates
    # the new radial velocity

    # First check case where observer and/or target aren't specified

    sc1 = SpectralCoord(500 * u.nm, radial_velocity=1 * u.km / u.s)

    sc2 = sc1.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc2.radial_velocity, 2 * u.km / u.s)

    sc3 = sc1.with_radial_velocity_shift(-3 * u.km / u.s)
    assert_quantity_allclose(sc3.radial_velocity, -2 * u.km / u.s)

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc4 = SpectralCoord(500 * u.nm,
                            radial_velocity=1 * u.km / u.s,
                            observer=gcrs_not_origin)

    sc5 = sc4.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc5.radial_velocity, 2 * u.km / u.s)

    sc6 = sc4.with_radial_velocity_shift(-3 * u.km / u.s)
    assert_quantity_allclose(sc6.radial_velocity, -2 * u.km / u.s)

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc7 = SpectralCoord(500 * u.nm,
                            radial_velocity=1 * u.km / u.s,
                            target=ICRS(10 * u.deg, 20 * u.deg))

    sc8 = sc7.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc8.radial_velocity, 2 * u.km / u.s)

    sc9 = sc7.with_radial_velocity_shift(-3 * u.km / u.s)
    assert_quantity_allclose(sc9.radial_velocity, -2 * u.km / u.s)

    # Check that things still work when both observer and target are specified

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc10 = SpectralCoord(500 * u.nm,
                             observer=ICRS(0 * u.deg,
                                           0 * u.deg,
                                           distance=1 * u.m),
                             target=ICRS(10 * u.deg,
                                         20 * u.deg,
                                         radial_velocity=1 * u.km / u.s,
                                         distance=10 * u.kpc))

    sc11 = sc10.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc11.radial_velocity, 2 * u.km / u.s)

    sc12 = sc10.with_radial_velocity_shift(-3 * u.km / u.s)
    assert_quantity_allclose(sc12.radial_velocity, -2 * u.km / u.s)
def test_asteroid_velocity_frame_shifts():
    """
    This test mocks up the use case of observing a spectrum of an asteroid
    at different times and from different observer locations.
    """
    time1 = time.Time('2018-12-13 9:00')
    dt = 12 * u.hour
    time2 = time1 + dt

    # make the silly but simplifying assumption that the astroid is moving along
    # the x-axis of GCRS, and makes a 10 earth-radius closest approach

    v_ast = [5, 0, 0] * u.km / u.s
    x1 = -v_ast[0] * dt / 2
    x2 = v_ast[0] * dt / 2
    z = 10 * u.Rearth

    cdiff = CartesianDifferential(v_ast)

    asteroid_loc1 = GCRS(CartesianRepresentation(x1.to(u.km),
                                                 0 * u.km,
                                                 z.to(u.km),
                                                 differentials=cdiff),
                         obstime=time1)
    asteroid_loc2 = GCRS(CartesianRepresentation(x2.to(u.km),
                                                 0 * u.km,
                                                 z.to(u.km),
                                                 differentials=cdiff),
                         obstime=time2)

    # assume satellites that are essentially fixed in geostationary orbit on
    # opposite sides of the earth
    observer1 = GCRS(CartesianRepresentation(
        [0 * u.km, 35000 * u.km, 0 * u.km]),
                     obstime=time1)
    observer2 = GCRS(CartesianRepresentation(
        [0 * u.km, -35000 * u.km, 0 * u.km]),
                     obstime=time2)

    wls = np.linspace(4000, 7000, 100) * u.AA
    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        spec_coord1 = SpectralCoord(wls,
                                    observer=observer1,
                                    target=asteroid_loc1)

    assert spec_coord1.radial_velocity < 0 * u.km / u.s
    assert spec_coord1.radial_velocity > -5 * u.km / u.s

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        spec_coord2 = SpectralCoord(wls,
                                    observer=observer2,
                                    target=asteroid_loc2)

    assert spec_coord2.radial_velocity > 0 * u.km / u.s
    assert spec_coord2.radial_velocity < 5 * u.km / u.s

    # now check the behavior of with_observer_stationary_relative_to: we shift each coord
    # into the velocity frame of its *own* target.  That would then be a
    # spectralcoord that would allow direct physical comparison of the two
    # different spec_corrds.  There's no way to test that, without
    # actual data, though.

    # spec_coord2 is redshifted, so we test that it behaves the way "shifting
    # to rest frame" should - the as-observed spectral coordinate should become
    # the rest frame, so something that starts out red should become bluer
    target_sc2 = spec_coord2.with_observer_stationary_relative_to(
        spec_coord2.target)
    assert np.all(target_sc2 < spec_coord2)
    # rv/redshift should be 0 since the observer and target velocities should
    # be the same
    assert_quantity_allclose(target_sc2.radial_velocity,
                             0 * u.km / u.s,
                             atol=1e-7 * u.km / u.s)

    # check that the same holds for spec_coord1, but be more specific: it
    # should follow the standard redshift formula (which in this case yields
    # a blueshift, although the formula is the same as 1+z)
    target_sc1 = spec_coord1.with_observer_stationary_relative_to(
        spec_coord1.target)
    assert_quantity_allclose(target_sc1,
                             spec_coord1 / (1 + spec_coord1.redshift))
def test_los_shift_radial_velocity():

    # Tests to make sure that with_radial_velocity_shift correctly calculates
    # the new radial velocity

    # First check case where observer and/or target aren't specified

    sc1 = SpectralCoord(500 * u.nm, radial_velocity=1 * u.km / u.s)

    sc2 = sc1.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc2.radial_velocity, 2 * u.km / u.s)

    sc3 = sc1.with_radial_velocity_shift(-3 * u.km / u.s)
    assert_quantity_allclose(sc3.radial_velocity, -2 * u.km / u.s)

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc4 = SpectralCoord(500 * u.nm,
                            radial_velocity=1 * u.km / u.s,
                            observer=gcrs_not_origin)

    sc5 = sc4.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc5.radial_velocity, 2 * u.km / u.s)

    sc6 = sc4.with_radial_velocity_shift(-3 * u.km / u.s)
    assert_quantity_allclose(sc6.radial_velocity, -2 * u.km / u.s)

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc7 = SpectralCoord(500 * u.nm,
                            radial_velocity=1 * u.km / u.s,
                            target=ICRS(10 * u.deg, 20 * u.deg))

    sc8 = sc7.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc8.radial_velocity, 2 * u.km / u.s)

    sc9 = sc7.with_radial_velocity_shift(-3 * u.km / u.s)
    assert_quantity_allclose(sc9.radial_velocity, -2 * u.km / u.s)

    # Check that things still work when both observer and target are specified

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc10 = SpectralCoord(500 * u.nm,
                             observer=ICRS(0 * u.deg,
                                           0 * u.deg,
                                           distance=1 * u.m),
                             target=ICRS(10 * u.deg,
                                         20 * u.deg,
                                         radial_velocity=1 * u.km / u.s,
                                         distance=10 * u.kpc))

    sc11 = sc10.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc11.radial_velocity, 2 * u.km / u.s)

    sc12 = sc10.with_radial_velocity_shift(-3 * u.km / u.s)
    assert_quantity_allclose(sc12.radial_velocity, -2 * u.km / u.s)

    # Check that things work if radial_velocity wasn't specified at all

    sc13 = SpectralCoord(500 * u.nm)
    sc14 = sc13.with_radial_velocity_shift(1 * u.km / u.s)
    assert_quantity_allclose(sc14.radial_velocity, 1 * u.km / u.s)

    sc15 = sc1.with_radial_velocity_shift()
    assert_quantity_allclose(sc15.radial_velocity, 1 * u.km / u.s)

    # Check that units are verified

    with pytest.raises(u.UnitsError,
                       match="Argument must have unit physical "
                       "type 'speed' for radial velocty or "
                       "'dimensionless' for redshift."):
        sc1.with_radial_velocity_shift(target_shift=1 * u.kg)
def test_with_observer_stationary_relative_to():

    # Simple tests of with_observer_stationary_relative_to to cover different
    # ways of calling it

    # The replicate method makes a new object with attributes updated, but doesn't
    # do any conversion

    sc1 = SpectralCoord([4000, 5000] * u.AA)
    with pytest.raises(ValueError,
                       match='This method can only be used if both '
                       'observer and target are defined on the '
                       'SpectralCoord'):
        sc1.with_observer_stationary_relative_to('icrs')

    sc2 = SpectralCoord([4000, 5000] * u.AA,
                        observer=ICRS(0 * u.km,
                                      0 * u.km,
                                      0 * u.km,
                                      -1 * u.km / u.s,
                                      0 * u.km / u.s,
                                      -1 * u.km / u.s,
                                      representation_type='cartesian',
                                      differential_type='cartesian'),
                        target=ICRS(0 * u.deg,
                                    45 * u.deg,
                                    distance=1 * u.kpc,
                                    radial_velocity=2 * u.km / u.s))

    # Motion of observer is in opposite direction to target
    assert_quantity_allclose(sc2.radial_velocity, (2 + 2**0.5) * u.km / u.s)

    # Change to observer that is stationary in ICRS
    sc3 = sc2.with_observer_stationary_relative_to('icrs')

    # Velocity difference is now pure radial velocity of target
    assert_quantity_allclose(sc3.radial_velocity, 2 * u.km / u.s)

    # Check setting the velocity in with_observer_stationary_relative_to
    sc4 = sc2.with_observer_stationary_relative_to(
        'icrs', velocity=[-2**0.5, 0, -2**0.5] * u.km / u.s)

    # Observer once again moving away from target but faster
    assert_quantity_allclose(sc4.radial_velocity, 4 * u.km / u.s)

    # Check that we can also pass frame classes instead of names

    sc5 = sc2.with_observer_stationary_relative_to(
        ICRS, velocity=[-2**0.5, 0, -2**0.5] * u.km / u.s)
    assert_quantity_allclose(sc5.radial_velocity, 4 * u.km / u.s)

    # And make sure we can also pass instances of classes without data

    sc6 = sc2.with_observer_stationary_relative_to(
        ICRS(), velocity=[-2**0.5, 0, -2**0.5] * u.km / u.s)
    assert_quantity_allclose(sc6.radial_velocity, 4 * u.km / u.s)

    # And with data provided no velocities are present

    sc7 = sc2.with_observer_stationary_relative_to(
        ICRS(0 * u.km, 0 * u.km, 0 * u.km, representation_type='cartesian'),
        velocity=[-2**0.5, 0, -2**0.5] * u.km / u.s)
    assert_quantity_allclose(sc7.radial_velocity, 4 * u.km / u.s)

    # And also have the ability to pass frames with velocities already defined

    sc8 = sc2.with_observer_stationary_relative_to(
        ICRS(0 * u.km,
             0 * u.km,
             0 * u.km,
             2**0.5 * u.km / u.s,
             0 * u.km / u.s,
             2**0.5 * u.km / u.s,
             representation_type='cartesian',
             differential_type='cartesian'))
    assert_quantity_allclose(sc8.radial_velocity,
                             0 * u.km / u.s,
                             atol=1e-10 * u.km / u.s)

    # Make sure that things work properly if passing a SkyCoord

    sc9 = sc2.with_observer_stationary_relative_to(
        SkyCoord(
            ICRS(0 * u.km, 0 * u.km, 0 * u.km,
                 representation_type='cartesian')),
        velocity=[-2**0.5, 0, -2**0.5] * u.km / u.s)
    assert_quantity_allclose(sc9.radial_velocity, 4 * u.km / u.s)

    sc10 = sc2.with_observer_stationary_relative_to(
        SkyCoord(
            ICRS(0 * u.km,
                 0 * u.km,
                 0 * u.km,
                 2**0.5 * u.km / u.s,
                 0 * u.km / u.s,
                 2**0.5 * u.km / u.s,
                 representation_type='cartesian',
                 differential_type='cartesian')))
    assert_quantity_allclose(sc10.radial_velocity,
                             0 * u.km / u.s,
                             atol=1e-10 * u.km / u.s)

    # But we shouldn't be able to pass both a frame with velocities, and explicit velocities

    with pytest.raises(
            ValueError,
            match=
            'frame already has differentials, cannot also specify velocity'):
        sc2.with_observer_stationary_relative_to(
            ICRS(0 * u.km,
                 0 * u.km,
                 0 * u.km,
                 2**0.5 * u.km / u.s,
                 0 * u.km / u.s,
                 2**0.5 * u.km / u.s,
                 representation_type='cartesian',
                 differential_type='cartesian'),
            velocity=[-2**0.5, 0, -2**0.5] * u.km / u.s)

    # And velocities should have three elements

    with pytest.raises(
            ValueError,
            match='velocity should be a Quantity vector with 3 elements'):
        sc2.with_observer_stationary_relative_to(
            ICRS, velocity=[-2**0.5, 0, -2**0.5, -3] * u.km / u.s)

    # Make sure things don't change depending on what frame class is used for reference
    sc11 = sc2.with_observer_stationary_relative_to(
        SkyCoord(
            ICRS(0 * u.km,
                 0 * u.km,
                 0 * u.km,
                 2**0.5 * u.km / u.s,
                 0 * u.km / u.s,
                 2**0.5 * u.km / u.s,
                 representation_type='cartesian',
                 differential_type='cartesian')).transform_to(Galactic))
    assert_quantity_allclose(sc11.radial_velocity,
                             0 * u.km / u.s,
                             atol=1e-10 * u.km / u.s)

    # Check that it is possible to preserve the observer frame
    sc12 = sc2.with_observer_stationary_relative_to(LSRD)
    sc13 = sc2.with_observer_stationary_relative_to(
        LSRD, preserve_observer_frame=True)

    assert isinstance(sc12.observer, Galactic)
    assert isinstance(sc13.observer, ICRS)
def test_replicate():

    # The replicate method makes a new object with attributes updated, but doesn't
    # do any conversion

    sc_init = SpectralCoord([4000, 5000] * u.AA, redshift=2)

    sc_set_rv = sc_init.replicate(redshift=1)
    assert_quantity_allclose(sc_set_rv.radial_velocity, 0.6 * c)
    assert_quantity_allclose(sc_init, [4000, 5000] * u.AA)

    sc_set_rv = sc_init.replicate(radial_velocity=c / 2)
    assert_quantity_allclose(sc_set_rv.redshift, np.sqrt(3) - 1)
    assert_quantity_allclose(sc_init, [4000, 5000] * u.AA)

    gcrs_origin = GCRS(CartesianRepresentation([0 * u.km, 0 * u.km, 0 * u.km]))
    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc_init2 = SpectralCoord([4000, 5000] * u.AA,
                                 redshift=1,
                                 observer=gcrs_origin)
    with np.errstate(all='ignore'):
        sc_init2.replicate(redshift=.5)
    assert_quantity_allclose(sc_init2, [4000, 5000] * u.AA)

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc_init3 = SpectralCoord([4000, 5000] * u.AA,
                                 redshift=1,
                                 target=gcrs_origin)
    with np.errstate(all='ignore'):
        sc_init3.replicate(redshift=.5)
    assert_quantity_allclose(sc_init2, [4000, 5000] * u.AA)

    with pytest.warns(AstropyUserWarning,
                      match='No velocity defined on frame'):
        sc_init4 = SpectralCoord([4000, 5000] * u.AA,
                                 observer=gcrs_origin,
                                 target=gcrs_origin)
    with pytest.raises(
            ValueError,
            match=
            'Cannot specify radial velocity or redshift if both target and observer are specified'
    ):
        sc_init4.replicate(redshift=.5)

    sc_init = SpectralCoord([4000, 5000] * u.AA, redshift=2)
    sc_init_copy = sc_init.replicate(copy=True)
    sc_init[0] = 6000 * u.AA
    assert_quantity_allclose(sc_init_copy, [4000, 5000] * u.AA)

    sc_init = SpectralCoord([4000, 5000] * u.AA, redshift=2)
    sc_init_ref = sc_init.replicate()
    sc_init[0] = 6000 * u.AA
    assert_quantity_allclose(sc_init_ref, [6000, 5000] * u.AA)