示例#1
0
def correctChargeSensor(xscan, yscan, xs, ys, fig=None):
    """ Calculate correction for a non-linear charge sensor

    Args:
        xscan, yscan: data of scan to be corrected
        xs, ys: scan of charge sensor response

    Returns:
        results (dict): dictionary with intermediate results

    """

    peaks = qtt.algorithms.coulomb.coulombPeaks(xs, ys, fig=fig, istep=1)

    try:
        # range for fit: select from the first detected peak
        xsr = xs[peaks[0]['pbottoml']:peaks[0]['p']]
        ysr = ys[peaks[0]['pbottoml']:peaks[0]['p']]
    except Exception as ex:
        # no good peaks, just take the entire range
        xsr = xs
        ysr = ys

    # smooth fit to SD curve
    dl = DataLinearizer(xsr, ysr)

    if fig is not None:
        dl.show(fig)
        minv, maxv = np.min(yscan), np.max(yscan)
        pgeometry.plot2Dline([0, -1, minv], '--c', label='range of scan')
        pgeometry.plot2Dline([0, -1, maxv], '--c', label=None)
#        plt.plot(xsr, linfit(xsr), ':k', label='linear fit')

    return dl, {'peaks': peaks}
示例#2
0
def plot_pinchoff(result, ds=None, fig=10, verbose=1):
    """ Plot result of a pinchoff scan """
    if ds is None:
        ds = qtt.data.get_dataset(result)

    if not result.get('type', 'none') in ['gatesweep', 'pinchoff']:
        raise Exception('calibration result of incorrect type')

    if fig is not None:
        plt.figure(fig)
        plt.clf()
        MatPlot(ds.default_parameter_array(), num=fig)

        lowvalue = result['lowvalue']
        highvalue = result['highvalue']
        pinchoff_point = result['pinchoff_point']
        midpoint = result['midpoint']
        midvalue = result['midvalue']

        plot2Dline([0, -1, lowvalue], '--c', alpha=.5, label='low value')
        plot2Dline([0, -1, highvalue], '--c', alpha=.5, label='high value')

        plot2Dline([-1, 0, midpoint], ':m', linewidth=2, alpha=0.5, label='midpoint')
        if verbose >= 2:
            plt.plot(midpoint, midvalue, '.m', label='midpoint')
        plot2Dline([-1, 0, pinchoff_point], '--g', linewidth=1, alpha=0.5, label='pinchoff_point')
示例#3
0
文件: onedot.py 项目: q4quanta/qtt
def plot_onedot(results, ds=None, verbose=2, fig=100, linecolor='c', ims=None, extentImageMatlab=None, lv=None):
    """ Plot results of a barrier-barrier scan of a single dot

    Args:
        results (dict): results of the onedotGetBalance function
        ds (None or DataSet): dataset to use for plotting
        fig (int or None): figure window to plot to
    """

    if ds is None:
        ds = qtt.data.get_dataset(results)

    if fig is not None:
        _plot_dataset(ds, fig)

        if verbose >= 2:
            pgeometry.plotPoints(results['balancefit'], '--', color=linecolor, linewidth=2, label='balancefit')
        if verbose >= 2:
            pgeometry.plotPoints(results['balancepoint0'], '.r', markersize=13, label='balancepoint0')
        pgeometry.plotPoints(results['balancepoint'], '.m', markersize=17, label='balancepoint')

        if ims is not None:
            qtt.utilities.tools.showImage(ims, extentImageMatlab, fig=fig + 1)  # XX
            plt.axis('image')
            plt.title('Smoothed image')
            pgeometry.plotPoints(results['balancepoint'], '.m', markersize=16, label='balancepoint')

            qtt.utilities.tools.showImage(ims > lv, None, fig=fig + 2)
            pgeometry.plotPoints(results['balancefitpixel'], '--c', markersize=16, label='balancefit')
            pgeometry.plotLabels(results['balancefitpixel'])
            plt.axis('image')
            plt.title('thresholded area')

            if verbose >= 2:
                qq = ims.flatten()
                plt.figure(fig + 3)
                plt.clf()
                plt.hist(qq, 20)
                plot2Dline([-1, 0, np.percentile(ims, 1)], '--m', label='percentile 1')
                plot2Dline([-1, 0, np.percentile(ims, 2)], '--m', label='percentile 2')
                plot2Dline([-1, 0, np.percentile(ims, 99)], '--m', label='percentile 99')
                plot2Dline([-1, 0, lv], '--r', linewidth=2, label='lv')
                plt.legend(numpoints=1)
                plt.title('Histogram of image intensities')
                plt.xlabel('Image (smoothed) values')
