package de.tu.darmstadt.seemoo.ansian.control; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.util.Log; import android.widget.Toast; import de.greenrobot.event.EventBus; import de.greenrobot.event.Subscribe; import de.tu.darmstadt.seemoo.ansian.MainActivity; import de.tu.darmstadt.seemoo.ansian.control.StateHandler.State; import de.tu.darmstadt.seemoo.ansian.control.events.BandwidthEvent; import de.tu.darmstadt.seemoo.ansian.control.events.DemodulationEvent; import de.tu.darmstadt.seemoo.ansian.control.events.FrequencyEvent; import de.tu.darmstadt.seemoo.ansian.control.events.RequestFrequencyEvent; import de.tu.darmstadt.seemoo.ansian.control.events.RequestStateEvent; import de.tu.darmstadt.seemoo.ansian.control.events.SourceEvent; import de.tu.darmstadt.seemoo.ansian.control.events.StateEvent; import de.tu.darmstadt.seemoo.ansian.control.threads.Demodulator; import de.tu.darmstadt.seemoo.ansian.control.threads.SourceControlThread; 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.FileIQSource; import de.tu.darmstadt.seemoo.ansian.model.sources.HackrfSource; import de.tu.darmstadt.seemoo.ansian.model.sources.IQSourceInterface; import de.tu.darmstadt.seemoo.ansian.model.sources.IQSourceInterface.SourceType; import de.tu.darmstadt.seemoo.ansian.model.sources.RtlsdrSource; /** * SourceControl handles the communication with the different sources * * @author Steffen Kreis * */ public class SourceControl implements IQSourceInterface.Callback { private MiscPreferences preferences; public static final String LOGTAG = "SourceControl"; public static final String RECORDING_DIR = "AnSiAn"; public static final int RTL2832U_RESULT_CODE = 1234; // arbitrary value, // used when sending // intent to // RTL2832U private static MainActivity activity; private static IQSourceInterface source; private static SourceControl instance; public SourceControlThread scannerThread; public static SourceControl getInstance() { if (instance == null) { instance = new SourceControl(); } return instance; } private SourceControl() { activity = MainActivity.instance; preferences = Preferences.MISC_PREFERENCE; EventBus.getDefault().register(this); createSource(); } /** * Will create a IQ Source instance according to the user settings. * * @return true on success; false on error */ public boolean createSource() { long frequency = Preferences.GUI_PREFERENCE.getFrequency(); int sampleRate = Preferences.MISC_PREFERENCE.getSampleRate(); SourceType sourceType = preferences.getSourceType(); switch (sourceType) { case FILE_SOURCE: // Create IQ Source (filesource) String filename = preferences.getSourceFileName(); int fileFormat = preferences.getSourceFileFormat(); boolean repeat = preferences.isRepeating(); source = new FileIQSource(filename, Preferences.MISC_PREFERENCE.getFileSourceSampleRate(), Preferences.MISC_PREFERENCE.getFileSourceFrequency(), 16384, repeat, fileFormat); break; case HACKRF_SOURCE: // Create HackrfSource source = new HackrfSource(); source.setFrequency(frequency); source.setSampleRate(sampleRate); ((HackrfSource) source).setVgaRxGain(preferences.getVgaRxGain()); ((HackrfSource) source).setLnaGain(preferences.getVgaRxGain()); ((HackrfSource) source).setAmplifier(preferences.getAmplifier()); ((HackrfSource) source).setAntennaPower(preferences.getAntennaPower()); ((HackrfSource) source).setFrequencyShift(preferences.getHackRfFrequenyShift()); break; case RTLSDR_SOURCE: // Create RtlsdrSource if (preferences.isExternalServer()) source = new RtlsdrSource(preferences.getRtlSdrIp(), preferences.getRtlSdrPort()); else { source = new RtlsdrSource("127.0.0.1", 1234); } if (sampleRate > 2000000) // might be the case after switching over // from HackRF sampleRate = 2000000; source.setFrequency(frequency); source.setSampleRate(sampleRate); ((RtlsdrSource) source).setFrequencyCorrection(preferences.getFrequencyCorrection()); ((RtlsdrSource) source).setFrequencyShift(preferences.getRtlsdrFrequencyShift()); ((RtlsdrSource) source).setManualGain(preferences.isManualGain()); if (((RtlsdrSource) source).isManualGain()) { ((RtlsdrSource) source).setGain(preferences.getGain()); ((RtlsdrSource) source).setIFGain(preferences.getIFGain()); } break; default: Log.e(LOGTAG, "createSource: Invalid source type: " + sourceType); return false; } // inform the analyzer surface about the new source EventBus.getDefault().post(new SourceEvent(source)); return true; } /** * Will open the IQ Source instance. Note: some sources need special * treatment on opening, like the rtl-sdr source. * * @return true on success; false on error */ public boolean openSource() { SourceType sourceType = preferences.getSourceType(); switch (sourceType) { case FILE_SOURCE: if (source != null && source instanceof FileIQSource) return source.open(activity, this); else { Log.e(LOGTAG, "openSource: sourceType is FILE_SOURCE, but source is null or of other type."); return false; } case HACKRF_SOURCE: if (source != null && source instanceof HackrfSource) return source.open(activity, this); else { Log.e(LOGTAG, "openSource: sourceType is HACKRF_SOURCE, but source is null or of other type."); return false; } case RTLSDR_SOURCE: if (source != null && source instanceof RtlsdrSource) { // We might need to start the driver: if (!preferences.isExternalSource()) { // start local rtl_tcp instance: try { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("iqsrc://-a 127.0.0.1 -p 1234 -n 1")); activity.startActivityForResult(intent, RTL2832U_RESULT_CODE); } catch (ActivityNotFoundException e) { Log.e(LOGTAG, "createSource: RTL2832U is not installed"); // Show a dialog that links to the play market: new AlertDialog.Builder(activity).setTitle("RTL2832U driver not installed!") .setMessage("You need to install the (free) RTL2832U driver to use RTL-SDR dongles.") .setPositiveButton("Install from Google Play", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=marto.rtl_tcp_andro")); activity.startActivity(marketIntent); } }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // do nothing } }).show(); return false; } } return source.open(activity, this); } else { Log.e(LOGTAG, "openSource: sourceType is RTLSDR_SOURCE, but source is null or of other type."); return false; } default: Log.e(LOGTAG, "openSource: Invalid source type: " + sourceType); return false; } } public static IQSourceInterface getSource() { if (instance == null) getInstance(); return source; } @Override public void onIQSourceReady(IQSourceInterface source) { // is called after // source.open() Log.d(LOGTAG, "onIQSourceReady called"); EventBus.getDefault().post(new RequestStateEvent(State.MONITORING)); // will start the // processing loop, // scheduler and // source } @Override public void onIQSourceError(final IQSourceInterface source, final String message) { activity.runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(activity, "Error with Source: [" + source.getName() + "]: " + message, Toast.LENGTH_LONG) .show(); } }); EventBus.getDefault().post(new RequestStateEvent(State.STOPPED)); if (source != null && source.isOpen()) source.close(); } public boolean isOpen() { return source.isOpen(); } public void changeSource() { if (source != null) { boolean change = false; if (source.getType() != preferences.getSourceType()) change = true; if (source instanceof FileIQSource) { if (!((FileIQSource) source).getFilename().equals(preferences.getSourceFileName())) { change = true; } } if (change) { if (source.isOpen()) source.close(); createSource(); } } } /** * starts wider band scanning mode */ private void startScanner() { if (scannerThread == null) { scannerThread = new SourceControlThread(source.getMaxSampleRate()); } scannerThread.start(); } /** * starts wider band scanning mode */ private void stopScanner() { if (scannerThread != null) scannerThread.stopScanner(); scannerThread = null; } @Subscribe public void onEvent(StateEvent event) { if (event.getState() == State.SCANNING) startScanner(); else stopScanner(); } @Subscribe public void onEvent(BandwidthEvent event) { int bandwidth = event.getBandwidth(); if (!StateHandler.isPaused()) { if (bandwidth > source.getMaxSampleRate() && source.allowsScanning()) EventBus.getDefault().post(new RequestStateEvent(State.SCANNING)); else EventBus.getDefault().post(new RequestStateEvent(State.MONITORING)); } if (preferences.isAdaptiveSamplerate() && scannerThread == null && !StateHandler.isDemodulating()) source.setSampleRate(source.getNextHigherOptimalSampleRate(bandwidth)); } @Subscribe public void onEvent(DemodulationEvent event) { if (event.isOff()) source.setSampleRate(Preferences.GUI_PREFERENCE.getBandwidth()); else { // Preferences.GUI_PREFERENCE.setBandwidth(Demodulator.INPUT_RATE); source.setSampleRate(Demodulator.INPUT_RATE); } } /** * Handle frequency change request. Checks if desired frequency can be tuned * to and throws a FrequencyEvent which notifies others that the frequency * actually has been changed * * @param event */ @Subscribe public void onEvent(RequestFrequencyEvent event) { long frequency = event.getRequestedFrequency(); if (event.isCheckPrevious()) { long freqIndicator = frequency - event.getPreviousFrequency(); if ((source.getMinFrequency() > frequency && freqIndicator > 0) || (frequency > source.getMaxFrequency() && freqIndicator < 0)) { source.setFrequency(event.getRequestedFrequency()); EventBus.getDefault().post(new FrequencyEvent(frequency)); return; } } if (source.getMinFrequency() < frequency && frequency < source.getMaxFrequency()) { source.setFrequency(event.getRequestedFrequency()); EventBus.getDefault().post(new FrequencyEvent(frequency)); } } }