package de.tu.darmstadt.seemoo.ansian.model.preferences; import android.content.pm.ActivityInfo; import android.util.Log; import de.greenrobot.event.EventBus; import de.greenrobot.event.Subscribe; import de.tu.darmstadt.seemoo.ansian.MainActivity; import de.tu.darmstadt.seemoo.ansian.R; import de.tu.darmstadt.seemoo.ansian.control.SourceControl; import de.tu.darmstadt.seemoo.ansian.control.StateHandler; 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.ChangeChannelWidthEvent; import de.tu.darmstadt.seemoo.ansian.control.events.DemodScaleEvent; import de.tu.darmstadt.seemoo.ansian.control.events.DemodValueChangeEvent; 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.RequestBandwidthEvent; 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.ScaleEvent; import de.tu.darmstadt.seemoo.ansian.control.events.ScanAreaUpdateEvent; import de.tu.darmstadt.seemoo.ansian.control.events.ScrollEvent; import de.tu.darmstadt.seemoo.ansian.control.events.SpectrumScaleEvent; import de.tu.darmstadt.seemoo.ansian.control.events.SquelchChangeEvent; import de.tu.darmstadt.seemoo.ansian.control.events.StateEvent; import de.tu.darmstadt.seemoo.ansian.control.events.TapEvent; import de.tu.darmstadt.seemoo.ansian.control.events.WaterfallScaleEvent; import de.tu.darmstadt.seemoo.ansian.control.events.WaveFormScaleEvent; import de.tu.darmstadt.seemoo.ansian.control.threads.Demodulator; import de.tu.darmstadt.seemoo.ansian.model.WaterfallColorMap; import de.tu.darmstadt.seemoo.ansian.model.demodulation.Demodulation; import de.tu.darmstadt.seemoo.ansian.model.sources.IQSourceInterface; /** * Preferences concerning GUI and appropriate data representation * * @author Markus Grau * @author Steffen Kreis * */ public class GuiPreferences extends MySharedPreferences { private final String LOGTAG = "GuiPreferences"; private int bandwidth; private long centerFrequency; private long demodFrequency; private int maxBandwidth; private int minBandwidth = 1000; private int demodBandwidth; private float fftWaterfallRatio = 0.5f; // percentage of the height the fft // consumes // on the surface private static int waterfallRowHeight = 1; private static final float minDB = -100; private static final float maxDB = 10; private static final float mindBdiff = 10f; private static float curMinDB = -50; private static float curMaxDB = -5; private static int fftDrawingType = 2; private float squelch = -20; private boolean relativeFrequencies; private boolean debugInformation; private int fontSize; // Indicates the font size of the // grid labels private int frameRate; private boolean dynamicFramerate; private int colormapType; private WaterfallColorMap colormap; private boolean peakHold; private boolean pauseWaterfall; private boolean scannerWaterfall; private boolean autoscale; private boolean squelchSatisfied = false; private boolean hideTabs; private static enum FONT_SIZE { NULL, SMALL, MEDIUM, LARGE } public GuiPreferences(MainActivity activity) { super(activity); EventBus.getDefault().register(this); } public void loadPreference() { bandwidth = getInt("bandwidth", 2000000); maxBandwidth = getInt("max_bandwidth", 40000000); centerFrequency = getLong("center_frequency", 97000000); demodFrequency = getLong("demod_frequency", 97000000); demodBandwidth = getInt("demod_Bandwidth", 100000); relativeFrequencies = getBoolean("relative_frequencies", false); debugInformation = getBoolean("debug_information", false); setFontSize(getInt("font_size", 2)); frameRate = getInt("framerate", 30); dynamicFramerate = getBoolean("dynamic_framerate", false); pauseWaterfall = getBoolean("pause_waterfall", false); scannerWaterfall = getBoolean("scanner_waterfall", false); autoscale = getBoolean("autoscale", false); hideTabs = getBoolean("hide_tabs", false); // FFT fftDrawingType = getInt("fft_drawing_type", 1); colormapType = getInt("colormap_type", 0); colormap = new WaterfallColorMap(colormapType); peakHold = getBoolean("peak_hold", false); fftWaterfallRatio = getFloat("fft_waterfall_ratio", 0.5f); // Screen Orientation: setScreenOrientation(getString("screen_orientation", "auto")); } private void setScreenOrientation(String screenOrientation) { if (screenOrientation.equals("auto")) activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); else if (screenOrientation.equals("landscape")) activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); else if (screenOrientation.equals("portrait")) activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); else if (screenOrientation.equals("reverse_landscape")) activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); else if (screenOrientation.equals("reverse_portrait")) activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); } public void savePreference() { // create editor MyEditor editor = edit(); editor.putInt("bandwidth", bandwidth); editor.putInt("max_bandwidth", maxBandwidth); editor.putLong("center_frequency", centerFrequency); editor.putLong("demod_frequency", demodFrequency); editor.putLong("demod_bandwidth", demodBandwidth); editor.putBoolean("relative_frequencies", relativeFrequencies); editor.putBoolean("debug_information", debugInformation); editor.putString("font_size", "" + fontSize); editor.putString("framerate", "" + frameRate); editor.putBoolean("dynamic_framerate", dynamicFramerate); // FFT editor.putString("fft_drawing_type", "" + fftDrawingType); editor.putString("colormap_type", "" + colormapType); editor.putBoolean("peak_hold", peakHold); editor.putString("fft_waterfall_ratio", "" + fftWaterfallRatio); Log.d(LOGTAG, LOGTAG + " saved: " + editor.commit()); } @Subscribe public void onEvent(ScaleEvent event) { float xScale = event.getXScale(); float yScale = event.getYScale(); if (event instanceof WaveFormScaleEvent) { // TODO implement return; } if (event instanceof DemodScaleEvent) { Demodulation demodulation = Demodulator.getDemodulation(); int maxBW = demodulation.getMaxUserFilterWidth(); int minBW = demodulation.getMinUserFilterWidth(); Log.d(LOGTAG, "db for scale: max " + maxBW + " mmin " + minBW); int finalBW = (int) Math.max(minBW, Math.min(maxBW, demodBandwidth * xScale)); setDemodBandwidth(finalBW); EventBus.getDefault().post(new ChangeChannelWidthEvent(finalBW)); return; } if ((xScale > yScale && bandwidth > minBandwidth && xScale > 1) || (bandwidth < maxBandwidth && xScale < 1)) { bandwidth /= xScale; EventBus.getDefault().post(new BandwidthEvent(bandwidth)); return; } if (event instanceof SpectrumScaleEvent) { if (yScale != 1f) { float oldDiff = (curMaxDB - curMinDB); float newDiff = oldDiff;// / 1.5f; float centerDb = curMaxDB - oldDiff / 2; if (yScale > 1) { newDiff = newDiff - newDiff * (yScale - 1); } else { newDiff = newDiff + newDiff * (1 / yScale - 1); } Log.d(LOGTAG, "yscale: " + yScale); Log.d(LOGTAG, "newdiff: " + newDiff); newDiff = Math.max(newDiff, mindBdiff) / 2; curMinDB = Math.max(minDB, centerDb - newDiff); curMaxDB = Math.min(maxDB, centerDb + newDiff); } } if (event instanceof WaterfallScaleEvent) { } } public void updateFFTWaterfallRatio() { fftWaterfallRatio = getFloat("fft_waterfall_ratio", fftWaterfallRatio); } @Subscribe public void onEvent(TapEvent event) { IQSourceInterface source = SourceControl.getSource(); if (StateHandler.isDemodulating()) { long newDemodFrequency = (long) ((centerFrequency - bandwidth / 2) + (event.getxVal()) * bandwidth); if (newDemodFrequency > source.getMinFrequency() && newDemodFrequency < source.getMaxFrequency()) { demodFrequency = newDemodFrequency; EventBus.getDefault().post(new DemodValueChangeEvent(demodFrequency)); } } else { long newBandwidth = SourceControl.getSource().getMaxSampleRate(); long newCenterFrequency = (long) ((centerFrequency - bandwidth / 2) + (event.getxVal()) * bandwidth); EventBus.getDefault().post(new RequestFrequencyEvent(newCenterFrequency)); EventBus.getDefault().post(new BandwidthEvent(bandwidth = (int) newBandwidth)); EventBus.getDefault().post(new RequestStateEvent(State.MONITORING)); } } @Subscribe public void onEvent(FrequencyEvent event) { centerFrequency = event.getFrequency(); } @Subscribe public void onEvent(DemodulationEvent event) { if (demodFrequency < centerFrequency - bandwidth / 2 || demodFrequency > centerFrequency + bandwidth / 2) { demodFrequency = centerFrequency; } } private void correctSquelch() { if (squelch < curMinDB || squelch > curMaxDB) { squelch = curMinDB + (curMaxDB - curMinDB) / 4; } EventBus.getDefault().post(new SquelchChangeEvent(squelch)); } @Subscribe public void onEvent(ScrollEvent event) { float xScrollVal = event.getXScroll(); float yScrollVal = event.getYScroll(); if (event.isDemodScroll()) { if (Math.abs(xScrollVal) > Math.abs(yScrollVal)) { long newDemodFrequency = (long) (getDemodFrequency() - xScrollVal * bandwidth); setDemodFrequency(newDemodFrequency); EventBus.getDefault().post(new DemodValueChangeEvent(newDemodFrequency, demodBandwidth)); } else { squelch = Math.max(Math.min(curMaxDB, squelch += yScrollVal * (curMaxDB - curMinDB)), curMinDB); EventBus.getDefault().post(new SquelchChangeEvent(squelch)); } } else { if (Math.abs(xScrollVal) > Math.abs(yScrollVal)) { // horizontal scrolling long relFreqChange = (long) (xScrollVal * bandwidth); long newFrequency = getFrequency() + relFreqChange; if (StateHandler.isScanning()) { EventBus.getDefault().post(new ScanAreaUpdateEvent(centerFrequency - bandwidth / 2, centerFrequency + bandwidth / 2, 2000000, 1)); centerFrequency = newFrequency; } else { IQSourceInterface source = SourceControl.getSource(); long freqIndicator = newFrequency - centerFrequency; if (newFrequency > source.getMinFrequency() && newFrequency < source.getMaxFrequency()) { EventBus.getDefault().post(new RequestFrequencyEvent(newFrequency, centerFrequency)); long newDemodFrequency = demodFrequency + relFreqChange; setDemodFrequency(newDemodFrequency); EventBus.getDefault().post(new DemodValueChangeEvent(newDemodFrequency, demodBandwidth)); } else if ((source.getMinFrequency() > newFrequency && freqIndicator > 0) || (newFrequency > source.getMaxFrequency() && freqIndicator < 0)) { EventBus.getDefault().post(new RequestFrequencyEvent(newFrequency, centerFrequency)); } } } else { // vertical scrolling float oldDiff = curMaxDB - curMinDB; float diff = oldDiff; diff = oldDiff * yScrollVal / fftWaterfallRatio; Log.d(LOGTAG, "diff:" + diff); // diff = Math.max(diff, mindBdiff); // Log.d(LOGTAG, "diff:" + diff); // Make sure we stay in the boundaries: if (curMaxDB - diff > maxDB) oldDiff = maxDB - curMaxDB; if (curMinDB - diff < minDB) oldDiff = minDB - curMinDB; curMinDB = Math.max(curMinDB - diff, minDB); curMaxDB = Math.min(curMaxDB - diff, maxDB); correctSquelch(); } } } public int getBandwidth() { return bandwidth; } public long getFrequency() { return centerFrequency; } public void setDemodBandwidth(int bandwidth) { this.demodBandwidth = bandwidth; } public int getDemodBandwidth() { return demodBandwidth; } public long getDemodFrequency() { return demodFrequency; } public void setDemodFrequency(long frequency) { EventBus.getDefault().post(new DemodFrequencyEvent(frequency)); demodFrequency = frequency; } public float getAbsMinDBLevel() { return minDB; } public float getAbsMaxDBLevel() { return maxDB; } public float getCurMinDB() { return curMinDB; } public void setMinDBLevelGUI(float minLevel) { curMinDB = minLevel; } public float getCurMaxDB() { return curMaxDB; } public void setMaxDBLevelGUI(float maxDBLevel) { curMaxDB = maxDBLevel; } public void setSquelch(float squelch) { this.squelch = squelch; } public int getFftDrawingType() { return fftDrawingType; } public void setMinDb(float minDb) { curMinDB = minDb; } public void setMaxDb(float maxDb) { curMaxDB = maxDb; } public void setFftDrawingType(int drawingType) { fftDrawingType = drawingType; } public int getWaterfallRowHeight() { return waterfallRowHeight; } public float getSquelch() { return squelch; } public boolean isDisplayRelativeFrequencies() { return relativeFrequencies; } public boolean isShowDebugInformation() { return debugInformation; } /** * @return current font size: FONT_SIZE_SMALL, *_MEDIUM, *_LARGE */ public int getFontSize() { return fontSize; } /** * Returns the height/width of the frequency/power grid in px * * @return size of the grid (frequency grid height / power grid width) in px */ public int getGridSize() { return (int) (75 * activity.getResources().getDisplayMetrics().xdpi / 200); } /** * Set the font size * * @param fontSize * FONT_SIZE_SMALL, *_MEDIUM or *_LARGE */ private void setFontSize(int fontSize) { int normalTextSize; int smallTextSize; switch (FONT_SIZE.values()[fontSize]) { case SMALL: normalTextSize = (int) (getGridSize() * 0.3); smallTextSize = (int) (getGridSize() * 0.2); break; case MEDIUM: normalTextSize = (int) (getGridSize() * 0.476); smallTextSize = (int) (getGridSize() * 0.25); break; case LARGE: normalTextSize = (int) (getGridSize() * 0.7); smallTextSize = (int) (getGridSize() * 0.35); break; default: Log.e(LOGTAG, "setFontSize: Invalid font size: " + fontSize); return; } this.fontSize = fontSize; ColorPreference.TEXT_PAINT.setTextSize(normalTextSize); ColorPreference.TEXT_SMALL_PAINT.setTextSize(smallTextSize); Log.i(LOGTAG, "setFontSize: X-dpi=" + activity.getResources().getDisplayMetrics().xdpi + " X-width=" + activity.getResources().getDisplayMetrics().widthPixels + " fontSize=" + fontSize + " normalTextSize=" + normalTextSize + " smallTextSize=" + smallTextSize); } public float getFFTRatio() { if (StateHandler.isScanning() && !scannerWaterfall) return 1; else return fftWaterfallRatio; } public void setFFTRatio(float d) { fftWaterfallRatio = d; } @Override public String getName() { return "gui"; } @Override public int getResID() { return R.xml.gui_preferences; } public int getFramerate() { return frameRate; } public boolean isDynamicFrameRate() { return dynamicFramerate; } public boolean isPeakHold() { return peakHold; } public boolean isWaterfallPaused() { return pauseWaterfall; } public WaterfallColorMap getWaterfallColorMap() { return colormap; } public boolean isScannerWaterfall() { return scannerWaterfall; } public void setBandwidth(int inputRate) { this.bandwidth = inputRate; } public boolean isAutoscale() { return autoscale; } public void setSquelchSatisfied(boolean b) { squelchSatisfied = b; } public boolean isSquelchSatisfied() { return squelchSatisfied; } public boolean isTabsHide() { return hideTabs; } // /** // * Sets the fft to waterfall ratio // * // * @param fftRatio // * percentage of the fft on the screen (0 -> 0%; 1 -> 100%) // */ // public void setFftRatio(float fftRatio) { // if (fftRatio != this.fftRatio) { // this.fftRatio = fftRatio; // // waterfallDrwb.createWaterfallLineBitmaps(); // recreate the // // waterfall // // Recreate the shaders: // ColorPreference.FFT_PAINT.setShader( // new LinearGradient(0, 0, 0, getFftHeight(), Color.WHITE, Color.BLUE, // Shader.TileMode.MIRROR)); // } // } }