示例#4
0
def analysePeaks(x,
                 y,
                 peaks,
                 verbose=1,
                 doplot=0,
                 typicalhalfwidth=None,
                 parameters=None,
                 istep=None):
    """ Analyse Coulomb peaks

    Args:
        x,y: arrays with data
            data in mV
        peaks: list of detected peaks to be analysed
        typicalhalfwidth : float
            typical width of peak (half side) in mV (mV ??)
    """
    if parameters is None:
        parameters = {}
    if istep is not None:
        warnings.warn('ignoring legacy argument istep')

    if typicalhalfwidth is not None:
        raise Exception(
            'please set typicalhalfwidth in the parameters argument')

    typicalhalfwidth = parameters.get('typicalhalfwidth', 13)

    if not issorted(x):
        pass
    if x[0] > x[-1]:
        print('analysePeaks: warning: x values are not sorted!!!!')

    leftp = max(3, x.size / 200)  # ignore all data to the left of this point

    for ii, peak in enumerate(peaks):
        p = peak['p']
        if verbose:
            print('analysePeaks: peak %d: max %.1f' % (ii, peak['y']))

        if p < leftp:
            # discard all measurements to the left of the scan
            peak['valid'] = 0
            peak['xhalf'] = np.NaN
            peak['xhalfl'] = np.NaN
            peak['phalf'] = np.NaN
            continue

        # determine starting points for search of peak
        zi = np.interp(
            [x[p] - 3. * typicalhalfwidth, x[p], x[p] + 3. * typicalhalfwidth],
            x, range(x.size))
        zi = np.round(zi).astype(int)

        zi[0] = max(zi[0], leftp)  # discard points on the left of scan
        zi[1] = max(zi[1], leftp)

        if doplot >= 2:
            plt.plot(x[zi[0]], y[zi[0]], '.g', markersize=11, label='mu-3*thw')
            plt.plot(x[zi[-1]],
                     y[zi[-1]],
                     '.',
                     color=[0, .27, 0],
                     markersize=11,
                     label='mu+3*thw')

        ind = range(zi[0], zi[1])
        if len(ind) == 0:
            if verbose >= 2:
                print('analysePeaks: error? x[p] %f' % x[p])
            peak['valid'] = 0
            peak['xhalf'] = np.NaN
            peak['phalf'] = np.NaN
            continue
        if verbose >= 2:
            print('  peak %d: range to search for half width %d to %d' %
                  (ii, zi[0], zi[1]))
        xl, yl = x[ind], y[ind]

        if 0:
            hminval0 = peak['halfvaluelow']
            fv = peak['y'] - 1.8 * (peak['y'] - peak['halfvaluelow'])
        else:
            hminval0 = .5 * (peak['y'] + np.min(y[ind]))
            fv = peak['y'] - 1.8 * (peak['y'] - hminval0)

        xh = np.interp(hminval0, yl, xl)
        ph = np.interp(hminval0, yl, range(xl.size))
        ph0 = ind[int(ph)]
        xf = np.interp(fv, yl, xl)
        peak['phalf0'] = ph0
        peak['phalfl'] = None

        #peak['indlocal'] = list(ind)

        phalfvalue = np.interp(ph, range(xl.size), yl)
        yhalfl = np.interp(ph, range(xl.size), yl)
        peak['xhalfl'] = xh
        peak['xfoot'] = xf
        peak['yhalfl'] = yhalfl

        if doplot >= 2:
            plt.plot(peak['xhalfl'],
                     yhalfl,
                     '.',
                     color=[1, 1, 0],
                     markersize=11)
            pgeometry.plot2Dline([-1, 0, x[p] - 3 * typicalhalfwidth],
                                 ':c',
                                 label='3*thw')
            pgeometry.plot2Dline([-1, 0, peak['xfoot']], ':y', label='xfoot')

        pratio = np.abs(phalfvalue - peak['y']) / (-peak['halfvaluelow'] +
                                                   peak['y'])
        if verbose >= 2:
            print('  paratio %.2f' % pratio)

        if verbose >= 2:
            print(
                np.abs(phalfvalue - peak['y']) /
                (-peak['halfvaluelow'] + peak['y']))
        if pratio > .1:
            peak['valid'] = peak['valid']
        else:
            peak['valid'] = 0

        if verbose:
            print('   peak %d: valid %d' % (ii, peak['valid']))
    return peaks
