Exemple #1
0
 def test_pulse_phase1(self):
     """Test pulse phase calculation, frequency only."""
     times = np.arange(0, 4, 0.5)
     ph = pulse_phase(times, 1, ph0=0, to_1=False)
     np.testing.assert_array_almost_equal(ph, times)
Exemple #2
0
def run_folding(file,
                freq,
                fdot=0,
                fddot=0,
                nbin=16,
                nebin=16,
                tref=None,
                test=False,
                emin=0,
                emax=1e32,
                norm='to1',
                smooth_window=None,
                deorbit_par=None,
                **opts):
    from matplotlib.gridspec import GridSpec
    import matplotlib.pyplot as plt

    file_label = ''
    ev = load_events(file)
    if deorbit_par is not None:
        events = deorbit_events(ev, deorbit_par)

    times = ev.time
    gtis = ev.gti
    plot_energy = True
    if hasattr(ev, 'energy') and ev.energy is not None:
        energy = ev.energy
        elabel = 'Energy'
    elif hasattr(ev, 'pi') and ev.pi is not None:
        energy = ev.pi
        elabel = 'PI'
    else:
        energy = np.ones_like(times)
        elabel = ''
        plot_energy = False

    if tref is None:
        tref = times[0]

    good = (energy > emin) & (energy < emax)
    times = times[good]
    energy = energy[good]
    phases = pulse_phase(times - tref, freq, fdot, fddot, to_1=True)

    binx = np.linspace(0, 1, nbin + 1)
    if plot_energy:
        biny = np.percentile(energy, np.linspace(0, 100, nebin + 1))
        biny[0] = emin
        biny[-1] = emax

    profile, _ = np.histogram(phases, bins=binx)
    if smooth_window is None:
        smooth_window = np.min([len(profile), np.max([len(profile) // 10, 5])])
        smooth_window = _check_odd(smooth_window)

    smoothed_profile = savgol_filter(profile,
                                     window_length=smooth_window,
                                     polyorder=2,
                                     mode='wrap')

    profile = np.concatenate((profile, profile))
    smooth = np.concatenate((smoothed_profile, smoothed_profile))

    if plot_energy:
        histen, _ = np.histogram(energy, bins=biny)

        hist2d, _, _ = np.histogram2d(phases.astype(np.float64),
                                      energy,
                                      bins=(binx, biny))

    binx = np.concatenate((binx[:-1], binx + 1))
    meanbins = (binx[:-1] + binx[1:]) / 2

    if plot_energy:
        hist2d = np.vstack((hist2d, hist2d))
        hist2d_save = np.copy(hist2d)
        X, Y = np.meshgrid(binx, biny)

        if norm == 'ratios':
            hist2d /= smooth[:, np.newaxis]
            hist2d *= histen[np.newaxis, :]
            file_label = '_ratios'
        else:
            hist2d /= histen[np.newaxis, :]
            factor = np.max(hist2d, axis=0)[np.newaxis, :]
            hist2d /= factor
            file_label = '_to1'

    plt.figure()
    if plot_energy:
        gs = GridSpec(2, 2, height_ratios=(1, 3))
        ax0 = plt.subplot(gs[0, 0])
        ax1 = plt.subplot(gs[1, 0], sharex=ax0)
        ax2 = plt.subplot(gs[1, 1], sharex=ax0)
        ax3 = plt.subplot(gs[0, 1])

    else:
        ax0 = plt.subplot()

    # Plot pulse profile
    max = np.max(smooth)
    min = np.min(smooth)
    ax0.plot(meanbins, profile, drawstyle='steps-mid', color='white', zorder=2)
    ax0.plot(meanbins,
             smooth,
             drawstyle='steps-mid',
             label='Smooth profile '
             '(P.F. = {:.1f}%)'.format(100 * (max - min) / max),
             color='k',
             zorder=3)
    err_low, err_high = \
        poisson_conf_interval(smooth,
                              interval='frequentist-confidence', sigma=3)

    try:
        ax0.fill_between(meanbins,
                         err_low,
                         err_high,
                         color='grey',
                         zorder=1,
                         alpha=0.5,
                         label='3-sigma confidence',
                         step='mid')
    except AttributeError:
        # MPL < 2
        ax0.fill_between(meanbins,
                         err_low,
                         err_high,
                         color='grey',
                         zorder=1,
                         alpha=0.5,
                         label='3-sigma confidence')

    ax0.axhline(max, lw=1, color='k')
    ax0.axhline(min, lw=1, color='k')

    mean = np.mean(profile)
    ax0.fill_between(meanbins,
                     mean - np.sqrt(mean),
                     mean + np.sqrt(mean),
                     alpha=0.5)
    ax0.axhline(mean, ls='--')
    ax0.legend()
    ax0.set_ylim([0, None])

    if plot_energy:
        ax1.pcolormesh(X, Y, hist2d.T)
        ax1.semilogy()

        ax1.set_xlabel('Phase')
        ax1.set_ylabel(elabel)
        ax1.set_xlim([0, 2])

        pfs = []
        errs = []
        meannrgs = (biny[:-1] + biny[1:]) / 2
        for i, prof in enumerate(hist2d_save.T):
            smooth = savgol_filter(prof,
                                   window_length=smooth_window,
                                   polyorder=2,
                                   mode='wrap')
            max = np.max(smooth)
            min = np.min(smooth)
            pf = 100 * (max - min) / max
            ax2.plot(meanbins,
                     prof,
                     drawstyle='steps-mid',
                     label='{}={:.2f}-{:.2f}'.format(elabel, biny[i],
                                                     biny[i + 1], pf))
            std = np.max(prof - smooth)
            ax2.set_xlabel('Phase')
            ax2.set_ylabel('Counts')

            pfs.append(pf)
            errs.append(std / max)

        if len(meannrgs) < 6:
            ax2.legend()
        ax2.set_xlim([0, 2])

        ax3.errorbar(meannrgs,
                     pfs,
                     fmt='o',
                     yerr=errs,
                     xerr=(biny[1:] - biny[:-1]) / 2)
        ax3.semilogx()
        ax3.set_xlabel('Energy')
        ax3.set_ylabel('Pulsed fraction')

    plt.savefig('Energyprofile' + file_label + '.png')
    if not test:  # pragma:no cover
        plt.show()
Exemple #3
0
 def test_pulse_phase3(self):
     """Test pulse phase calculation, fddot only."""
     times = np.arange(0, 4, 0.5)
     ph = pulse_phase(times, 0, 0, 1, ph0=0, to_1=False)
     np.testing.assert_array_almost_equal(ph, 1/6 * times ** 3)
Exemple #4
0
 def test_pulse_phase3(self):
     """Test pulse phase calculation, fddot only."""
     times = np.arange(0, 4, 0.5)
     ph = pulse_phase(times, 0, 0, 1, ph0=0, to_1=False)
     np.testing.assert_array_almost_equal(ph, 1 / 6 * times**3)
Exemple #5
0
def efold_search_AandB(events_A,
                       events_B,
                       f_min,
                       f_max,
                       f_steps,
                       fdots=None,
                       time_intervals=None,
                       nbin=32,
                       pi_min=35,
                       pi_max=260,
                       return_peak=False,
                       z_n=2):
    # Scan over frequency and do epoch folding.

    A_mask = np.sqrt(
        np.square(events_A.x - events_A.centroid[0]) +
        np.square(events_A.y - events_A.centroid[1])) <= events_A.radius
    B_mask = np.sqrt(
        np.square(events_B.x - events_B.centroid[0]) +
        np.square(events_B.y - events_B.centroid[1])) <= events_B.radius

    temp_time = np.concatenate([events_A.time[A_mask], events_B.time[B_mask]])
    sorted_arg = np.argsort(temp_time)
    temp_time = temp_time[sorted_arg]
    temp_pi = np.concatenate([events_A.pi[A_mask],
                              events_B.pi[B_mask]])[sorted_arg]

    joined_ev = EventList_ext(time=temp_time,
                              gti=sting_gti.cross_two_gtis(
                                  events_A.gti, events_B.gti),
                              pi=temp_pi)
    ref_time = joined_ev.time[0]
    f_arr = np.linspace(f_min, f_max, num=f_steps)

    # if fdots:
    #     fgrid, fdgrid, z_stats = z_n_search(joined_ev.time, f_arr, nharm=z_n, nbin=nbin, gti=joined_ev.gti, fdots=fdots, segment_size=1e6)
    # else:
    #     fgrid, z_stats = z_n_search(joined_ev.time, f_arr, nharm=z_n, nbin=nbin, gti=joined_ev.gti, fdots=fdots, segment_size=1e6)

    pi_mask = ((joined_ev.pi > pi_min) * (joined_ev.pi < pi_max)).astype(bool)
    # The times to actually fold into a profile
    fold_times = joined_ev.time[pi_mask] - ref_time
    z_stats = []
    if fdots is not None:
        f_grid, fd_grid = np.meshgrid(f_arr, fdots)
        z_stats = np.zeros(f_grid.shape)
        for x in tqdm(range(f_steps)):
            for y in range(len(fdots)):
                # The phase of each folded event
                fold_phases = plsr.pulse_phase(fold_times,
                                               *[f_grid[y, x], fd_grid[y, x]])
                z_stats[y, x] = plsr.z_n(fold_phases, n=z_n)

        z_prob = stats.z2_n_logprobability(z_stats,
                                           ntrial=(f_steps * len(fdots)),
                                           n=z_n)

        if return_peak:
            max_yx = np.unravel_index(np.argmax(z_stats, axis=None),
                                      z_stats.shape)
            phase_bins, profile, profile_err, _ = \
                joined_ev.fold_events(*[f_grid[max_yx], fd_grid[max_yx]], time_intervals = time_intervals, \
                                        nbin = nbin, ref_time = ref_time, region_filter=False, pi_min=pi_min, pi_max=pi_max, weight_pos=False, z_n=z_n)

            return f_grid, fd_grid, z_prob, z_stats, phase_bins, profile, profile_err

        else:
            return f_grid, fd_grid, z_prob, z_stats

    else:
        for f in tqdm(f_arr):
            # The phase of each folded event
            fold_phases = plsr.pulse_phase(fold_times, f)

            z_stat = plsr.z_n(fold_phases, n=z_n)

            # _, _, _, z_stat = \
            #     joined_ev.fold_events(f, time_intervals = time_intervals, \
            #                             nbin = nbin, ref_time = joined_ev.time[0], region_filter=False, pi_min=pi_min, pi_max=pi_max, weight_pos=False, z_n=z_n)

            z_stats.append(z_stat)

        z_stats = np.array(z_stats)
        z_prob = stats.z2_n_logprobability(z_stats, ntrial=len(f_arr), n=z_n)

        if return_peak:
            phase_bins, profile, profile_err, _ = \
                joined_ev.fold_events(f_arr[np.argmax(z_stats)], time_intervals = time_intervals, \
                                        nbin = nbin, ref_time = ref_time, region_filter=False, pi_min=pi_min, pi_max=pi_max, weight_pos=False, z_n=z_n)

            return f_arr, z_prob, z_stats, phase_bins, profile, profile_err

        else:
            return f_arr, z_prob, z_stats
Exemple #6
0
 def test_pulse_phase1(self):
     """Test pulse phase calculation, frequency only."""
     times = np.arange(0, 4, 0.5)
     ph = pulse_phase(times, 1, ph0=0, to_1=False)
     np.testing.assert_array_almost_equal(ph, times)
Exemple #7
0
    def fold_events(self,
                    *frequency_derivatives,
                    time_intervals=None,
                    pi_min=35,
                    pi_max=1909,
                    region_filter=False,
                    centroid=None,
                    radius=None,
                    ref_time=None,
                    nbin=64,
                    weights=1,
                    gtis=None,
                    expocorr=False,
                    weight_pos=False,
                    z_n=2):

        # Epoch folding without livetime correction.
        # Includes region and energy filtering, position weighting, and custom weighting.

        if region_filter or weight_pos:
            if centroid == None:
                centroid = self.centroid
            if radius == None:
                radius = self.radius
        if time_intervals == None:
            time_intervals = [[self.time[0], self.time[-1]]]
        if ref_time == None:
            ref_time = self.time[0]
        if gtis == None:
            gtis = self.gti

        time_mask = np.zeros(np.shape(self.time))
        if np.shape(time_intervals)[-1] != 2:
            print('The array of time intervals has the wrong shape')
            return None
        for interval in time_intervals:
            start_time, end_time = interval
            if (start_time < np.min(self.time)):
                print('Invalid start time')
                return None
            elif (end_time > np.max(self.time)):
                print('Invalid end time')
                return None
            time_mask = time_mask + ((self.time >= start_time) *
                                     (self.time <= end_time))
        time_mask = time_mask.astype(bool)
        pi_mask = ((self.pi > pi_min) * (self.pi < pi_max)).astype(bool)

        reg_mask = np.ones(np.shape(self.time)).astype(bool)
        p_weights = 1.0
        if region_filter:
            # if weight_pos:
            # print('Region filtering overrides position weighting.')
            reg_mask = (np.sqrt(
                np.square(self.x - centroid[0]) +
                np.square(self.y - centroid[1])) < radius).astype(bool)
        elif weight_pos:
            #             print('Make sure you have called set_xy_weights')
            p_weights = self.xy_weights[time_mask * pi_mask * reg_mask]


#             print([g.x_mean, g.y_mean, g.amplitude, g.x_stddev, g.y_stddev])

# The times to actually fold into a profile
        fold_times = self.time[time_mask * pi_mask * reg_mask]
        # The phase of each folded event
        fold_phases = plsr.pulse_phase(fold_times, *frequency_derivatives)

        phase_bins, profile, profile_err = plsr.fold_events(fold_times, *frequency_derivatives, ref_time=ref_time, \
                                                           nbin=nbin, weights=weights * p_weights, gtis=gtis, expocorr=expocorr)

        z_stat = plsr.z_n(fold_phases, n=z_n, norm=weights * p_weights)

        return phase_bins, profile, profile_err, z_stat
Exemple #8
0
    def fold_events_ltcorr(self, *frequency_derivatives, time_intervals= None, pi_min=35, pi_max=1909, \
                           region_filter=False, centroid = None, radius=None, ref_time=None, nbin = 64, weights  = 1, gtis = None, expocorr=False, weight_pos=False):
        # Do Epoch folding to look for pulsations while also correction for livetime variations. This is important for high count rates and high pulse fractions.
        # Includes region and energy filtering, position weighting, and custom weighting.

        if centroid == None:
            centroid = self.centroid
        if radius == None:
            radius = self.radius
        if time_intervals == None:
            time_intervals = [[self.time[0], self.time[-1]]]
        if ref_time == None:
            ref_time = self.time[0]
        if gtis == None:
            gtis = self.gti

        time_mask = np.zeros(np.shape(self.time))
        if np.shape(time_intervals)[-1] != 2:
            print('The array of time intervals has the wrong shape')
            return None
        for interval in time_intervals:
            start_time, end_time = interval
            if (start_time < np.min(self.time)):
                print('Invalid start time')
                return None
            elif (end_time > np.max(self.time)):
                print('Invalid end time')
                return None
            time_mask = time_mask + ((self.time >= start_time) *
                                     (self.time <= end_time))
        time_mask = time_mask.astype(bool)
        pi_mask = ((self.pi > pi_min) * (self.pi < pi_max)).astype(bool)

        reg_mask = np.ones(np.shape(self.time)).astype(bool)
        p_weights = 1.0
        if region_filter:
            if weight_pos:
                print('Region filtering overrides position weighting.')
            reg_mask = (np.sqrt(
                np.square(self.x - centroid[0]) +
                np.square(self.y - centroid[1])) < radius).astype(bool)
        elif weight_pos:
            #             print('Make sure you have called set_xy_weights')
            p_weights = self.xy_weights[time_mask * pi_mask * reg_mask]
#             print([g.x_mean, g.y_mean, g.amplitude, g.x_stddev, g.y_stddev])

# The times to actually fold into a profile
        fold_times = self.time[time_mask * pi_mask * reg_mask]
        # The phase of each folded event
        fold_phases = plsr.pulse_phase(fold_times, *frequency_derivatives)

        # We should use PRIOR from every event though
        temp_times = self.time[time_mask]
        temp_prior = self.prior[time_mask]
        temp_phases = plsr.pulse_phase(temp_times, *frequency_derivatives)

        #         print(frequency_derivatives)
        p_derivs = plsr.p_to_f(*frequency_derivatives)
        p_t = np.zeros(np.shape(temp_times))
        # Taylor expand P(t)
        for i in range(len(p_derivs)):
            p_t = p_t + (p_derivs[i] * np.power(temp_times - ref_time, i) /
                         scipy.special.factorial(i, exact=True))

        phase_bins, profile, profile_err = plsr.fold_events(fold_times, *frequency_derivatives, ref_time=ref_time, \
                                                           nbin=nbin, weights=weights * p_weights, gtis=gtis, expocorr=expocorr)
        livetime_profile = np.zeros(np.shape(profile))

        # During which phase bin did PRIOR start counting before each event?
        start_bins = np.floor(nbin * (temp_phases - (temp_prior / p_t)))
        # What phase bin is each event at?
        end_bins = np.floor(nbin * temp_phases)

        # Add 1 to every phase bin for which PRIOR was active.
        for i in range(len(start_bins)):
            start = start_bins[i]
            end = end_bins[i]
            #             print(start)
            # If PRIOR started counting in a previous cycle, add 1 to each full cycle and to the partial cycles
            if start < 0:
                for j in range(int(np.floor(np.abs(start) / nbin))):
                    livetime_profile = livetime_profile + 1
                livetime_profile[int(start %
                                     nbin):] = livetime_profile[int(start %
                                                                    nbin):] + 1
                livetime_profile[:int(end)] = livetime_profile[:int(end)] + 1

            # Else, just add 1 to the portion of this cycle during which PRIOR was counting
            else:
                livetime_profile[int(start):int(
                    end)] = livetime_profile[int(start):int(end)] + 1

        # livetime corresponding to the phase bin for each photon
        fold_lts = np.array(
            [livetime_profile[int(b)] for b in np.floor(nbin * fold_phases)])

        z_stat = plsr.z_n(fold_phases,
                          n=2,
                          norm=weights * p_weights * np.max(livetime_profile) /
                          fold_lts)
        #         livetime_profile = livetime_profile/np.max(livetime_profile)

        return phase_bins, profile, profile_err, livetime_profile, z_stat
Exemple #9
0
def phase_tag(ev_list,
              parameter_info,
              gtis=None,
              mjdref=0,
              nbin=10,
              ref_to_max=False,
              pepoch=None,
              expocorr=True,
              pulse_ref_time=None,
              plot=True,
              test=False):
    """Phase-tag events in a FITS file with a given ephemeris.

    Parameters
    ----------
    ev_list : float
        Event times
    parameter_info : str or array of floats
        If a string, this is a pulsar parameter file that PINT is able to
        understand. Otherwise, this is a list of frequency derivatives
        [F0, F1, F2, ...]

    Other parameters
    ----------------
    gtis : [[g0_0, g0_1], [g1_0, g1_1], ...]
        Good time intervals
    nbin : int
        Number of nbin in the pulsed profile
    ref_to_max : bool
        Automatically refer the TOAs to the maximum of the profile
    pepoch : float, default None
        Reference epoch for the timing solution. If None, this is the start
        of the observation.
    pulse_ref_time : float
        Reference time for the pulse. This overrides ref_to_max
    plot : bool
        Plot diagnostics
    expocorr : bool
        Use exposure correction when calculating the profile

    """
    # ---- in MJD ----
    if gtis is None:
        gtis = np.array([[ev_list[0], ev_list[-1]]])

    ev_mjd = ev_list / 86400 + mjdref
    gtis_mjd = gtis / 86400 + mjdref

    pepoch = _assign_value_if_none(pepoch, gtis_mjd[0, 0])

    # ------ Orbital DEMODULATION --------------------
    if is_string(parameter_info):
        raise NotImplementedError('This part is not yet implemented. Please '
                                  'use single frequencies and pepoch as '
                                  'documented')

    else:
        frequency_derivatives = parameter_info
        times = (ev_mjd - pepoch) * 86400

    f = frequency_derivatives[0]

    phase = pulse_phase(times, *frequency_derivatives, to_1=False)
    gti_phases = pulse_phase((gtis_mjd - pepoch) * 86400,
                             *frequency_derivatives,
                             to_1=False)

    # ------- now apply period derivatives ------

    print("Calculating phases...", end='')
    ref_phase = 0
    ref_time = 0

    if pulse_ref_time is not None:
        ref_time = (pulse_ref_time - pepoch) * 86400
        ref_phase = ref_time * f
    elif ref_to_max:
        phase_to1 = phase - np.floor(phase)

        raw_profile, bins = np.histogram(phase_to1,
                                         bins=np.linspace(0, 1, nbin + 1))
        exposure = phase_exposure(gti_phases[0, 0],
                                  gti_phases[-1, 1],
                                  1,
                                  nbin=nbin,
                                  gtis=gti_phases)
        profile = raw_profile / exposure
        profile_err = np.sqrt(raw_profile) / exposure

        sinpars, bu, bu = fit_profile(profile,
                                      profile_err,
                                      nperiods=2,
                                      baseline=True,
                                      debug=test)
        fine_phases = np.linspace(0, 2, 1000 * 2)
        fitted_profile = std_fold_fit_func(sinpars, fine_phases)
        maxp = np.argmax(fitted_profile)
        ref_phase = fine_phases[maxp]
        if test:  # pragma: no cover
            # No tests with a pulsed profile yet
            ref_phase = bins[np.argmax(raw_profile)]

        ref_time = ref_phase / f

    phase -= ref_phase
    gti_phases -= ref_phase
    phase_to1 = phase - np.floor(phase)

    raw_profile, bins = np.histogram(phase_to1,
                                     bins=np.linspace(0, 1, nbin + 1))

    exposure = phase_exposure(gti_phases[0, 0],
                              gti_phases[-1, 1],
                              1,
                              nbin=nbin,
                              gtis=gti_phases)
    if np.any(np.logical_or(exposure != exposure, exposure == 0)):
        warnings.warn('Exposure has NaNs or zeros. Profile is not normalized')
        expocorr = False

    if not expocorr:
        exposure = np.ones_like(raw_profile)

    profile = raw_profile / exposure
    profile = np.append(profile, profile)
    exposure = np.append(exposure, exposure)
    profile_err = np.sqrt(profile)
    phs = (bins[1:] + bins[:-1]) / 2
    phs = np.append(phs, phs + 1)

    fig = None
    if plot:
        fig = plt.figure()
        plt.errorbar(phs,
                     profile / exposure,
                     yerr=profile_err / exposure,
                     fmt='none')
        plt.plot(phs, profile / exposure, 'k-', drawstyle='steps-mid')
        plt.xlabel("Phase")
        plt.ylabel("Counts")
        for i in range(20):
            plt.axvline(i * 0.1, ls='--', color='b')
        if not test:  # pragma: no cover
            plt.show()

    # ------ WRITE RESULTS BACK TO FITS --------------
    results = type('results', (object, ), {})
    results.ev_list = ev_list
    results.phase = phase
    results.frequency_derivatives = frequency_derivatives
    results.ref_time = ref_time
    results.figure = fig
    results.plot_phase = phs
    results.plot_profile = profile / exposure
    results.plot_profile_err = profile_err / exposure
    return results
Exemple #10
0
plt.axhline(y=prob3,lw=0.5,alpha=0.5)
plt.axhline(y=prob4,lw=0.5,alpha=0.5)
plt.axhline(y=prob5,lw=0.5,alpha=0.5)
plt.show()
"""

nphase = 20

##### using foldAt
phases = foldAt(x, pb, T0=0)

##### using phase mod 1
phase_mod = (1 / pb * x) % 1  #or shift by -0.7*pb

##### using stingray.pulse.pulsar
phase_stingray = pulse_phase(x, [1 / pb])  #,ph0=1-0.7)

expocorr = Lv2_phase.phase_exposure(times[0] - times[0],
                                    times[-1] - times[0],
                                    period=pb,
                                    nbin=nphase,
                                    gtis=gtis_conform)

################################################################################

##### Testing the 3 different routines for calculating phase

phase_bins = np.linspace(0, 1, nphase + 1)
profile, bin_edges, binnumber = stats.binned_statistic(phases,
                                                       y,
                                                       statistic='mean',
Exemple #11
0
def run_folding(file, freq, fdot=0, fddot=0, nbin=16, nebin=16, tref=0,
                test=False, emin=0, emax=1e32, norm='to1',
                smooth_window=None, **opts):

    file_label = ''
    ev = load_events(file)
    times = ev.time
    gtis = ev.gti
    plot_energy = True
    if hasattr(ev, 'energy') and ev.energy is not None:
        energy = ev.energy
        elabel = 'Energy'
    elif hasattr(ev, 'pi') and ev.pi is not None:
        energy = ev.pi
        elabel = 'PI'
    else:
        energy = np.ones_like(times)
        elabel = ''
        plot_energy = False

    good = (energy > emin) & (energy < emax)
    times = times[good]
    energy = energy[good]
    phases = pulse_phase(times - tref, freq, fdot, fddot, to_1=True)

    binx = np.linspace(0, 1, nbin + 1)
    if plot_energy:
        biny = np.percentile(energy, np.linspace(0, 100, nebin + 1))
        biny[0] = emin
        biny[-1] = emax

    profile, _ = np.histogram(phases, bins=binx)
    if smooth_window is None:
        smooth_window = np.min([len(profile), np.max([len(profile) // 10, 5])])
        smooth_window = _check_odd(smooth_window)

    smoothed_profile = savgol_filter(profile, window_length=smooth_window,
                                     polyorder=2, mode='wrap')

    profile = np.concatenate((profile, profile))
    smooth = np.concatenate((smoothed_profile, smoothed_profile))

    if plot_energy:
        histen, _ = np.histogram(energy, bins=biny)

        hist2d, _, _ = np.histogram2d(phases.astype(np.float64),
                                      energy, bins=(binx, biny))

    binx = np.concatenate((binx[:-1], binx + 1))
    meanbins = (binx[:-1] + binx[1:])/2

    if plot_energy:
        hist2d = np.vstack((hist2d, hist2d))
        X, Y = np.meshgrid(binx, biny)

        if norm == 'ratios':
            hist2d /= smooth[:, np.newaxis]
            hist2d *= histen[np.newaxis, :]
            file_label = '_ratios'
        else:
            hist2d /= histen[np.newaxis, :]
            factor = np.max(hist2d, axis=0)[np.newaxis, :]
            hist2d /= factor
            file_label = '_to1'

    plt.figure()
    if plot_energy:
        gs = GridSpec(2, 1, height_ratios=(1, 3))
        ax0 = plt.subplot(gs[0])
        ax1 = plt.subplot(gs[1], sharex=ax0)
    else:
        ax0 = plt.subplot()

    # Plot pulse profile
    max = np.max(smooth)
    min = np.min(smooth)
    ax0.plot(meanbins, profile, drawstyle='steps-mid',
             color='grey')
    ax0.plot(meanbins, smooth, drawstyle='steps-mid',
             label='Smooth profile '
                   '(P.F. = {:.1f}%)'.format(100 * (max - min) / max),
             color='k')
    ax0.axhline(max, lw=1, color='k')
    ax0.axhline(min, lw=1, color='k')


    mean = np.mean(profile)
    ax0.fill_between(meanbins, mean - np.sqrt(mean), mean + np.sqrt(mean))
    ax0.axhline(mean, ls='--')
    ax0.legend()

    if plot_energy:
        ax1.pcolormesh(X, Y, hist2d.T)
        ax1.semilogy()

        ax1.set_xlabel('Phase')
        ax1.set_ylabel(elabel)

    plt.savefig('Energyprofile' + file_label + '.png')
    if not test:  # pragma:no cover
        plt.show()