Esempio n. 1
0
    def run(self, data):
        """
        Expects a level2 file structure to be passed.
        """
        # First we need:
        # 1) The TOD data
        # 2) The feature bits to select just the observing period
        # 3) Elevation to remove the atmospheric component
        tod = np.nanmean(data['level1/spectrometer/band_average'][...], axis=1)
        feeds = data['level1/spectrometer/feeds'][:]
        nFeeds, nSamples = tod.shape
        scan_edges = self.getGroup(data, data,
                                   f'{self.level2}/Statistics/scan_edges')

        self.flags = np.zeros(tod.shape).astype(bool)

        for ifeed in range(nFeeds):
            for (s, e) in scan_edges:
                rtod = tod[ifeed, s:e] - self.median_filter(
                    tod[ifeed, s:e], self.medfilt_stepsize)
                rms = stats.AutoRMS(rtod)
                f = gaussian_filter1d(
                    (rtod > rms * self.sigma_clip_value).astype(np.float), 50)
                f /= np.max(f)
                self.flags[ifeed, s:e] = (f > 0.5)
Esempio n. 2
0
    def findHotCold(self, mtod):
        """
        Find the hot/cold sections of the ambient load scan
        """

        nSamps = mtod.size
        # Assuming there is a big step we take mid-value
        midVal = (np.nanmax(mtod)+np.nanmin(mtod))/2.

        # Sort the tod...
        mtodSort = np.sort(mtod)
        mtodArgsort = np.argsort(mtod)

        # Find the whitenoise rms
        rms = stats.AutoRMS(mtod[:,None]).flatten()

        # Where is it hot? where is it cold?
        groupHot  = (mtodSort-midVal) > rms*5
        groupCold = (mtodSort-midVal) < rms*5

        # Find indices where vane is in
        hotTod = mtod[(mtodArgsort[groupHot])]
        X =  np.abs((hotTod - np.median(hotTod))/rms) < 1
        if np.sum(X) == 0:
            raise NoHotError('No hot load data found')
        snips = int(np.min(np.where(X)[0])), int(np.max(np.where(X)[0]))
        idHot = (mtodArgsort[groupHot])[X]
        idHot = np.sort(idHot)

        # Find indices where vane is out
        coldTod = mtod[(mtodArgsort[groupCold])]
        X =  np.abs((coldTod - np.median(coldTod))/rms) < 1
        if np.sum(X) == 0:
            raise NoColdError('No cold load data found')

        snips = int(np.min(np.where(X)[0])), int(np.max(np.where(X)[0]))
        idCold = (mtodArgsort[groupCold])[X]
        idCold = np.sort(idCold)

        # Just find cold AFTER calvane
        diffCold = idCold[1:] - idCold[:-1]
        jumps = np.where((diffCold > 50))[0]

        if len(jumps) == 0:
            snip = 0
        else:
            snip = int(jumps[0])

        idHot = np.sort(idHot)
        idCold = np.sort(idCold[snip:])
        idCold = idCold[idCold > min(idHot)]
        return idHot, idCold
Esempio n. 3
0
    def FitPowerSpectrum(self, tod):
        """
        Calculate the power spectrum of the data, fits a 1/f noise curve, returns parameters
        """
        auto_rms = stats.AutoRMS(tod)
        nu, ps, counts = self.PowerSpectrum(tod)

        # Only select non-nan values
        # You may want to increase min counts,
        # as the power spectrum is non-gaussian for small counts
        good = (counts > 50) & ((nu < 0.03) | (nu > 0.05))

        args = (nu[good], ps[good], auto_rms / np.sqrt(counts[good]), auto_rms)
        bounds = [[None, None], [-3, 0]]
        P0 = [0, -1]
        P1 = minimize(self.Error, P0, args=args, bounds=bounds)

        return ps, nu, P1.x, auto_rms