示例#5
0
def peakFindBottom(x, y, peaks, fig=None, verbose=1):
    """ Find the left bottom of a detected peak

    Args:
        x (array): independent variable data
        y (array): signal data
        peaks (list): list of detected peaks
        fig (None or int): if integer, then plot results
        verbose (int): verbosity level
    """
    kk = np.ones(3) / 3.
    ys = scipy.ndimage.filters.correlate1d(y, kk, mode='nearest')
    peaks = copy.deepcopy(peaks)

    dy = np.diff(ys, n=1)
    dy = np.hstack((dy, [0]))
    for ii, peak in enumerate(peaks):
        if verbose:
            print('peakFindBottom: peak %d' % ii)

        if not peak['valid']:
            continue
        ind = range(peak['phalf0'])

        left_of_peak = 0 * y.copy()
        left_of_peak[ind] = 1
        r = range(y.size)
        left_of_peak_and_decreasing = left_of_peak * (
            dy < 0)  # set w to zero where the scan is increasing
        left_of_peak_and_decreasing[
            0] = 1  # make sure to stop at the left end of the scan...

        ww = left_of_peak_and_decreasing.nonzero()[0]
        if verbose >= 2:
            print('  peakFindBottom: size of decreasing area %d' % ww.size)

        if ww.size == 0:
            if peak['valid']:
                peak['valid'] = 0
                peak['validreason'] = 'peakFindBottom'
                if verbose >= 2:
                    print('peakFindBottom: invalid peak')
                    print(ind)
                    print(dy)
            continue
        bidx = ww[-1]
        peak['pbottomlow'] = bidx

        w = left_of_peak * (dy > 0)  # we need to be rising
        # we need to be above 10% of absolute low value
        w = w * ((ys) < ys[bidx] + .1 * (ys[peak['p']] - ys[bidx]))
        w = w * (r >= peak['pbottomlow'])
        ww = w.nonzero()[0]
        if ww.size == 0:
            if peak['valid']:
                peak['valid'] = 0
                peak['validreason'] = 'peakFindBottom'
                if verbose >= 2:
                    print('peakFindBottom: invalid peak (%s)' %
                          ('rising part ww.size == 0', ))
                    print(w)
                    print(ys)
            continue
        bidx = ww[-1]

        peak['pbottom'] = bidx
        peak['pbottoml'] = bidx
        peak['xbottom'] = x[bidx]
        peak['xbottoml'] = x[bidx]
        peak['vbottom'] = y[bidx]  # legacy
        peak['ybottoml'] = y[bidx]

        if verbose >= 3:
            plt.figure(53)
            plt.clf()
            plt.plot(x[ind], 0 * np.array(ind) + 1, '.b', label='ind')
            plt.plot(x[range(y.size)], w, 'or', label='w')
            plt.plot(x[range(y.size)],
                     dy < 0,
                     'dg',
                     markersize=12,
                     label='dy<0')
            pgeometry.enlargelims()
            pgeometry.plot2Dline([-1, 0, peak['x']], '--c', label='x')
            pgeometry.plot2Dline([-1, 0, x[peak['phalf0']]],
                                 '--y',
                                 label='phalf0')

            pgeometry.plot2Dline([-1, 0, x[peak['pbottomlow']]],
                                 ':k',
                                 label='pbottomlow')

            pgeometry.plot2Dline([-1, 0, peak['xbottoml']],
                                 '--y',
                                 label='xbottoml')

            plt.legend(loc=0)

    return peaks
