Ejemplo n.º 1
0
    def blackbody_radiance(self, T, spectral=True):
        """Calculate integrated radiance for blackbody at temperature T

        :param T: Temperature [K].  This can be either a python number, or
            a numpy ndarray, on a ureg quantity encompassing either.
        :param spectral: Parameter to control whether to return spectral
            radiance or radiance.  See self.integrate_radiances for
            details.

        Returns quantity ndarray with blackbody radiance in desired unit.
        Note that this is an ndarray with dimension (1,) even if you
        passin a scalar.
        """
        try:
            T = T.to("K")
        except AttributeError:
            T = ureg.Quantity(T, "K")
        T = ureg.Quantity(numpy.atleast_1d(T), T.u)
        # fails if T is multidimensional
        shp = T.shape
        return self.integrate_radiances(self.frequency,
                                        planck_f(
                                            self.frequency[numpy.newaxis, :],
                                            T.reshape((-1, ))[:,
                                                              numpy.newaxis]),
                                        spectral=spectral).reshape(shp)
Ejemplo n.º 2
0
def _S_radsi_to_K(S, u_radK):
    """Convert covariance matrix in radiance to one in BT

    This is done by first converting covariance matrices to correlation
    matrices using one set of uncertainties, then back with another.

    Parameters
    ----------

    S : xarray.DataArray
        Covariance matrix in rad_si units
    u_radK : xarray.DataArray
        Uncertainties in brightness temperature in K.

    Returns
    -------

    ureg.Quantity
        Covariance matrix in brightness temperature

    """
    S_L = ureg.Quantity(S.values, rad_u["si"]**2)
    uRe = ureg.Quantity(numpy.sqrt(numpy.diag(S_L)), rad_u["si"])
    R = S_L / (uRe[:, numpy.newaxis] * uRe[numpy.newaxis, :])
    S_BT = R * (u_radK[:, numpy.newaxis] * u_radK[numpy.newaxis, :])
    return S_BT
Ejemplo n.º 3
0
    def integrate_radiances(self, f, L, spectral=True):
        """From a spectrum of radiances and a SRF, calculate channel (spectral) radiance

        The spectral response function may not be specified on the same grid
        as the spectrum of radiances.  Therefore, this function interpolates
        the spectral response function onto the grid of the radiances.  This
        is less bad than the reverse, because a spectral response function
        tends to be more smooth than a spectrum.

        **Approximations:**

        * Interpolation of spectral response function onto frequency grid on
          which radiances are specified.

        :param ndarray f: Frequencies for spectral radiances [Hz]
        :param ndarray L: Spectral radiances [W m^-2 sr^-1 Hz^-1].
            Can be in radiance units of various kinds.  Make sure this is
            consistent with the spectral response function.
            Innermost dimension must correspond to frequencies.
        :param bool spectral: If true, return spectral radiance
            [W m^-2 sr^-1 Hz^-1].  If false, return radiance [W m^-2
            sr^-1].  Defaults to True.
        :returns: Channel (spectral) radiance according to 'spectral'
        """
        # Interpolate onto common frequency grid.  The spectral response
        # function is more smooth so less harmed by interpolation, so I
        # interpolate the SRF.
        fnc = scipy.interpolate.interp1d(self.frequency,
                                         self.W,
                                         bounds_error=False,
                                         fill_value=0.0)
        #w_on_L_grid = fnc(f) * (1 / ureg.Hz)
        w_on_L_grid = ureg.Quantity(fnc(f),
                                    ureg.dimensionless)  # * (1 / ureg.Hz)

        df = ureg.Quantity(numpy.diff(f), f.u)
        w1p = w_on_L_grid[1:]
        L1p = L[:, 1:]
        # ch_BT = (w_on_L_grid * L_f).sum(-1) / (w_on_L_grid.sum())
        # due to numexpr limitation, do sum seperately
        # and due to numexpr bug, explicitly consider zero-dim case
        #     see https://github.com/pydata/numexpr/issues/229
        if L.shape[0] == 0:
            ch_rad = numpy.empty(dtype="f8", shape=L.shape[:-1])
        else:
            #            ch_rad = numexpr.evaluate("sum(w_on_L_grid * L, {:d})".format(
            ch_rad = numexpr.evaluate(
                "sum(w1p * L1p * df, {:d})".format(L.ndim - 1))
        ch_rad = ureg.Quantity(ch_rad, w1p.u * L1p.u * df.u)
        if spectral:
            return ch_rad / (w1p * df).sum()
        else:
            return ch_rad