Esempio n. 4
0
    def create_maps(self, data, tod, filters, sel):
        """
        Bin maps into instrument frame centred on source
        """

        mjd = data['level1/spectrometer/MJD'][:]

        # We do Jupiter in the Az/El frame but celestial in sky frame
        #if self.source.upper() == 'JUPITER':
        az = data['level1/spectrometer/pixel_pointing/pixel_az'][:]
        el = data['level1/spectrometer/pixel_pointing/pixel_el'][:]
        N = az.shape[1] // 2 * 2
        daz = np.gradient(az[0, :]) * 50.
        daz = daz[sel]
        az = az[:, sel]
        el = el[:, sel]
        cw = daz > 1e-2
        ccw = daz < 1e-2

        mjd = mjd[sel]

        npix = self.Nx * self.Ny

        temp_maps = {
            'map': np.zeros(npix, dtype=np.float64),
            'cov': np.zeros(npix, dtype=np.float64)
        }

        maps = {
            'map':
            np.zeros(
                (tod.shape[0], tod.shape[1], tod.shape[2], self.Nx, self.Ny)),
            'cov':
            np.zeros(
                (tod.shape[0], tod.shape[1], tod.shape[2], self.Nx, self.Ny))
        }

        feed_avg = {
            'map': np.zeros((tod.shape[0], self.Nx, self.Ny)),
            'cov': np.zeros((tod.shape[0], self.Nx, self.Ny))
        }

        scan_maps = {
            'CW': {
                'map': np.zeros((self.Nx, self.Ny)),
                'cov': np.zeros((self.Nx, self.Ny))
            },
            'CCW': {
                'map': np.zeros((self.Nx, self.Ny)),
                'cov': np.zeros((self.Nx, self.Ny))
            }
        }

        azSource, elSource, raSource, decSource = Coordinates.sourcePosition(
            self.source, mjd, self.lon, self.lat)
        self.src_el = np.mean(elSource)
        self.src_az = np.mean(azSource)
        for ifeed in tqdm(self.feedlist,
                          desc=f'{self.name}:create_maps:{self.source}'):
            feed_tod = tod[ifeed, ...]

            #if self.source.upper() == 'JUPITER':
            x, y = Coordinates.Rotate(azSource, elSource, az[ifeed, :],
                                      el[ifeed, :], 0)

            pixels, pX, pY = self.getpixels(x, y, self.dx, self.dy, self.Nx,
                                            self.Ny)

            mask = np.ones(pixels.size, dtype=int)
            for isb in range(tod.shape[1]):
                for ichan in range(1, tod.shape[2] - 1):  # Always skip edges
                    for k in temp_maps.keys():
                        temp_maps[k][:] = 0.

                    z = (feed_tod[isb, ichan, sel] -
                         filters[ifeed, isb, ichan])
                    mask[:] = 1
                    mask[(pixels == -1) | np.isnan(z) | np.isinf(z)] = 0

                    if np.sum(np.isfinite(z)) == 0:
                        continue

                    rms = stats.AutoRMS(z)

                    weights = {
                        'map': z.astype(np.float64) / rms**2,
                        'cov': np.ones(z.size) / rms**2
                    }
                    for k in temp_maps.keys():
                        binFuncs.binValues(temp_maps[k],
                                           pixels,
                                           weights=weights[k],
                                           mask=mask)
                        maps[k][ifeed, isb, ichan,
                                ...] = np.reshape(temp_maps[k],
                                                  (self.Ny, self.Nx))
                        feed_avg[k][ifeed,
                                    ...] += np.reshape(temp_maps[k],
                                                       (self.Ny, self.Nx))

                    if (ifeed == 0):
                        for (key, direction) in zip(['CW', 'CCW'], [cw, ccw]):
                            for k in temp_maps.keys():
                                temp_maps[k][:] = 0.
                                binFuncs.binValues(
                                    temp_maps[k],
                                    pixels[direction],
                                    weights=weights[k][direction],
                                    mask=mask[direction])
                                scan_maps[key][k] += np.reshape(
                                    temp_maps[k], (self.Ny, self.Nx))

        xygrid = np.meshgrid(
            (np.arange(self.Nx) + 0.5) * self.dx - self.Nx * self.dx / 2.,
            (np.arange(self.Ny) + 0.5) * self.dy - self.Ny * self.dy / 2.)

        feed_avg['xygrid'] = xygrid
        maps['xygrid'] = xygrid

        feed_avg = self.average_maps(feed_avg)

        for key in scan_maps.keys():
            scan_maps[key] = self.average_maps(scan_maps[key])
            scan_maps[key]['xygrid'] = xygrid

        map_axes = np.array([a for a in maps['map'].shape])
        map_axes[2] = int(map_axes[2] / self.binwidth)
        map_axes = np.insert(map_axes, 3, self.binwidth)
        maps['map'] = np.nansum(np.reshape(maps['map'], map_axes), axis=3)
        maps['cov'] = np.nansum(np.reshape(maps['cov'], map_axes), axis=3)
        maps = self.average_maps(maps)

        self.map_freqs = np.mean(np.reshape(
            data[f'{self.level2}/frequency'][...], map_axes[1:4]),
                                 axis=-1)

        return maps, feed_avg, scan_maps
