def test_CyclicIndexes():
    # We define a vector that has three cycle changes directions three times
    x1 = np.linspace(0, 1, 21)
    x2 = np.linspace(1, -1, 21)
    x3 = np.linspace(-1, 3, 21)
    x = np.concatenate([x1, x2, x3])

    revesalIndexes = opd.GetCycleIndicies(x)

    assert np.all(revesalIndexes == np.array([0, 20, 41, 62]))
def triangles():
    np.random.seed(19991231)

    x = np.linspace(0, 10, 1000)
    # a triangle with small reversals
    triangleBig = scipy.signal.sawtooth(x * 2, 0.5)
    triangleSmall = scipy.signal.sawtooth(x * 20, 0.5) / 7
    triangle = triangleBig + triangleSmall
    triangleIndexes = opd.GetCycleIndicies(triangle,
                                           VectorY=triangle,
                                           CreatePlot=True,
                                           peakDist=200,
                                           peakProminence=0.1)
    return triangleIndexes
def noise():
    np.random.seed(19991231)

    x = np.linspace(0, 10, 1000)
    triangleBig = scipy.signal.sawtooth(x * 2, 0.5)  # a noisey triangle signla
    permutate = np.random.normal(0, 1, 1000) / 2
    Ynoise = triangleBig + permutate
    Ynoise = scipy.signal.savgol_filter(Ynoise, 53, 2)

    noiseIndexes = opd.GetCycleIndicies(Ynoise,
                                        VectorY=Ynoise,
                                        CreatePlot=True,
                                        peakDist=20,
                                        peakProminence=0.2)

    return noiseIndexes
def CyclicIndexes2():
    x1 = np.linspace(0, 1, 21)
    x2 = np.linspace(1, -1, 21)
    x3 = np.linspace(-1, 3, 21)
    x = np.concatenate([x1, x2, x3])

    y1 = np.sin(x1)
    y2 = np.e**x2 * np.sin(x2)
    y3 = np.e**-x3 * np.sin(x3)
    y = np.concatenate([y1, y2, y3])

    revesalIndexes = opd.GetCycleIndicies(x, VectorY=y, CreatePlot=True)

    plt.close('all')

    return revesalIndexes
def sub_vector():
    np.random.seed(19991231)

    # get the sub vectors
    x = np.linspace(0, 10, 1000)
    triangleBig = scipy.signal.sawtooth(x * 2, 0.5)  # a noisey triangle signla
    permutate = np.random.normal(0, 1, 1000) / 2
    Ynoise = triangleBig + permutate
    Ynoise = scipy.signal.savgol_filter(Ynoise, 53, 2)
    noiseIndexes = opd.GetCycleIndicies(Ynoise,
                                        VectorY=Ynoise,
                                        CreatePlot=True,
                                        peakDist=20,
                                        peakProminence=0.2)

    [subvectorx1,
     subvectory1] = opd.GetCycleSubVector(x, Ynoise, noiseIndexes[0],
                                          noiseIndexes[1], 100)
    # [subvectorx2, subvectory2] = opd.GetCycleSubVector(x, Ynoise, noiseIndexes[1],noiseIndexes[2], 100)
    # [subvectorx3, subvectory3] = opd.GetCycleSubVector(x, Ynoise, noiseIndexes[2],noiseIndexes[3], 100)

    return subvectorx1[55], subvectory1[23]