Ejemplo n.º 4
0
    def _add_harm_for_iasi(self, harm, channel, ok):
        # fill X1

        # self.ds["ref_radiance"] contains IASI radiances; need to use
        # this to simulate HIRS radiance for MetOp-A
        freq = ureg.Quantity(numpy.loadtxt(self.hiasi.freqfile), ureg.Hz)
        specrad_wn = UADA(self.ds.isel(line=ok)["ref_radiance"])
        specrad_f = specrad_wn.to(rad_u["si"], "radiance")
        srf = typhon.physics.units.em.SRF.fromRTTOV("metop_2", "hirs", channel)
        L = srf.integrate_radiances(
            freq, ureg.Quantity(specrad_f.values, specrad_f.attrs["units"]))
        harm["X1"] = (("M", "m1"), L[:, numpy.newaxis].astype("f4"))

        # fill Ur1.  Uncertainties in refeence not considered.
        # Arbitrarily put at 1‰.

        harm["Ur1"] = (("M", "m1"), (harm["X1"] * 0.001).astype("f4"))

        # fill Us1.

        harm["Us1"] = (("M", "m1"), numpy.zeros((harm.dims["M"], 1),
                                                dtype="f4"))

        # fill uncertainty_type1

        harm["uncertainty_type1"] = (("m1", ), numpy.array([1], dtype="i4"))

        # fill time1

        harm["time1"] = (("M", ), self.ds.isel(line=ok)["mon_time"])

        # and w-matrix stuff

        harm["w_matrix_use1"] = (("m1", ), numpy.array([0], dtype="i4"))
        harm["u_matrix_use1"] = (("m1", ), numpy.array([0], dtype="i4"))

        # add diagnostics
        #        harm[f"nominal_measurand2"] = (("M",),
        #            self.ds["mon_radiance"].sel(ch_mon=channel))

        harm[f"lon1"] = (("M", ), self.ds[f"ref_longitude"][ok])
        harm[f"lat1"] = (("M", ), self.ds[f"ref_latitude"][ok])

        #        harm[f"nominal_measurand_original1"] = harm[f"nominal_measurand1"]

        harm[f"column1"] = (("M", ), self.ds[f"ref_column"][ok])
        harm[f"row1"] = (("M", ), self.ds[f"ref_row"][ok])

        harm[f"matchup_distance"] = ((), 0)
