package de.tu.darmstadt.seemoo.ansian.control.threads;
import java.util.concurrent.ArrayBlockingQueue;
import android.util.Log;
import de.greenrobot.event.EventBus;
import de.greenrobot.event.Subscribe;
import de.tu.darmstadt.seemoo.ansian.control.events.DataEvent;
import de.tu.darmstadt.seemoo.ansian.control.events.DemodDataEvent;
import de.tu.darmstadt.seemoo.ansian.control.events.RecordingEvent;
import de.tu.darmstadt.seemoo.ansian.model.Recording;
import de.tu.darmstadt.seemoo.ansian.model.SamplePacket;
import de.tu.darmstadt.seemoo.ansian.model.demodulation.Demodulation.DemoType;
import de.tu.darmstadt.seemoo.ansian.model.preferences.MiscPreferences;
import de.tu.darmstadt.seemoo.ansian.model.preferences.Preferences;
import de.tu.darmstadt.seemoo.ansian.model.sources.IQSourceInterface;
/**
* <h1>AnSiAn - Scheduler</h1>
*
* Module: Scheduler.java Description: This Thread is responsible for forwarding
* the samples from the input hardware to the Demodulator and to the Processing
* Loop and at the correct speed and format. Sample packets are passed to other
* blocks by using blocking queues. The samples passed to the Demodulator will
* be shifted to base band first. If the Demodulator or the Processing Loop are
* to slow, the scheduler will automatically drop incoming samples to keep the
* buffer of the hackrf_android library from beeing filled up.
*
*
* @author Dennis Mantz
* @author Markus Grau
* @author Steffen Kreis
*
* Copyright (C) 2014 Dennis Mantz License:
* http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
*
* This library 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 2 of the License, or (at
* your option) any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
public class Scheduler extends Thread {
int fftUsed = 0;
int ffts = 0;
private boolean stopRequested = true;
private IQSourceInterface source = null; // Reference to the source of the
// IQ samples
private ArrayBlockingQueue<SamplePacket> demodQueue = null; // Queue
// that
// delivers
// samples
// to
// the
// Demodulator
// block
private MiscPreferences preferences;
private Recording recording;
public static SamplePacket samples;
// Define the size of the fft output and input Queues. By setting this value
// to 2 we basically end up
// with double buffering. Maybe the two queues are overkill, but it works
// pretty well like this and
// it handles the synchronization between the scheduler thread and the
// processing loop for us.
// Note that setting the size to 1 will not work well and any number higher
// than 2 will cause
// higher delays when switching frequencies.
private static final int DEMOD_QUEUE_SIZE = 20;
private static final String LOGTAG = "Scheduler";
public Scheduler(IQSourceInterface source) {
setPriority(MAX_PRIORITY);
preferences = Preferences.MISC_PREFERENCE;
this.source = source;
// Create the demod input- and output queues and allocate the buffer
// packets.
demodQueue = new ArrayBlockingQueue<SamplePacket>(DEMOD_QUEUE_SIZE);
EventBus.getDefault().register(this);
}
public void stopScheduler() {
if (recording != null)
recording.stopRecordingThread();
this.stopRequested = true;
this.source.stopSampling();
}
public void start() {
this.stopRequested = false;
this.source.startSampling();
super.start();
}
/**
* @return true if scheduler is running; false if not.
*/
public boolean isRunning() {
return !stopRequested;
}
public boolean isDemodulationActivated() {
return preferences.getDemodulation() != DemoType.OFF;
}
@Override
public void run() {
Log.i(LOGTAG, "Scheduler started. (Thread: " + this.getName() + ")");
long timestamp = System.currentTimeMillis();
long count = 0;
while (!stopRequested) {
// Get a new packet from the source:
byte[] packet = source.getPacket(1000);
if (packet == null) {
Log.e(LOGTAG, "run: No more packets from source. Shutting down...");
this.stopScheduler();
break;
}
count++;
long length = packet.length;
long timetemp = System.currentTimeMillis();
if (timetemp - timestamp > 1000) {
// Log.d(LOGTAG, "packetsize " + length+ " samplerate:
// "+source.getSampleRate());
// Log.d(LOGTAG, "samples per second: " + ((count * length)*1000
// / (timetemp - timestamp)) );
count = 0;
timestamp = timetemp;
}
// /// Recording
// ////////////////////////////////////////////////////////////////////////
if (recording != null) {
recording.write(packet);
}
// /// Demodulation
// /////////////////////////////////////////////////////////////////////
if (isDemodulationActivated()) {
SamplePacket demodBuffer = new SamplePacket(source.getPacketSize());
source.mixPacketIntoSamplePacket(packet, demodBuffer, Preferences.GUI_PREFERENCE.getDemodFrequency());
EventBus.getDefault().post(new DemodDataEvent(demodBuffer));
if (Preferences.GUI_PREFERENCE.isSquelchSatisfied()) {
if (demodQueue.offer(demodBuffer)) {
// deliver packet
} else {
Log.d(LOGTAG, "run: Flush the demod queue because demodulator is too slow!");
}
}
}
// /// FFT
// //////////////////////////////////////////////////////////////////////////////
// If buffer is null we request a new buffer from the fft input
// queue:
SamplePacket temp = new SamplePacket(preferences.getFFTSize());
source.fillPacketIntoSamplePacket(packet, temp);
EventBus.getDefault().post(new DataEvent(temp));
// otherwise we would just go for another round...
// If buffer was null we currently have no buffer available, which
// means we
// simply throw the samples away (this will happen most of the
// time).
ffts++;
// Log.d(LOGTAG, "FFT Usage: " + (float) fftUsed / ffts);
// In both cases: Return the packet back to the source buffer pool:
source.returnPacket(packet);
}
this.stopRequested = true;
if (recording != null)
{
recording.stopRecordingThread();
}
Log.i(LOGTAG, "Scheduler stopped. (Thread: " + this.getName() + ")");
}
public ArrayBlockingQueue<SamplePacket> getDemodQueue() {
return demodQueue;
}
@Subscribe
public void onEvent(RecordingEvent event) {
if (event.isRecording()) {
recording = event.getRecording();
recording.startRecordingThread();
} else {
recording.stopRecordingThread();
recording = null;
}
}
}