/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.LinkedList;
import java.util.List;
public class ReusableChannelResultsBuffer extends AbstractReusableBuffer
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableChannelResultsBuffer.class);
private ReusableComplexBufferQueue mReusableComplexBufferQueue =
new ReusableComplexBufferQueue("ReusableChannelResultsBuffer");
private LinkedList mEmptyBuffers = new LinkedList<>();
private LinkedList mFilledBuffers = new LinkedList<>();
private Integer mLength;
/**
* Reusable buffer for storing polyphase channelizer results arrays. This buffer is NOT thread safe and is
* designed to be used by only one consumer. The consumer must invoke the prepareForRecycle() method to indicate that this
* buffer can be reused.
*
* @param disposedListener to receive notification when this buffer is ready for prepareForRecycle
*/
ReusableChannelResultsBuffer(IReusableBufferDisposedListener disposedListener)
{
super(disposedListener);
}
/**
* Recycles this buffer for reuse.
*/
protected void prepareForRecycle()
{
mEmptyBuffers.addAll(mFilledBuffers);
mFilledBuffers.clear();
mLength = null;
}
/**
* Gets an empty channel results array correctly sized to the requested length. This method should be used to
* obtain all empty channel results buffers to fill with information and add back to this buffer so that the results
* arrays can be reused.
*
* @param length of the requested array
* @return a float array of the requested length that may contain stale data from previous use.
*/
public float[] getEmptyBuffer(int length)
{
float[] emptyBuffer = mEmptyBuffers.poll();
if(emptyBuffer == null || emptyBuffer.length != length)
{
emptyBuffer = new float[length];
}
return emptyBuffer;
}
/**
* Adds the channel results to this buffer. Note: you should only use channel results arrays obtained via the
* getEmptyBuffer() method and filled with data and then added back to this buffer via this method, so that array
* reuse can be accurately controlled.
*
* @param channelResults to add back to this buffer
*/
public void addChannelResults(float[] channelResults)
{
if(mLength == null)
{
mLength = channelResults.length;
}
else if(mLength != channelResults.length)
{
throw new IllegalArgumentException("Channel results length must be the same for all added results");
}
mFilledBuffers.offer(channelResults);
}
/**
* Channel results array contained in this buffer
*/
public List getChannelResults()
{
return mFilledBuffers;
}
/**
* Extracts a single I/Q interleaved sample buffer from this buffer of channel results arrays
*
* @param iChannelIndex to extract for the inphase sample. The q index is assumed to be one greater than i.
* @return float[] containing interleaved I and Q samples
* @throws IllegalArgumentException if this buffer doesn't contain any samples or if the requested channel is
* not within the channel range of the contained channel results.
*/
public ReusableComplexBuffer getChannel(int iChannelIndex)
{
if(mFilledBuffers.isEmpty())
{
throw new IllegalArgumentException("Buffer is empty - cannot extract channel [" + iChannelIndex + "]");
}
if(!isValidChannelIndex(iChannelIndex))
{
throw new IllegalArgumentException("Channel [" + iChannelIndex + "] is not valid for the contained channel " +
"results -- max channel is " + mLength);
}
ReusableComplexBuffer channelBuffer = mReusableComplexBufferQueue.getBuffer(mFilledBuffers.size() * 2);
float[] samples = channelBuffer.getSamples();
int pointer = 0;
int qChannelIndex = iChannelIndex + 1;
for(float[] channelResults: mFilledBuffers)
{
try
{
samples[pointer] = channelResults[iChannelIndex];
pointer++;
samples[pointer] = channelResults[qChannelIndex];
pointer++;
}
catch(Exception e)
{
mLog.error("Error accessing channel results - iIndex:" + iChannelIndex +
" Results Size: " + mFilledBuffers.size() +
" Samples Length:" + samples.length +
" Pointer:" + pointer, e);
}
}
channelBuffer.incrementUserCount();
return channelBuffer;
}
/**
* Indicates if the channel argument is valid for the contained channel results.
*
* @param channelIndex number (0 to N) to check
* @return false if the buffer is empty or if the channel is greater than the contained channel results
*/
private boolean isValidChannelIndex(int channelIndex)
{
return (mLength != null) && (channelIndex <= mLength);
}
}