Esempio n. 5
0
    def create_maps(self, data, tod, filters, sel):
        """
        Bin maps into instrument frame centred on source
        """

        mjd = data['spectrometer/MJD'][:]

        # We do Jupiter in the Az/El frame but celestial in sky frame
        #if self.source.upper() == 'JUPITER':
        az = data['spectrometer/pixel_pointing/pixel_az'][:]
        el = data['spectrometer/pixel_pointing/pixel_el'][:]
        N = az.shape[1] // 2 * 2
        daz = np.gradient(az[0, :]) * 50.
        daz = daz[sel]
        az = az[:, sel]
        el = el[:, sel]
        cw = daz > 1e-2
        ccw = daz < 1e-2

        mjd = mjd[sel]

        npix = self.Nx * self.Ny

        temp_maps = {
            'map': np.zeros(npix, dtype=np.float64),
            'cov': np.zeros(npix, dtype=np.float64)
        }

        maps = {
            'maps': {
                'map': np.zeros(
                    (tod.shape[0], tod.shape[1], self.Nx, self.Ny)),
                'cov': np.zeros((tod.shape[0], tod.shape[1], self.Nx, self.Ny))
            }
        }
        maps['feed_avg'] = {
            'map': np.zeros((tod.shape[0], 1, self.Nx, self.Ny)),
            'cov': np.zeros((tod.shape[0], 1, self.Nx, self.Ny))
        }
        maps['CW'] = {
            'map': np.zeros((1, 1, self.Nx, self.Ny)),
            'cov': np.zeros((1, 1, self.Nx, self.Ny))
        }
        maps['CCW'] = {
            'map': np.zeros((1, 1, self.Nx, self.Ny)),
            'cov': np.zeros((1, 1, self.Nx, self.Ny))
        }

        selections = {
            k: selection
            for k, selection in zip(maps.keys(), [
                np.ones(az.shape[-1], dtype=bool),
                np.ones(az.shape[-1], dtype=bool), cw, ccw
            ])
        }
        slices = {
            k: sl
            for k, sl in zip(maps.keys(), [
                lambda ifeed, isb: [
                    slice(ifeed, ifeed + 1),
                    slice(isb, isb + 1),
                    slice(None),
                    slice(None)
                ], lambda ifeed, isb: [
                    slice(ifeed, ifeed + 1),
                    slice(None),
                    slice(None),
                    slice(None)
                ], lambda ifeed, isb:
                [slice(None),
                 slice(None),
                 slice(None),
                 slice(None)], lambda ifeed, isb:
                [slice(None),
                 slice(None),
                 slice(None),
                 slice(None)]
            ])
        }

        self.source_positions = {
            k: a
            for k, a in zip(['az', 'el', 'ra', 'dec'],
                            Coordinates.sourcePosition(self.source, mjd,
                                                       self.lon, self.lat))
        }
        self.source_positions['mean_el'] = np.mean(self.source_positions['el'])
        self.source_positions['mean_az'] = np.mean(self.source_positions['az'])

        for ifeed in tqdm(self.feedlist,
                          desc=f'{self.name}:create_maps:{self.source}'):
            feed_tod = tod[ifeed, ...]

            pixels = self.get_pixel_positions(self.source_positions['az'],
                                              self.source_positions['el'],
                                              az[ifeed, :], el[ifeed, :])

            mask = np.ones(pixels.size, dtype=int)
            for isb in range(tod.shape[1]):
                for k in temp_maps.keys():
                    temp_maps[k][:] = 0.
                z = (feed_tod[isb, sel] - filters[ifeed, isb])
                mask[:] = 1
                mask[(pixels == -1) | np.isnan(z) | np.isinf(z)] = 0

                if np.sum(np.isfinite(z)) == 0:
                    continue

                rms = stats.AutoRMS(z)

                weights = {
                    'map': z.astype(np.float64) / rms**2,
                    'cov': np.ones(z.size) / rms**2
                }
                for k in temp_maps.keys():
                    for mode, map_data in maps.items():
                        if ('CW' in mode) & (ifeed > 1):
                            continue
                        binFuncs.binValues(
                            temp_maps[k],
                            pixels[selections[mode]],
                            weights=weights[k][selections[mode]],
                            mask=mask[selections[mode]])
                        maps[mode][k][slices[mode](ifeed, isb)] = np.reshape(
                            temp_maps[k], (self.Ny, self.Nx))

        xygrid = np.meshgrid(
            (np.arange(self.Nx) + 0.5) * self.dx - self.Nx * self.dx / 2.,
            (np.arange(self.Ny) + 0.5) * self.dy - self.Ny * self.dy / 2.)

        for k, v in maps.items():
            maps[k] = self.average_maps(maps[k])
            maps[k]['xygrid'] = xygrid
        return maps
