signal_merge.h

Back to Signal class

tim/core/

// Description: Merging signals into higher-dimensional signals

#ifndef TIM_SIGNAL_MERGE_H
#define TIM_SIGNAL_MERGE_H

#include "tim/core/signal.h"
#include "tim/core/signal_properties.h"

#include <pastel/sys/range.h>
#include <pastel/sys/array/array.h>

namespace Tim
{

    //! Merges a signal set into a higher-dimensional signal.
    template <
        ranges::forward_range Signal_Range,
        ranges::forward_range Lag_Range>
    SignalData merge(
        const Signal_Range& signalSet,
        const Lag_Range& lagSet)
    {
        ENSURE_OP(ranges::size(signalSet), ==, ranges::size(lagSet));

        if (ranges::empty(signalSet) ||
            ranges::empty(lagSet))
        {
            return SignalData();
        }

        // Compute joint dimension.

        integer jointDimension = 0;
        for (auto&& signal : signalSet) {
            jointDimension += signal.dimension();
        }

        Integer2 sharedTime = 
            sharedTimeInterval(signalSet, lagSet);
        integer samples = sharedTime[1] - sharedTime[0];

        // Allocate the joint signal.

        SignalData jointSignal(jointDimension, samples, sharedTime[0]);

        if  (samples == 0)
        {
            // There is no common time interval that
            // all signals would share.
            return jointSignal;
        }

        // Copy the signals into parts of the joint signal.

        integer dimensionOffset = 0;

        auto signalIter = std::begin(signalSet);
        for (integer lag : lagSet) 
        {
            const Signal& signal = *signalIter;
            const integer lagOffset = sharedTime[0] - (signal.t() + lag);
            integer dimension = signal.dimension();

            auto jointSliced = jointSignal.data().slicey(dimensionOffset);

            for (integer i = 0;i < samples;++i)
            {
                ranges::copy(
                    signal.data().columnRange(i + lagOffset),
                    std::begin(jointSliced.columnRange(i)));
            }

            dimensionOffset += dimension;

            ++signalIter;
        }

        return jointSignal;
    }

    //! Merges a signal set into a higher-dimensional signal.
    /*!
   This is a convenience function that calls:
   merge(signalSet, constantRange(0, ranges::size(signalSet)));
   See the documentation for that function.
   */
    template <ranges::forward_range Signal_Range>
    SignalData merge(
        const Signal_Range& signalSet)
    {
        return Tim::merge(signalSet,
            constantRange(0, ranges::size(signalSet)));
    }

    //! Merges two signal sets pairwise into a new signal set.
    template <
        typename X_Signal_Range,
        typename Y_Signal_Range,
        typename Signal_OutputIterator>
    void merge(
        const X_Signal_Range& xSignalSet,
        const Y_Signal_Range& ySignalSet,
        Signal_OutputIterator result,
        integer xLag = 0, integer yLag = 0)
    {
        ENSURE_OP(ranges::size(xSignalSet), ==, ranges::size(ySignalSet));

        auto xIter = std::begin(xSignalSet);
        auto xIterEnd = std::end(xSignalSet);
        auto yIter = std::begin(ySignalSet);

        while(xIter != xIterEnd)
        {
            *result = merge(*xIter, *yIter, xLag, yLag);

            ++result;
            ++xIter;
            ++yIter;
        }
    }

    //! Merges signals into a single high-dimensional signal.
    /*!
   Preconditions:
   ranges::size(lagSet) == ensembleSet.height()

   ensembleSet:
   An array where each row contains trials of one signal.
   The signals in the same column must correspond in time.

   result:
   An iterator where the merged signal trials are written.

   lagSet:
   For each row of 'ensembleSet', a lag giving the
   delay in samples to apply to the signal in that
   row before the merging.
   */
    template <
        typename Signal_OutputIterator,
        typename Lag_Range>
    void merge(
        const Array<Signal>& ensembleSet,
        Signal_OutputIterator result,
        const Lag_Range& lagSet)
    {
        ENSURE_OP(ranges::size(lagSet), ==, ensembleSet.height());

        std::vector<Signal> column(ensembleSet.height());

        integer trials = ensembleSet.width();
        for (integer i = 0;i < trials;++i)
        {
            for (integer j = 0;j < ensembleSet.height(); ++j) {
                column[j] = (Signal)ensembleSet(i, j);
            }

            *result = merge(column, lagSet);
            ++result;
        }
    }

    //! Merges signals into a single high-dimensional signal.
    /*!
   This is a convenience function that calls:
   merge(ensembleSet, result,
       constantRange(0, ensembleSet.height()));
   */
    template <typename Signal_OutputIterator>
    void merge(
        const Array<Signal>& ensembleSet,
        Signal_OutputIterator result)
    {
        Tim::merge(ensembleSet, result,
            constantRange(0, ensembleSet.height()));
    }

    //! Merges two signals into a higher-dimensional signal.
    inline TIM SignalData merge(
        const Signal& xSignal,
        const Signal& ySignal,
        integer xLag = 0,
        integer yLag = 0)
    {
        return Tim::merge(
            range({ xSignal , ySignal }),
            range({ xLag, yLag }));
    }

}

#endif