示例#6
0
def analyseGateSweep(dd, fig=None, minthr=None, maxthr=None, verbose=1, drawsmoothed=True, drawmidpoints=True):
    """ Analyse sweep of a gate for pinch value, low value and high value

    Args:
        dd (1D qcodes DataSet): structure containing the scan data
        minthr, maxthr : float
            parameters for the algorithm (default: None)

    Returns:
        result (dict): dictionary with analysis results
    """

    goodgate = True

    data = dd
    XX = None

    # should be made generic
    setpoint_name = [x for x in list(data.arrays.keys()) if not x.endswith('amplitude') and getattr(data, x).is_setpoint][0]
    value_parameter_name = data.default_parameter_name()  # e.g. 'amplitude'

    x = data.arrays[setpoint_name]
    value = np.array(data.arrays[value_parameter_name])

    # detect direction of scan
    scandirection = np.sign(x[-1] - x[0])
    if scandirection < 0:
        scandirection = 1
        x = x[::-1]
        value = value[::-1]


    # crude estimate of noise
    noise = np.nanpercentile(np.abs(np.diff(value)), 50)
    lowvalue = np.nanpercentile(value, 1)
    highvalue = np.nanpercentile(value, 90)
    # sometimes a channel is almost completely closed, then the percentile
    # approach does not function well
    ww = value[value >= (lowvalue + highvalue) / 2]
    highvalue = np.nanpercentile(ww, 90)

    if verbose >= 2:
        print('analyseGateSweep: lowvalue %.1f highvalue %.1f' %
              (lowvalue, highvalue))
    d = highvalue - lowvalue

    vv1 = value > (lowvalue + .2 * d)
    vv2 = value < (lowvalue + .8 * d)
    midpoint1 = vv1 * vv2
    ww = midpoint1.nonzero()[0]
    midpoint1 = np.mean(x[ww])

    # smooth signal
    kk = np.ones(3) / 3.
    ww = value
    for ii in range(4):
        ww = scipy.ndimage.filters.correlate1d(ww, kk, mode='nearest')
        # ww=scipy.signal.convolve(ww, kk, mode='same')
        # ww=scipy.signal.convolve2d(ww, kk, mode='same', boundary='symm')
    midvalue = .7 * lowvalue + .3 * highvalue
    if scandirection >= 0:
        mp = (ww >= (.7 * lowvalue + .3 * highvalue)).nonzero()[0][0]
    else:
        mp = (ww >= (.7 * lowvalue + .3 * highvalue)).nonzero()[0][-1]
    mp = max(mp, 2)  # fix for case with zero data signal
    midpoint2 = x[mp]

    if verbose >= 2:
        print('analyseGateSweep: midpoint2 %.1f midpoint1 %.1f' %
              (midpoint2, midpoint1))

    if minthr is not None and np.abs(lowvalue) > minthr:
        if verbose:
            print('analyseGateSweep: gate not good: gate is not closed')
            print(' minthr %s' % minthr)
        midpoint1 = np.percentile(x, .5)
        midpoint2 = np.percentile(x, .5)

        goodgate = False

    # check for gates that are fully open or closed
    if scandirection > 0:
        xleft = x[0:mp]
        leftval = ww[0:mp]
        rightval = ww[mp]
    else:
        xleft = x[mp:]
        leftval = ww[mp:]
        rightval = ww[0:mp]
    st = ww.std()
    if verbose>=2:
        print('analyseGateSweep: leftval %.2f, rightval %.2f' %
              (leftval.mean(), rightval.mean()))
    if goodgate and (rightval.mean() - leftval.mean() < .3 * st):
        if verbose:
            print(
                'analyseGateSweep: gate not good: gate is not closed (or fully closed)')
        midpoint1 = np.percentile(x, .5)
        midpoint2 = np.percentile(x, .5)
        goodgate = False

    # fit a polynomial to the left side
    if goodgate and leftval.size > 5:
        # TODO: make this a robust fit
        fit = np.polyfit(xleft, leftval, 1)
        pp = np.polyval(fit, xleft)

        # pmid = np.polyval(fit, midpoint2)
        p0 = np.polyval(fit, xleft[-1])
        pmid = np.polyval(fit, xleft[0])
        if verbose>=2:
            print('analyseGateSweep: p0 %.1f, pmid %.1f, leftval[0] %.1f' % (p0, pmid, leftval[0]))

        if pmid + (pmid - p0) * .25 > leftval[0]:
            midpoint1 = np.percentile(x, .5)
            midpoint2 = np.percentile(x, .5)
            goodgate = False
            if verbose:
                print(
                    'analyseGateSweep: gate not good: gate is not closed (or fully closed) (line fit check)')

    # another check on closed gates
    if scandirection > 0:
        leftidx = range(0, mp)
        fitleft = np.polyfit(
            [x[leftidx[0]], x[leftidx[-1]]], [lowvalue, value[leftidx[-1]]], 1)
    else:
        leftidx = range(mp, value.shape[0])
        fitleft = np.polyfit(
            [x[leftidx[-1]], x[leftidx[0]]], [lowvalue, value[leftidx[0]]], 1)
    leftval = value[leftidx]

    leftpred = np.polyval(fitleft, x[leftidx])

    if np.abs(scandirection * (xleft[1] - xleft[0])) > 150 and xleft.size > 15:
        xleft0 = x[mp + 6:]
        leftval0 = ww[mp + 6:]
        fitL = np.polyfit(xleft0, leftval0, 1)
        pp = np.polyval(fitL, xleft0)
        nd = fitL[0] / (highvalue - lowvalue)
        if goodgate and (nd * 750 > 1):
            midpoint1 = np.percentile(x, .5)
            midpoint2 = np.percentile(x, .5)
            goodgate = False
            if verbose:
                print('analyseGateSweep: gate not good: gate is not closed (or fully closed) (slope check)')
            pass

    if np.mean(leftval - leftpred) > noise:
        midpoint1 = np.percentile(x, .5)
        midpoint2 = np.percentile(x, .5)
        goodgate = False
        if verbose:
            print(
                'analyseGateSweep: gate not good: gate is not closed (or fully closed) (left region check)')
        pass

    if fig is not None:
        cfigure(fig)
        plt.clf()
        plt.plot(x, value, '.-b', linewidth=2)
        plt.xlabel('Sweep %s [mV]' % setpoint_name, fontsize=14)
        plt.ylabel('keithley [pA]', fontsize=14)

        if drawsmoothed:
            plt.plot(x, ww, '-g', linewidth=1)

        plot2Dline([0, -1, lowvalue], '--m', label='low value')
        plot2Dline([0, -1, highvalue], '--m', label='high value')

        if drawmidpoints:
            if verbose >= 2:
                plot2Dline([-1, 0, midpoint1], '--g', linewidth=1)
            plot2Dline([-1, 0, midpoint2], '--m', linewidth=2)

        if verbose >= 2:
            plt.plot(x[leftidx], leftpred, '--r', markersize=15, linewidth=1, label='leftpred')
            plt.plot(x[leftidx], leftval, '--m', markersize=15, linewidth=1, label='leftval')


    adata = dict({'description': 'pinchoff analysis', 'pinchvalue': 'use pinchoff_point instead',
                  '_pinchvalueX': midpoint1 - 50, 'goodgate': goodgate})
    adata['lowvalue'] = lowvalue
    adata['highvalue'] = highvalue
    adata['xlabel'] = 'Sweep %s [mV]' % setpoint_name
    adata['pinchoff_point'] = midpoint2 - 50
    pinchoff_index = np.interp(-70.5, x, np.arange(x.size) )
    adata['pinchoff_value'] = value[int(pinchoff_index)]
    adata['midpoint'] = float(midpoint2)
    adata['midvalue'] = midvalue
    adata['dataset']=dd.location
    adata['type']='gatesweep'

    if verbose>=1:
        print('analyseGateSweep: pinch-off point %.3f, value %.3f' % (adata['midpoint'], adata['midvalue']) )

    if verbose >= 2:
        print('analyseGateSweep: gate status %d: pinchvalue %.1f' %
              (goodgate, adata['pinchoff_point']))
        adata['Xsmooth'] = ww
        adata['XX'] = XX
        adata['X'] = value
        adata['x'] = x
        adata['ww'] = ww
        adata['_mp'] = mp

    return adata
