2
votes

I'm trying to combine three signal waveforms into a single, interleaved waveform. I need to know the best way to do it in C++ STL. Better solutions would use as much C++ STL style as possible, avoid redundant code, etc. Is there some STL "tuple" type class that would do this for me? I need contiguous storage at all times for backward compatibility with other code (therefore, vector). The best solution would be correct and easy to understand. Space and speed are not as high of a priority as correctness and ease of understanding.

The output waveform must be ordered like this: first sample from first channel, first sample from second channel, first sample from third channel, then continue with the second sample from each channel and repeat for all samples. I know that all three input waveforms have the same number of samples.

In Matlab, I would have done it like this:

function outputWaveform=Interleave3(a, b, c)
outputWaveform=zeros([1 3*length(a)]);
outputWaveform(1:3:end)=a(:);
outputWaveform(2:3:end)=b(:);
outputWaveform(3:3:end)=c(:);

This is my first C++ STL attempt:

typedef vector<double>  dVector;
typedef vector<double>::iterator  dVectorIT;
dVector Interleave3(dVector a, dVector b, dVector c)
{
    dVector result(0, 3*a.size());
    dVectorIT aIT=a.begin(), bIT=b.begin(), cIT=c.begin(), rIT=result.begin();
    for(; aIT != a.end(); ++aIT, ++bIT, ++cIT)
    {
        *rIT++=*aIT;
        *rIT++=*bIT;
        *rIT++=*cIT;
    }
    return  result;
}

It works, but is there a better way to do this? I hoped there might be some clever way to do it in one line with transform(). Can you append b to a, then c to a, then transform the temporary "a1a2a3...-b1b2b3...-c1c2c3..." vector into "a1b1c1a2b2c2a3b3c3..."?

Bonus question: I also need the inverse operation (to split an output waveform of 3*N samples into 3 vectors of N samples each). The Matlab solution is quite easy:

function [a, b, c]=Deinterleave3(outputWaveform)
a=outputWaveform(1:3:end);
b=outputWaveform(2:3:end);
c=outputWaveform(3:3:end);

C++ STL seems fairly awkward, and I bet there's a better way to do it than this:

typedef vector<double>  dVector;
typedef vector<double>::iterator  dVectorIT;
void Deinterleave3(dVector outputWaveform, dVector &a, dVector &b, dVector &c)
{
    ASSERT( !(outputWaveform.size()%3) );
    a.clear(); b.clear(); c.clear();
    dVectorIT oIT=outputWaveform.begin();
    for(; oIT != outputWaveform.end(); )
    {
        a.push_back( *oIT++ );
        b.push_back( *oIT++ );
        c.push_back( *oIT++ );
    }
}

Is there some clever combination of transform() and back_inserter() which would do the inverse operation? Again, using a temporary vector would be acceptable.

Boost has a "zip iterator," but I can't figure out if it would perform either the interleaving or deinterleaving operation.

EDIT: fixed the missing angle brackets (<>). The HTML filter ate them! Also, I have a new idea about how to fix this with a custom iterator.

2
Couldn't I define a custom iterator that incremented by 3 instead of 1? I tried but failed to make a generic iterator with a private member variable for the number of values to skip (3) and the total number of values. When it reached the end, it would wrap around. E.g., 51 elements would reference elements in this order [0], [3],[6]...[48],[1],[4],...user244795
The zip iterator will do the interleaving "virtually" - i.e. you can use the vectors as if they were interleaved, so you don't even need to de-interleave.ltjax

2 Answers

5
votes

If you're willing to use Boost, you can use slices to get syntax very close to your code in Matlab. Here's a C++ version of your Interleave3 function:

template<typename V>
V Interleave3(V const& a, V const& b, V const& c)
{
    using namespace boost::numeric::ublas;

    V v(a.size() + b.size() + c.size());
    vector_slice<V>(v, slice(0, 3, a.size())) = a;
    vector_slice<V>(v, slice(1, 3, b.size())) = b;
    vector_slice<V>(v, slice(2, 3, c.size())) = c;

    return v;
}

The catch is that instead of using std::vector you have to use boost::numeric::ublas::vector.

0
votes

I'm not sure about interleaving but you can use for_each to deinterleave. You would still want to add additional error checking (that the source size is correct etc):

#include <iostream>
#include <vector>

typedef std::vector<double> dVector;
typedef dVector::iterator dVectorIT;

class DeInterleave
{
public:
    DeInterleave(std::vector<dVector>& output) : output_(output), currentIndex_(0) { }
    void operator()(const dVector::value_type& item)
    {
        output_[currentIndex_].push_back(item);
        currentIndex_ = (currentIndex_ + 1) % output_.size();
    }

private:
    std::vector<dVector>& output_;
    int currentIndex_;
};

int main()
{
    dVector source;
    source.push_back(5.0);
    source.push_back(6.0);
    source.push_back(8.0);
    source.push_back(2.0);
    source.push_back(2.0);
    source.push_back(1.0);
    std::vector<dVector> dest(3);

    std::for_each(source.begin(), source.end(), DeInterleave(dest));

    std::cout << dest[0].size() << " " << dest[0][0] << ":" << dest[0][1] << std::endl;
    std::cout << dest[1].size() << " " << dest[1][0] << ":" << dest[1][1] << std::endl;
    std::cout << dest[2].size() << " " << dest[2][0] << ":" << dest[2][1] << std::endl;

    return 0;
}