Esempio n. 6
0
    def run(self, data):
        """
        Expects a level2 file structure to be passed.
        """
        fname = data.filename.split('/')[-1]
        # First we need:
        # 1) The TOD data
        # 2) The feature bits to select just the observing period
        # 3) Elevation to remove the atmospheric component
        tod = data[f'{self.level2}/averaged_tod'][...]
        az = data['level1/spectrometer/pixel_pointing/pixel_az'][...]
        el = data['level1/spectrometer/pixel_pointing/pixel_el'][...]
        feeds = data['level1/spectrometer/feeds'][:]
        bands = [
            b.decode('ascii') for b in data['level1/spectrometer/bands'][:]
        ]

        statistics = self.getGroup(data, data, f'{self.level2}/Statistics')
        scan_edges = self.getGroup(data, statistics, 'scan_edges')

        # Looping over Feed - Band - Channel, perform 1/f noise fit
        nFeeds, nBands, nChannels, nSamples = tod.shape
        #if 20 in feeds:
        #    nFeeds -= 1
        nScans = len(scan_edges)

        self.powerspectra = np.zeros((nFeeds, nBands, nScans, self.nbins))
        self.freqspectra = np.zeros((nFeeds, nBands, nScans, self.nbins))
        self.fnoise_fits = np.zeros((nFeeds, nBands, nScans, 3))
        self.wnoise_auto = np.zeros((nFeeds, nBands, nChannels, nScans, 1))
        self.atmos = np.zeros((nFeeds, nBands, nScans, 3))
        self.atmos_errs = np.zeros((nFeeds, nBands, nScans, 3))

        self.filter_tods = []  # Store as a list of arrays, one for each "scan"
        self.filter_coefficients = np.zeros(
            (nFeeds, nBands, nChannels, nScans,
             1))  # Stores the per channel gradient of the  median filter
        self.atmos_coefficients = np.zeros(
            (nFeeds, nBands, nChannels, nScans,
             1))  # Stores the per channel gradient of the  median filter

        pbar = tqdm(total=(nFeeds * nBands * nChannels * nScans),
                    desc=self.name)

        for iscan, (start, end) in enumerate(scan_edges):
            local_filter_tods = np.zeros((nFeeds, nBands, end - start))
            for ifeed in range(nFeeds):
                if feeds[ifeed] == 20:
                    continue
                for iband in range(nBands):

                    band_average = np.nanmean(tod[ifeed, iband, 3:-3,
                                                  start:end],
                                              axis=0)
                    atmos_filter, atmos, atmos_errs = self.FitAtmosAndGround(
                        band_average, az[ifeed, start:end], el[ifeed,
                                                               start:end])

                    local_filter_tods[ifeed, iband, :] = self.median_filter(
                        band_average - atmos_filter)[:band_average.size]

                    self.atmos[ifeed, iband, iscan, :] = atmos
                    self.atmos_errs[ifeed, iband, iscan, :] = atmos_errs

                    ps, nu, f_fits, w_auto = self.FitPowerSpectrum(
                        band_average - atmos_filter -
                        local_filter_tods[ifeed, iband, :])
                    self.powerspectra[ifeed, iband, iscan, :] = ps
                    self.freqspectra[ifeed, iband, iscan, :] = nu
                    self.fnoise_fits[ifeed, iband, iscan, 0] = w_auto
                    self.fnoise_fits[ifeed, iband, iscan, 1:] = f_fits

                    #self.logger(f'{fname}:{self.name}: Feed {feeds[ifeed]} Band {bands[iband]} RMS  - {w_auto:.3f}K')
                    #self.logger(f'{fname}:{self.name}: Feed {feeds[ifeed]} Band {bands[iband]} Knee - {f_fits[0]:.3f}')
                    #self.logger(f'{fname}:{self.name}: Feed {feeds[ifeed]} Band {bands[iband]} Spec - {f_fits[1]:.3f}')

                    for ichan in range(nChannels):
                        if np.nansum(tod[ifeed, iband, ichan, start:end]) == 0:
                            continue
                        # Check atmosphere coefficients
                        atmos_coeff, med_coeff, offset = self.coefficient_jointfit(
                            tod[ifeed, iband, ichan, start:end], atmos_filter,
                            local_filter_tods[ifeed, iband, :])
                        w_auto = stats.AutoRMS(tod[ifeed, iband, ichan,
                                                   start:end])
                        self.wnoise_auto[ifeed, iband, ichan,
                                         iscan, :] = w_auto
                        self.filter_coefficients[ifeed, iband, ichan,
                                                 iscan, :] = med_coeff
                        self.atmos_coefficients[ifeed, iband, ichan,
                                                iscan, :] = atmos_coeff
                        pbar.update(1)
            self.filter_tods += [local_filter_tods]
        pbar.close()