package de.tu.darmstadt.seemoo.ansian.model;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import de.greenrobot.event.EventBus;
import de.greenrobot.event.Subscribe;
import de.tu.darmstadt.seemoo.ansian.control.SourceControl;
import de.tu.darmstadt.seemoo.ansian.control.events.ScanAreaUpdateEvent;
import de.tu.darmstadt.seemoo.ansian.control.threads.SourceControlThread;
/**
*
* ScannerBuffer holds all the frequency spectrum data for wider band range
* scanning. The single data packets are referenced to by their center
* frequency. ScannerBuffer only takes care of the data holding, the frequency
* change is handled by {@link SourceControlThread}.
*
* @author Steffen Kreis
*
*/
public class ScannerBuffer {
private Hashtable<Long, FFTSample> samples = new Hashtable<Long, FFTSample>();
private String LOGTAG = "ScannerBuffer";
private final int DEFAULT_MAXSIZE = 500;
private int maxSize;
private long lowerCenterFrequency = Long.MAX_VALUE;
private long upperCenterFrequency = Long.MIN_VALUE;
private static long samplerate = 2000000;
public ScannerBuffer(int size) {
this.maxSize = size;
EventBus.getDefault().register(this);
}
public ScannerBuffer() {
new ScannerBuffer(DEFAULT_MAXSIZE);
}
/**
*
* Adds a sample to the Hashtable. As the frequency is calculated to save as
* few as possible fft data packets and minimize the frequency hops, old
* data is overwritten automatically
*
* @param sample
*/
public void addSample(FFTSample sample) {
if (SourceControl.getSource().isTunerSettled()) {
long frequency = sample.getCenterFrequency();
if ((frequency - samplerate / 2) % samplerate == 0) {
if (frequency > upperCenterFrequency) {
upperCenterFrequency = frequency;
long lowestPossibleFrequency = frequency - maxSize * samplerate;
if (lowerCenterFrequency < lowestPossibleFrequency) {
lowerCenterFrequency = lowestPossibleFrequency;
// TODO remove 'lower than possible'-samples
}
}
if (frequency < lowerCenterFrequency) {
lowerCenterFrequency = frequency;
long highestPossibleFrequency = frequency + maxSize * samplerate;
if (upperCenterFrequency > highestPossibleFrequency) {
upperCenterFrequency = highestPossibleFrequency;
// TODO remove 'higher than possible'-samples
}
}
// Log.d(LOGTAG, "added Sample " + frequency);
samples.put(frequency, sample);
}
}
}
public int getTotalAmount() {
return (int) ((upperCenterFrequency - lowerCenterFrequency) / samplerate) + 1;
}
public FFTSample getScannerSample(long frequency) {
return samples.get(frequency);
}
public Hashtable<Long, FFTSample> getScannerSamples() {
return samples;
}
public long getLowerFrequency() {
return lowerCenterFrequency;
}
public long getUpperFrequency() {
return upperCenterFrequency;
}
public void setSamplerate(long pSamplerate) {
if (samplerate != pSamplerate) {
samplerate = pSamplerate;
samples = new Hashtable<Long, FFTSample>();
}
}
/**
* Calculates suitable center frequency. To save unnecessary frequency
* changes and also the unwanted redundant data saving, as few as possible
* single data spectrums are saved. This is realized by simply separating
* the small single spectrums by their bandwidth. The center frequency of a
* spectrum references it and the next ones can be found by
* adding/subtracting a multiple of the samplerate
*/
public static long calcSuitableFrequency(long frequency, boolean isLowerFrequency) {
frequency = Math.max(frequency, SourceControl.getSource().getMinFrequency());
frequency = Math.min(frequency, SourceControl.getSource().getMaxFrequency());
long distance = frequency % samplerate;
if (!isLowerFrequency && distance == 0) {
return frequency - samplerate / 2;
}
return frequency - distance + samplerate / 2;
}
/**
* Remove samples which are outside the desired area
*
*/
public void removeOuterSamples() {
Enumeration<Long> samplEnums = samples.keys();
while (samplEnums.hasMoreElements()) {
long centerFreq = samplEnums.nextElement();
if (centerFreq < lowerCenterFrequency || centerFreq > upperCenterFrequency) {
samples.remove(centerFreq);
}
}
}
public FFTDrawData getDrawData(int pixelWidth) {
return getDrawData(samples, pixelWidth);
}
/**
* handle a change of the desired total bandwidth (fit the lower and upper
* frequency accordingly, remove samples which are outside the desired
* bandwidth)
*
* @param event
*/
@Subscribe
public void onEvent(ScanAreaUpdateEvent event) {
// Log.d(LOGTAG, "scanareaupdate");
lowerCenterFrequency = calcSuitableFrequency(event.getLowerFrequency(), true);
upperCenterFrequency = calcSuitableFrequency(event.getUpperFrequency(), false);
samplerate = event.getSamplerate() / event.getScanCropDataFactor();
removeOuterSamples();
}
/**
*
* Returns the draw data, already scaled to the desired size (screen width)
*
*
* @param lastHash
* @param pixelWidth
* @return
*/
public FFTDrawData getDrawData(Hashtable<Long, FFTSample> lastHash, int pixelWidth) {
Enumeration<Long> enumer = lastHash.keys();
int startX = pixelWidth - 1;
int endX = 0;
float[] mags = new float[pixelWidth];
for (int pos = 0; pos < mags.length; pos++) {
mags[pos] = Float.NaN;
}
while (enumer.hasMoreElements() && mags.length == pixelWidth) {
FFTSample fftSample = lastHash.get(enumer.nextElement());
// Log.d(LOGTAG, "samplefreq: " + fftSample.getCenterFrequency());
FFTDrawData curDrawData = fftSample.getDrawData(pixelWidth);
float[] curMags = curDrawData.getValues();
if (curMags != null) {
int curStartX = curDrawData.getStart();
int curEndX = curStartX + curMags.length;
if (curStartX < startX) {
startX = curStartX;
}
if (curEndX > endX) {
endX = curEndX;
}
int curPos = 0;
for (int resultPos = curStartX; resultPos < curEndX; resultPos++) {
try {
mags[resultPos] = curMags[curPos++];
} catch (ArrayIndexOutOfBoundsException e) {
// occurs when orientation changed
// TODO find satisfying solution
return null;
}
}
}
}
if (startX < endX) {
mags = Arrays.copyOfRange(mags, startX, endX);
return new FFTDrawData(mags, startX);
}
return null;
}
}