示例#7
0
    noise.sort()

    pp = np.zeros((len(noise), 6))
    for ii, n in enumerate(noise):
        pgeometry.tprint('quick fit %d/%d' % (ii, len(noise)))
        yyx = yy + n * (np.random.rand(yy.size) - .5)
        parfit, _, _ = fit_pol_all(xx, yyx, kT=0.001, par_guess=None)
        pp[ii] = parfit

    plt.figure(200)
    plt.clf()
    plt.plot(noise, pp[:, 0], '.b', label='tunnel coupling')
    plt.xlabel('Noise')
    plt.ylabel('Estimated tunnel frequency')

    pgeometry.plot2Dline([0, -1, par[0]], '--c', label='true value')

    # %% Show effect of proper initialization
    yyx = yy + n * (np.random.rand(yy.size) - .5)
    parfit1, _, _ = fit_pol_all(xx, yyx, kT=0.001, par_guess=par)
    parfit2, _, _ = fit_pol_all(xx, yyx, kT=0.001, par_guess=None, verbose=2)
    parfit2i, _, _ = fit_pol_all(xx, yyx, kT=0.001, par_guess=parfit2)

    yy1 = polmod_all_2slopes(xx, parfit1, kT=0.001)
    yy2 = polmod_all_2slopes(xx, parfit2, kT=0.001)
    yy2i = polmod_all_2slopes(xx, parfit2i, kT=0.001)

    c0 = polweight_all_2slopes(xx, yy, par, kT=0.001)
    c1 = polweight_all_2slopes(xx, yy1, par, kT=0.001)
    c2 = polweight_all_2slopes(xx, yy2, par, kT=0.001)
    c2i = polweight_all_2slopes(xx, yy2i, par, kT=0.001)