def AnimateCyclicXYCurves(Curve1,
                          Curve2,
                          NFrames=120,
                          fps=24,
                          peakDist=10,
                          Xbound=[],
                          Ybound=[]):
    """
    This functions plots two curves, allowing us to compare experimental and 
    non-experiemntal.


    Parameters
    ----------
    Curve1 : TYPE
        the first curve to animate.
    Curve2 : TYPE
        the second curve to animate.
    NFrames : TYPE, optional
        Number of frames between cycles in the animation. The default is 48.
    fps : TYPE, optional
        The number of frames per second. The default is 24.
    peakDist : TYPE, optional
        This is used to find X direction reversals. We look for peaks within 
        this distance. The default is 10.
    Xbound : [xmin, xmax], optional
        A custome bound on the graphs xlimits. The default is [].
    Ybound : [xmin, xmax], optional
        A custome bound on the graphs ylimits. The default is [].


    """

    # Detect reversal points
    curve1Indicies = D.GetCycleIndicies(Curve1[:, 0],
                                        Curve1[:, 1],
                                        peakDist=10)
    curve2Indicies = D.GetCycleIndicies(Curve2[:, 0],
                                        Curve2[:, 1],
                                        peakDist=20)

    # Define the number of cycles, i.e. the number of indexes in the range
    Ncycles = len(curve1Indicies) - 1
    Ncycles_2 = len(curve2Indicies) - 1

    if Ncycles != Ncycles_2:
        raise Exception(
            'The experiment and Analysis have a different number of cycles')

    Nsteps = Ncycles * NFrames

    # Initialize animation curve
    animationCurve1 = np.zeros([Nsteps, 2])
    animationCurve2 = np.zeros([Nsteps, 2])

    # Create the animation curve for the
    for ii in range(Ncycles):

        [Ex, Ey] = D.GetCycleSubVector(Curve1[:, 0], Curve1[:, 1],
                                       curve1Indicies[ii],
                                       curve1Indicies[ii + 1], NFrames)
        [Ax, Ay] = D.GetCycleSubVector(Curve2[:, 0], Curve2[:, 1],
                                       curve2Indicies[ii],
                                       curve2Indicies[ii + 1], NFrames)

        animationCurve1[ii * NFrames:(ii + 1) * NFrames, 0] = Ex
        animationCurve1[ii * NFrames:(ii + 1) * NFrames, 1] = Ey

        animationCurve2[ii * NFrames:(ii + 1) * NFrames, 0] = Ax
        animationCurve2[ii * NFrames:(ii + 1) * NFrames, 1] = Ay

    if Xbound == []:
        xmin = 1.1 * np.min([animationCurve1[:, 0], animationCurve2[:, 0]])
        xmax = 1.1 * np.max([animationCurve1[:, 0], animationCurve2[:, 0]])
    else:
        xmin = Xbound[0]
        xmax = Xbound[1]
    if Ybound == []:
        ymin = 1.1 * np.min([animationCurve1[:, 1], animationCurve2[:, 1]])
        ymax = 1.1 * np.max([animationCurve1[:, 1], animationCurve2[:, 1]])
    else:
        ymin = Ybound[0]
        ymax = Ybound[1]

    animationCurve1 = animationCurve1.T
    animationCurve2 = animationCurve2.T

    outputFrames = Nsteps

    # Initialize the plot
    fig1 = plt.figure()
    line, = plt.plot(
        [],
        [],
    )
    line2, = plt.plot([], [], 'bo')
    line3, = plt.plot(
        [],
        [],
    )
    line4, = plt.plot([], [], 'ro')
    plt.xlim(xmin, xmax)
    plt.ylim(ymin, ymax)

    # Define the update function
    def update_line(time, animationCurve1, animationCurve2, line, line2, line3,
                    line4):

        currentData1 = animationCurve1[..., :(time + 1)]
        currentData2 = animationCurve2[..., :(time + 1)]

        line.set_data(currentData1)
        [x1, y1] = currentData1[:, -1]
        line2.set_data(x1, y1)

        line3.set_data(currentData2)
        [x2, y2] = currentData2[:, -1]
        line4.set_data(x2, y2)

        return line, line2, line3, line4

    ani = animation.FuncAnimation(fig1,
                                  update_line,
                                  outputFrames,
                                  fargs=(animationCurve1, animationCurve2,
                                         line, line2, line3, line4),
                                  interval=1000 / fps,
                                  blit=True)

    return ani
# =============================================================================
# Find Index for cycle reversal points some input data
# =============================================================================
"""
This is useful for finding data reversal points in our data. If your data is 
noisey, you may want to skip some sub-peaks and only find big reversals.
"""

# We define a vector that has three cycle changes directions three times
x1 = np.linspace(0, 1, 21)
x2 = np.linspace(1, -1, 21)
x3 = np.linspace(-1, 3, 21)
x = np.concatenate([x1, x2, x3])

revesalIndexes = opd.GetCycleIndicies(x)

# We can also plot the to make sure it worked. We'll define a y vector for
# illustrative purposes.
y1 = np.sin(x1)
y2 = np.e**x2 * np.sin(x2)
y3 = np.e**-x3 * np.sin(x3)
y = np.concatenate([y1, y2, y3])

revesalIndexes = opd.GetCycleIndicies(x, VectorY=y, CreatePlot=True)

# we can also find the reversal point for more tricky curves
# We can also skip small revsals. y is used only for illustrative purposes
x = np.linspace(0, 10, 1000)

# a triangle with small reversals