View Raw SPL
/*****************************************************************************
*                                                                            *
*   XCORR.SPL   Copyright (C) 2000-2017 DSP Development Corporation          *
*                               All Rights Reserved                          *
*                                                                            *
*   Author:     Randy Race                                                   *
*                                                                            *
*   Synopsis:   Cross-correlation using convolution                          *
*                                                                            *
*   Revisions:   2 May 2000  RRR  Creation - from FREQ.MAC                   *
*               29 Nov 2009  RRR  conv argument reversal                     *
*               26 Apr 2017  RRR  matrix support                             *
*                                                                            *
*****************************************************************************/


#if @HELP_XCORR

    XCORR

    Purpose: Cross correlation using the convolution method

    Syntax:  XCORR(s1, s2, norm)

                 s1 - input series

                 s2 - input series

               norm - optional integer, normalization method,

                      0: None,
                      1: Unity (-1 to 1)
                      2: Biased
                      3: Unbiased

                      defaults to 0: None

    Returns: A series

    Example:
             W1: gsin(1000, .001, 4)
             W2: gsin(1000, .001, 4)
             W3: xcorr(w1, w2)

             Performs the cross correlation of two sinewaves. The
             peaks of the result indicate the two waveforms are
             very similar at the time intervals where the peaks
             occur.

    Example:
             W1: gsin(1000, .001, 4)
             W2: gnorm(1000, .001)
             W3: xcorr(w1, w1, 1)
             W4: xcorr(w1, w2, 1)

             W3 displays the cross correlation of a sinewave normalized
             to -1 and 1. W4 shows the cross correlation between a sinewave
             and random noise. The normalized maximum of W3 is 1.0, indicating
             perfect correlation at time t == 0. Although the waveform of
             W4 displays some peaks, the normalized maximum is roughly 0.04
             indicating little correlation between W1 and W2. For
             a graphical representation, overplot W4 in W3.

    Remarks:
             The cross-correlation is used to determine how similar two
             series are to each other. XCORR performs correlation by
             computing the direct convolution of the input series.

             The output length L is:

                      L = length(s1) + length(s2) - 1

             For series of equal lengths and offsets, the zeroth lag
             component is the mid point of the series.

             The BIASED normalization divides the result by M, the
             maximum length of the input series.

             The UNBIASED normalization divides the result by

                              M - abs(M - i - 1) + 1

             where i is the index of the result.

             See FXCORR for the frequency domain implementation.


    See Also:
             Acorr
             Conv
             Facorr
             Fconv
             Fxcorr
#endif


/* time domain cross correlation */
xcorr(s1, s2, norm)
{
        local len, maxlen, t, dx, j, s3, u, snorm;

        if (argc < 3)
        {
                if (argc < 2) 
                {
                        if (argc < 1) 
                        {
                                error("xcorr - input series required");
                        }

                        s2 = refseries(s1);
                }

                if (isarray(s2))
                {
                        norm = 0;
                }
                else
                {
                        norm = s2;
                        s2   = refseries(s1);
                }
        }

        if (isstring(norm))
        {
                switch(tolower(norm))
                {
                        case "coeff":
                        case "unity":
                                norm = 1;
                                break;

                        case "biased":
                                norm = 2;
                                break;

                        case "unbiased":
                                norm = 3;
                                break;

                        default:
                                norm = 0;
                                break;
                }
        }

        if (numcols(s1) > 1 && numcols(s2) > 1)
        {
                /* all correlations column-wise */
                t  = {};
                s3 = conj(rev(s2));

                if (norm == 1)
                {
                        /* unity normalization for each column */
                        snorm = colsum(s2 * s2);

                        loop (j = 1.. numcols(s1))
                        {
                                u = conv(col(s1, j), s3);
                                u = u / sqrt(colsum(col(s1, j)^2) * snorm);

                                t = ravel(t, u);
                        }
                }
                else
                {
                        loop (j = 1..numcols(s1))
                        {
                                t = ravel(t, conv(col(s1, j), s3));
                        }

                        t = xcorr_norm(t, s1, s2, norm);
                }
        }
        else
        {
                /* perform correlation in the time domain */
                t = conv(s1, conj(rev(s2)));

                /* normalization */
                t = xcorr_norm(t, s1, s2, norm);
        }

        /* all done */
        return(t);
}


xcorr_norm(t, s1, s2, norm)
{
        local dx, maxlen;

        /* normalization */
        switch (norm)
        {
                case 0:        /* none */
                default:
                        break;

                case 1: /* -1 to +1 */
                        t = t / sqrt(colsum(s1 * s1) * colsum(s2 * s2));
                        break;

                case 2: /* biased */
                        maxlen = max({collength(s1), collength(s2)});
                        t = t / maxlen;
                        break;

                case 3: /* unbiased */
                        dx = deltax(t);
                        maxlen = max({length(s1), length(s2)});
                        t = t / ({1..maxlen, (maxlen - 1)..-1..1});

                        /* preserve original deltax */
                        setdeltax(t, dx);
                        break;
        }

        return(t);
}