Ejemplo n.º 5
0
def specrad_frequency_to_planck_bt(L, f):
    """Convert spectral radiance per frequency to brightness temperature

    This function converts monochromatic spectral radiance per frequency
    to Planck brightness temperature.  This is calculated by inverting the
    Planck function.

    Note that this function is NOT correct to estimate polychromatic
    brightness temperatures such as channel brightness temperatures.  For
    this, you need the spectral response function — see the SRF class.

    :param L: Spectral radiance [W m^-2 sr^-1 Hz^-1]
    :param f: Corresponding frequency [Hz]
    :returns: Planck brightness temperature [K].
    """

    # f needs to be double to prevent overflow
    f = numpy.asarray(f).astype(numpy.float64)
    if L.size > 1500000:
        logger.debug("Doing actual BT conversion: {:,} spectra * {:,} "
                     "frequencies = {:,} radiances".format(
                         L.size // L.shape[-1], f.size, L.size))
    # BT = (h * f) / (k * numpy.log((2*h*f**3)/(L * c**2) + 1))
    BT = numexpr.evaluate("(h * f) / (k * log((2*h*f**3)/(L * c**2) + 1))")
    BT = numpy.ma.masked_invalid(BT)
    if L.size > 1500000:
        logger.debug("(done)")
    return ureg.Quantity(BT, ureg.K)
Ejemplo n.º 6
0
    def fromRTTOV(cls, sat, instr, ch):
        """Read SRF from RTTOV format files.

        Requires that in the TYPHONRC configuration file, the fields
        'srf_rttov' in the section corresponding to instrument `instr` are
        defined to point to the respective files in RTTOV format.  Within
        those definitions, {sat:s} will be substituted with the satellite
        name and {ch:>02d} with the channel number.  For example, in
        typhonrc, we might have:

        [hirs]
        srf_rttov = /path/to/rtcoef_{sat:s}_hirs-shifted_src_ch{ch:>02d}.txt

        Arguments:

            sat [str]

                Satellite name, such as 'noaa-15'

            instr [str]

                Instrument name, such as 'hirs'.

            ch [int]

                Channel number (start counting at 1).
        """
        cf = config.conf[instr]
        M = numpy.loadtxt(cf["srf_rttov"].format(sat=sat, ch=ch), skiprows=4)
        wn = ureg.Quantity(M[:, 0], 1 / ureg.cm)
        W = M[:, 1]
        return cls(wn, W)
Ejemplo n.º 7
0
    def __init__(self, f, W):
        """Initialise SRF object.

        You can either initialise an SRF from scratch, or use the
        classmethod `fromArtsXML` to read it from a file.

        A toy example on initiating it from scratch:

        >>> from typhon.physics.units.common import ureg
        >>> from typhon.physics.units.em import SRF
        >>> srf = SRF(ureg.Quantity(numpy.array([200, 200.1, 200.2, 200.3, 200.4, 200.5]), 'GHz'), numpy.array([0, 0.5, 1, 1, 0.5, 0]))
        >>> R_300 = srf.blackbody_radiance(ureg.Quantity(300, 'K'))
        >>> print(R_300)
        [  3.63716781e-15] watt / hertz / meter ** 2 / steradian
        >>> print(R_300.to("K", "radiance", srf=srf))
        [ 300.] kelvin

        You can also pass in other spectroscopic units (wavenumber,
        wavelength) that will be converted internally to frequency:

        >>> srf = SRF(ureg.Quantity(numpy.array([10.8, 10.9, 11.0, 11.1, 11.2, 11.3]), 'um'), numpy.array([0, 0.5, 1, 1, 0.5, 0]))
        >>> R_300 = srf.blackbody_radiance(ureg.Quantity(numpy.atleast_1d(250), 'K'))
        >>> print(R_300)
        [  1.61922509e-12] watt / hertz / meter ** 2 / steradian
        >>> print(R_300.to("cm * mW / m**2 / sr", "radiance"))
        [ 48.54314703] centimeter * milliwatt / meter ** 2 / steradian
        >>> print(R_300.to("K", "radiance", srf=srf))
        [ 300.] kelvin

        :param ndarray f: Array of frequencies.  Can be either a pure
            ndarray, which will be assumed to be in Hz, or a ureg
            quantity.
        :param ndarray W: Array of associated weights.
        """

        try:
            self.frequency = f.to("Hz", "sp")
        except AttributeError:
            self.frequency = ureg.Quantity(f, "Hz")
        self.W = W
Ejemplo n.º 8
0
def calc_y_for_srf_shift(Δλ,
                         y_master,
                         srf0,
                         L_spectral_db,
                         f_spectra,
                         y_ref,
                         unit=ureg.um,
                         regression_type=sklearn.linear_model.LinearRegression,
                         regression_args={"fit_intercept": True},
                         predict_quantity="bt",
                         u_y_ref=None,
                         u_y_target=None):
    """Calculate radiances or BTs estimating y_target from y_master assuming srf0 shifts by Δλ

    Try to estimate y_target from y_master, assuming that y_master are
    bts or radiances corresponding to spectral response function srf0.
    For the estimate, use a database described by L_spectral_db and
    f_spectra.  This database will be used to train a regression from
    the original bt (y_master, due to srf0) to a shifted bt (due to
    srf0 shifted by Δλ).  The regression is trained using the spectral
    database (L_spectral_db, f_spectra, bt_ref).  This function
    then applies the regression to y_master.

    This function is designed to be called by
    :func:`calc_cost_for_srf_shift`, which in turn is designed
    to be called repeatedly within an optimisation framework (see
    :func:`estimate_srf_shift`).  Therefore, as much as possible is
    precalculated before calling this.  Hence, you also need to pass
    bt_ref, which equals integrated brightness temperatures for
    the reference satellite, corresponding to f_spectra and L_spectral_db.

    This estimate considers one channel at a time.  It may be more optimal
    to estimate multiple channels at a time.  This is to be done.

    Parameters
    ----------
        
    Δλ : Quantity or float
        shift in SRF.  Will be converted to the
        unit (typically µm or nm) from the pint user registry (see
        later argument).  Scalar.
    y_master : (N, k) Quantity ndarray
        Brightness temperatures [K] or
        radiances [radiance units] for reference satellite.  N
        samples, k channels.  Quantity must be consistent with the one
        described by predict_quantity.  These are used in the actual
        prediction; the training is performed with y_ref.  For
        example, if we are predicting NOAA18-8 from NOAA19-1-12 in a
        test setting, y_master would correspond to NOAA19-1-12 test
        data, and y_ref would be the same for training data.  In the
        real world, y_ref is still simulated, but y_master are actual
        measurements.
    srf0 : typhon.physics.units.em.SRF
        SRF relative to which
        the shift is to be calculated.
    L_spectral_db (M, l) ndarray
        Database of spectra (such as from IASI)
        to use.  Should be in spectral radiance per frequency units [W
        / (m^2 sr Hz)].  M spectra with l radiances each.
    f_spectra : (l, ) Quantity ndarray
        frequencies corresponding to L_spectral_db [Hz].  1-D with length l.
    y_ref : (M, k) Quantity ndarray :
        Predictands used to train regressions,
        i.e. the training database.  This information follows directly from
        L_spectral_db and SRFs on the reference satellite, but it is
        an expensive calculation so should be pre-calculated.  If
        predicting from the same satellite, at least one channel will
        correspond to srf0, such that::

            # in case of radiances
            L = srf0.integrate_radiances(f_spectra, L_spectral_db)
            # in case of BTs
            bt = srf0.channel_radiance2bt(L)

        but this is not the case if one satellite is predicted from
        another.
    unit : Unit
        unit from pint unit registry.  Defaults to ureg.um.
    regression_type : scikit-learn regressor
        Type of regression.
        Defaults to sklearn.linear_model.LinearRegression.  Other good
        option would be sklearn.cross_decomposition.PLSRegression.
        As long as regression_type(\*\*regression_args) behaves like
        those two (with .fit and .predict), it should be OK.
    regression_args : dict
        Keyword arguments to pass on to regressor.
        For example, for sklearn.linear_model.LinearRegression you
        would want to at least pass ``{"fit_intercept": True}``.  For
        sklearn.cross_decomposition.PLSRegression you might use
        ``{"n_components": 9, "scale": False}``.  Please refer to
        scikit-learn documentation.

    Returns
    -------

    y: ndarray
        estimates for shifted y_master or y_master values

    """
    try:
        Δλ = Δλ.to(unit)
    except AttributeError:
        Δλ = ureg.Quantity(Δλ, unit)
    srf_sh = srf0.shift(Δλ)
    L_target = srf_sh.integrate_radiances(f_spectra, L_spectral_db)
    if predict_quantity == "bt":
        y_target = srf_sh.channel_radiance2bt(L_target)
    elif predict_quantity == "radiance":
        y_target = L_target
    else:
        raise ValueError(
            "Invalid predict_quantity: {:s}".format(predict_quantity))

    # sklearn wants 2-D inputs even when there is only a single predictand
    # atleast_2d makes it (1, N), I need (N, 1), but transposing the
    # result of atleast_2d would be wrong if the array was already 2D to
    # begin with
    if y_master.ndim == 1:
        y_master = y_master[:, numpy.newaxis]

    if y_ref.ndim == 1:
        y_ref = y_ref[:, numpy.newaxis]

    #clf = sklearn.linear_model.LinearRegression(fit_intercept=True)
    if issubclass(regression_type, sklearn.base.RegressorMixin):
        clf = regression_type(**regression_args)
        clf.fit(y_ref.m, y_target.m)
        return ureg.Quantity(clf.predict(y_master.m).squeeze(), y_master.u)
    elif issubclass(regression_type, scipy.odr.odrpack.ODR):
        try:
            sx = u_y_ref.to(y_ref.u, "radiance").m.squeeze()
            sy = u_y_target.to(u_y_target.u, "radiance").m.squeeze()
        except pint.DimensionalityError:  # probably noise in other unit
            # here, those are really only used as weights, so same-unit
            # is not essential?  Otherwise I need to convert them, a
            # non-trivial step.
            sx = u_y_ref.m.squeeze()
            sy = u_y_target.m.squeeze()

        # meaningful initial guess: new value equal to old channel, rest 0
        # β0 has a offset and then one slope for each channel
        # don't set to zero, says odrpack guide section 1.E.
        β0 = numpy.zeros(shape=(y_ref.shape[1] + 1, )) + 0.001
        samech = abs((y_target[:, numpy.newaxis] - y_ref)).mean(0).argmin()
        β0[samech + 1] = 1
        if (sx == 0).any() or (sy == 0).any():
            mydata = scipy.odr.RealData(y_ref.m.T, y_target.m)
        else:
            mydata = scipy.odr.RealData(y_ref.m.T, y_target.m, sx=sx, sy=sy)
        myodr = scipy.odr.ODR(mydata, scipy.odr.multilinear, beta0=β0)
        myout = myodr.run()
        if not any(
                x in myout.stopreason for x in {
                    "Sum of squares convergence", "Iteration limit reached",
                    "Parameter convergence",
                    "Both sum of squares and parameter convergence"
                }):
            raise ODRFitError("ODR fitting did not converge.  "
                              "Stop reason: {:s}".format(myout.stopreason[0]))
        return ureg.Quantity(myout.beta[0], y_master.u) + (
            myout.beta[numpy.newaxis, 1:] * y_master).sum(1)
    else:
        raise ValueError("Unknown type of regression!")
Ejemplo n.º 9
0
def plot_rself_test(h, ds, temperatures, channels, regr_type, regr_args, tit,
                    fn):

    model = models.RSelfTemperature(h, temperatures, (regr_type, regr_args))

    #view_space = M[h.scantype_fieldname] == h.typ_space

    #M = M[view_space]

    N = len(channels)

    (ncol, nrow) = typhon.plots.get_subplot_arrangement(N)

    (f, ax_all) = matplotlib.pyplot.subplots(nrow,
                                             ncol,
                                             sharex=False,
                                             figsize=(2 + 3 * ncol,
                                                      3 + 2.5 * nrow),
                                             squeeze=False)
    for (a, c) in zip(ax_all.ravel(), channels):
        model.fit(ds, c)
        (X, Y_ref, Y_pred) = model.test(ds, c)
        xloc = Y_ref.to(rad_u["ir"], "radiance")
        yloc = (Y_pred - Y_ref).to(rad_u["ir"], "radiance")
        rng = [
            scipy.stats.scoreatpercentile(xloc[~xloc.isnull()], [1, 99]),
            scipy.stats.scoreatpercentile(yloc[~xloc.isnull()], [1, 99])
        ]
        unmasked = (~xloc.isnull()) & (~yloc.isnull())
        a.hexbin(xloc[unmasked],
                 yloc[unmasked],
                 cmap="viridis",
                 mincnt=1,
                 gridsize=20,
                 extent=[rng[0][0], rng[0][1], rng[1][0], rng[1][1]])
        #        a.hist2d(xloc, yloc,
        #                cmap="viridis", cmin=1,
        #                bins=20, range=rng)
        typhon.plots.plot_distribution_as_percentiles(
            a,
            xloc[unmasked],
            yloc[unmasked],
            nbins=20,
            color="tan",
            ptiles=[5, 25, 50, 75, 95],
            linestyles=[":", "--", "-", "--", ":"])
        rmse = numpy.sqrt(((Y_pred - Y_ref).to(rad_u["ir"],
                                               "radiance")**2).mean())
        rmse = ureg.Quantity(rmse.values, rmse.attrs["units"])
        a.annotate(f"RMSE: {rmse.round(2):~}",
                   xy=(.99, .99),
                   xycoords='axes fraction',
                   horizontalalignment='right',
                   verticalalignment="top")
        a.set_title("Ch. {:d}".format(c))
        a.grid(axis="both", color="white")
        #a.set_aspect("equal", "box", "C")
        a.plot(rng, [0, 0], 'k-', linewidth=0.5)
        if a in ax_all[:, 0]:
            a.set_ylabel("Calib. offset (Estimate-Reference)\n[{:~}]".format(
                rad_u["ir"]))
        if a in ax_all[-1, :]:
            a.set_xlabel("Calib. offset (Reference)\n[{:~}]".format(
                rad_u["ir"]))
        for ax in (a.xaxis, a.yaxis):
            ax.set_major_locator(
                matplotlib.ticker.MaxNLocator(nbins=4, prune=None))


#    for a in ax_all.ravel()[:len(channels)]:
        a.set_xlim(rng[0])
        a.set_ylim(rng[1])
    for a in ax_all.ravel()[len(channels):]:
        a.set_visible(False)

    f.suptitle(tit)
    f.subplots_adjust(hspace=0.3)
    graphics.print_or_show(f, False, fn)
Ejemplo n.º 10
0
    def evaluate(self, ds, ch):
        """Apply self-emission model to data

        Does a simple linear interpolation of the Rself value from 
        surrounding calibartion lines. Rself will be zero at the first
        calibration time and will be estimate at the second calibrated time
        to match the observed space view signal given the gain etc. The
        values will then be interpolated in time to generate the new
        values of Rself. The final curves will give a sawtooth variation
        starting near zero to a maximum just before the next calibration
        observation.

        This method:

        - extracts the predictor from the source scanlines dataset (time)
        - converts this to the right format, including masking bad lines
        - estimate the predictand (space counts) for all lines assuming a
          linear model

        Parameters
        ----------

        ds : xarray.Dataset
            Dataset containing the L1B scanlines for which the
            self-emission is to be estimated
        ch : int
            Channel for which to estimate self-emission.

        Returns
        -------

        X : xarray.Dataset
            Predictor that was used to evaluate
        Y : `typhon.physics.units.tools.UnitsAwareDataArray`
            Estimates of self-emission for all scanlines in ds
        """
        #
        # Get the temperatures to estimate uncertainty
        #
        # Make sure temperatures are in sync with space counts etc.
        # so ensures both space and iwct observations available
        #
        views_space = xarray.DataArray(
            ds["scantype"].values == self.hirs.typ_space,
            coords=ds["scantype"].coords)
        views_iwct = xarray.DataArray(
            ds["scantype"].values == self.hirs.typ_iwt,
            coords=ds["scantype"].coords)
        dsi = self.hirs.dist_space_iwct
        space_followed_by_iwct = (views_space[:-dsi].variable
                                  & views_iwct[dsi:].variable)

        ds_temp = ds.isel(time=slice(None, -dsi)).isel(
            time=space_followed_by_iwct)

        start = True
        L = []
        for t_fld in self.temperatures:
            t_fld = _tovs_defs.temperature_names.get(t_fld, t_fld)
            x = ds["temperature_{:s}".format(t_fld)]
            for dim in set(x.dims) - {"time"}:
                x = x.mean(dim=dim, keep_attrs=True)
            newT = x.astype("f8")
            L.append(x.astype("f8"))  # prevent X⁴ precision loss
            #
            # Convert Temperatures to radiance
            #
            Rad = self.hirs.srfs[ch - 1].blackbody_radiance(
                ureg.Quantity(newT.values, ureg.K))
            gd = (newT > 340.)
            if numpy.sum(gd) > 0:
                Rad[gd] = numpy.nan
            if start:
                npR = numpy.zeros((1, len(Rad)), dtype=numpy.float32)
                npRout = numpy.zeros((1, len(Rad)), dtype=numpy.float32)
                npR[0, :] = Rad[:]
                start = False
            else:
                npRout[0, :] = Rad
                npR = numpy.append(npR, npRout, axis=0)

        # Needed as predictors for T_outlier routines
        X = xarray.merge(L)
        Tname = "temperature_{:s}".format(self.temperatures[0])
        R = UADA(npR,\
                     dims=['ntemp','time'],\
                     coords={'time':ds[Tname].coords['time'].values,\
                                 'ntemp':numpy.arange(len(self.temperatures))},\
                                 attrs={"units": str(rad_u["si"])}
                 )
        start = True
        L = []
        for t_fld in self.temperatures:
            t_fld = _tovs_defs.temperature_names.get(t_fld, t_fld)
            x = ds_temp["temperature_{:s}".format(t_fld)]
            for dim in set(x.dims) - {"time"}:
                x = x.mean(dim=dim, keep_attrs=True)
            newT = x.astype("f8")
            L.append(x.astype("f8"))  # prevent X⁴ precision loss
            #
            # Convert Temperatures to radiance
            #
            Radspace = self.hirs.srfs[ch - 1].blackbody_radiance(
                ureg.Quantity(newT.values, ureg.K))
            gd = (newT > 340.)
            if numpy.sum(gd) > 0:
                Radspace[gd] = numpy.nan
            if start:
                npRspace = numpy.zeros((1, len(Radspace)), dtype=numpy.float32)
                npRspaceout = numpy.zeros((1, len(Radspace)),
                                          dtype=numpy.float32)
                npRspace[0, :] = Radspace[:]
                start = False
            else:
                npRspaceout[0, :] = Radspace
                npRspace = numpy.append(npRspace, npRspaceout, axis=0)

        # Needed as predictors for T_outlier routines
        X = xarray.merge(L)
        Tname = "temperature_{:s}".format(self.temperatures[0])
        R_space = UADA(npRspace,\
                     dims=['ntemp','time'],\
                     coords={'time':ds_temp[Tname].coords['time'].values,\
                                 'ntemp':numpy.arange(len(self.temperatures))},\
                                 attrs={"units": str(rad_u["si"])}
                       )
        #
        # Get where different views are
        #
        views_space = xarray.DataArray(ds["scantype"].values == \
                                           tovs.HIRS.typ_space, \
                                           coords=ds["scantype"].coords)
        #        views_iwct = xarray.DataArray(ds["scantype"].values == \
        #                                          tovs.HIRS.typ_iwt, \
        #                                          coords=ds["scantype"].coords)
        views_Earth = xarray.DataArray(ds["scantype"].values == \
                                           tovs.HIRS.typ_Earth, \
                                           coords=ds["scantype"].coords)
        #
        # Get counts
        #
        (counts_space, counts_iwct) = self.hirs.extract_calibcounts(ds, ch)

        #
        # Get gain/offset/non-linear etc.
        #
        (time, offset, slope,
         a2) = self.hirs.calculate_offset_and_slope(ds, ch,
                                                    self.hirs.srfs[ch - 1])
        if not numpy.array_equal(numpy.isfinite(offset),
                                 numpy.isfinite(slope)):
            raise ValueError("Expecting offset and slope to have same "
                             "finite values, but I got disappointed.")

        # Check to see if we have a non-linear term
        try:
            a_2 = a2.values[0]
        except:
            a_2 = 0.

        #
        # Note use median here as Gerrit does - presumably for 'outlier' cases
        # sometimes seen in calibration views
        #
        slope_val = slope.median('scanpos').values
        #
        # Get Rself at calibration points based on previous gain
        #
        Rself_cal = numpy.zeros((counts_space.values.shape[0]-1,\
                                     counts_space.values.shape[1]))
        for pos in range(counts_space.values.shape[1]):
            Rself_cal[:,pos]=slope_val[pos]*\
                (counts_space.values[1:,pos]-counts_space.values[0:-1,pos])+\
                a_2*(counts_space.values[1:,pos]**2-\
                                counts_space.values[0:-1,pos]**2)

        #
        # Gerrits code seems to use the median values so we will as well
        #
        Rself_med = numpy.zeros(Rself_cal.shape[0])
        for pos in range(Rself_cal.shape[0]):
            Rself_med[pos] = numpy.median(Rself_cal[pos, :])
        scanpos_time = ds["time"][views_Earth]
        Rearth = R.values[:, views_Earth]
        Rspace = R_space.values[:, :]

        #
        # Get combination of input Ts that mimic observed self
        # emission - used to estimate uncertainty, not Rself
        # itself
        # Note fit to delta radiances to radiances themselves
        #
        p_estRself, u_est, ok = self.estimate_Rself(Rspace, Rself_med)

        #
        # Loop round calibration cycle data to get Rself at earth view
        # locations
        #
        # Also
        # Estimate uncertainty from varition of other temperatures as
        # percentage from linear model and apply mean percentage to
        # Rself to get uncertainty
        #
        Rself = numpy.zeros(shape=(scanpos_time.coords["time"].size,), \
                                dtype="f4")
        uRself = numpy.zeros(shape=(scanpos_time.coords["time"].size,), \
                                 dtype="f4")
        estR = numpy.zeros(shape=(scanpos_time.coords["time"].size,), \
                               dtype="f4")
        Rself.fill(numpy.nan)
        uRself.fill(numpy.nan)
        estR.fill(numpy.nan)
        for i in range(len(time.values) - 1):
            gd = (scanpos_time.values >= time.values[i]) & \
                (scanpos_time.values <= time.values[i+1])
            if numpy.sum(gd) > 0:
                dtime = self.get_np_seconds(time.values[i + 1] -
                                            time.values[i])
                Rself_slope = Rself_med[i] / dtime
                dtime = self.get_np_seconds(scanpos_time.values[gd] -
                                            time.values[i])
                Rself[gd] = Rself_slope * dtime
                if ok:
                    T = Rearth[:, gd]
                    Tdata = numpy.zeros(T.shape[1])
                    Tdata[:] = p_estRself[0]
                    for j in range(len(p_estRself) - 1):
                        Tdata = Tdata + p_estRself[j + 1] * (T[j, :] -
                                                             Rspace[j, i])
                    estR[gd] = Tdata
                    zero_point = Tdata[0]
                    Tdata = Tdata - zero_point
                    U_slope = (Tdata[Tdata.shape[0] - 1] -
                               Tdata[0]) / Tdata.shape[0]
                    Uval = numpy.zeros(Tdata.shape[0])
                    Uval[:] = 0.
                    for j in range(Tdata.shape[0]):
                        if numpy.all(numpy.isfinite(T[:, j])):
                            #
                            # Uncertainty is deviation from a straight line
                            #
                            Uval[j] = numpy.abs((Tdata[j] - U_slope * j))
                        else:
                            Uval[j] == 0.
                    uRself[gd] = Uval

        #
        # Set minimum uncertainty at that from fit of data
        #
        gd = (numpy.isfinite(uRself) & (uRself < u_est))
        if numpy.sum(gd) > 0:
            uRself[gd] = u_est

        # Fill all lines with u_est if no calculation done
        gd = (~numpy.isfinite(Rself) | ~numpy.isfinite(uRself))
        if numpy.sum(gd) > 0:
            uRself[gd] = u_est


#        a = numpy.zeros((len(Rself),8))
#        a[:,0] = Rself
#        a[:,1] = uRself
#        a[:,2] = Rearth[0,:]
#        a[:,3] = Rearth[1,:]
#        a[:,4] = Rearth[2,:]
#        a[:,5] = Rearth[3,:]
#        a[:,6] = Rearth[4,:]
#        a[:,7] = estR[:]
#        numpy.savetxt('test.dat',a)

        Y_pred = UADA(Rself,\
                          coords=scanpos_time.coords["time"].coords, \
                          attrs={"units": str(rad_u["si"])})

        #
        # Force u_Rself to be a single value as this is what
        # Gerrits code expects unfortunately - subsequent code
        # seems hardwired to this assumption...
        #
        uRself = numpy.mean(uRself)

        rself_time = [scanpos_time.values[0],\
                          scanpos_time.values[len(scanpos_time.values)-1]]
        return rself_time, X, Y_pred, uRself