示例#8
0
文件: coulomb.py 项目: keaton217/qtt
def peakFindBottom(x, y, peaks, fig=None, verbose=1):
    """ Find the left bottom of a detected peak """
    kk = np.ones(3) / 3.
    ys = scipy.ndimage.filters.correlate1d(y, kk, mode='nearest')
    peaks = copy.deepcopy(peaks)

    dy = np.diff(ys, n=1)
    dy = np.hstack((dy, [0]))
    for ii, peak in enumerate(peaks):
        if verbose:
            print('peakFindBottom: peak %d' % ii)

        if not peak['valid']:
            continue
        ind = range(peak['phalf0'])

        w0 = 0 * y.copy()
        w0[ind] = 1
        r = range(y.size)
        w = w0 * (dy < 0)  # set w to zero where the scan is increasing
        w[0] = 1  # make sure to stop at the left end of the scan...

        ww = w.nonzero()[0]
        if verbose >= 2:
            print('  peakFindBottom: ww.size %d' % ww.size)

        if ww.size == 0:
            if peak['valid']:
                peak['valid'] = 0
                peak['validreason'] = 'peakFindBottom'
                if verbose >= 2:
                    print('peakFindBottom: invalid peak')
                    print(ind)
                    print(dy)
            continue
        bidx = ww[-1]
        peak['pbottomlow'] = bidx

        w = w0 * (dy > 0)   # we need to be rising
        # we need to be above 10% of absolute low value
        w = w * ((ys) < ys[bidx] + .1 * (ys[peak['p']] - ys[bidx]))
        w = w * (r >= peak['pbottomlow'])
        ww = w.nonzero()[0]
        if ww.size == 0:
            if peak['valid']:
                peak['valid'] = 0
                peak['validreason'] = 'peakFindBottom'
                if verbose >= 2:
                    print('peakFindBottom: invalid peak (%s)' % ('rising part ww.size == 0', ))
                    print(w)
                    print(ys)
 #                   print(w)
            continue
        bidx = ww[-1]

        peak['pbottom'] = bidx
        peak['pbottoml'] = bidx
        peak['xbottom'] = x[bidx]
        peak['xbottoml'] = x[bidx]
        peak['vbottom'] = y[bidx]  # legacy
        peak['ybottoml'] = y[bidx]

        if verbose >= 3:
            # for debugging
            plt.figure(53)
            plt.clf()
            plt.plot(x[ind], 0 * np.array(ind) + 1, '.b', label='ind')
            plt.plot(x[range(y.size)], w, 'or', label='w')
            plt.plot(x[range(y.size)], dy < 0, 'dg',
                     markersize=12, label='dy<0')
            pmatlab.enlargelims()
            pmatlab.plot2Dline([-1, 0, peak['x']], '--c', label='x')
            pmatlab.plot2Dline([-1, 0, x[peak['phalf0']]],
                               '--y', label='phalf0')

            pmatlab.plot2Dline([-1, 0, x[peak['pbottomlow']]],
                               ':k', label='pbottomlow')

            pmatlab.plot2Dline([-1, 0, peak['xbottoml']],
                               '--y', label='xbottoml')

            plt.legend(loc=0)

    return peaks