package de.tu.darmstadt.seemoo.ansian.model.sources; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; import android.content.Context; import android.util.Log; import de.tu.darmstadt.seemoo.ansian.model.SamplePacket; import de.tu.darmstadt.seemoo.ansian.model.sources.hackrf.Hackrf; import de.tu.darmstadt.seemoo.ansian.model.sources.hackrf.HackrfCallbackInterface; import de.tu.darmstadt.seemoo.ansian.model.sources.hackrf.HackrfUsbException; import de.tu.darmstadt.seemoo.ansian.tools.IQConverter; import de.tu.darmstadt.seemoo.ansian.tools.Signed8BitIQConverter; /** * <h1>AnSiAn - HackRF source</h1> * * Module: HackrfSource.java Description: Source class representing a HackRF * device. * * @author Dennis Mantz * * 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 HackrfSource implements IQSourceInterface, HackrfCallbackInterface { private Hackrf hackrf = null; private String name = null; private Callback callback = null; private ArrayBlockingQueue<byte[]> queue = null; private long frequency = 0; private int sampleRate = 0; private int basebandFilterWidth = 0; private boolean automaticBBFilterCalculation = true; private int vgaRxGain = 0; private int vgaTxGain = 0; private int lnaGain = 0; private boolean amplifier = false; private boolean antennaPower = false; private int frequencyShift = 0; // virtually shift the frequency according // to an external up/down-converter private IQConverter iqConverter; private static final String LOGTAG = "HackRFSource"; public static final long MIN_FREQUENCY = 1l; public static final long MAX_FREQUENCY = 7250000000l; public static final int MAX_SAMPLERATE = 20000000; public static final int MIN_SAMPLERATE = 4000000; public static final int MAX_VGA_RX_GAIN = 62; public static final int MAX_VGA_TX_GAIN = 47; public static final int MAX_LNA_GAIN = 40; public static final int VGA_RX_GAIN_STEP_SIZE = 2; public static final int VGA_TX_GAIN_STEP_SIZE = 1; public static final int LNA_GAIN_STEP_SIZE = 8; public static final int[] OPTIMAL_SAMPLE_RATES = { 4000000, 6000000, 8000000, 10000000, 12500000, 16000000, 20000000 }; public HackrfSource() { iqConverter = new Signed8BitIQConverter(); } /** * Will forward an error message to the callback object * * @param msg * error message */ private void reportError(String msg) { if (callback != null) callback.onIQSourceError(this, msg); else Log.e(LOGTAG, "reportError: Callback is null. (Error: " + msg + ")"); } @Override public boolean open(Context context, Callback callback) { int queueSize = 1000000; this.callback = callback; // Initialize the HackRF (i.e. open the USB device, which requires the // user to give permissions) return Hackrf.initHackrf(context, this, queueSize); } @Override public boolean close() { return true; } @Override public void onHackrfReady(Hackrf hackrf) { this.hackrf = hackrf; if (callback != null) callback.onIQSourceReady(this); } @Override public void onHackrfError(String message) { Log.e(LOGTAG, "Error while opening HackRF: " + message); reportError(message); } @Override public boolean isOpen() { if (hackrf == null) return false; try { hackrf.getBoardID(); // this will only succeed if the hackrf is // ready/open return true; // no exception was thrown --> hackrf is open! } catch (HackrfUsbException e) { return false; // exception was thrown --> hackrf is not open } } @Override public String getName() { if (name == null && hackrf != null) { try { name = Hackrf.convertBoardIdToString(hackrf.getBoardID()); } catch (HackrfUsbException e) { } } if (name != null) return name; else return "HackRF"; } @Override public long getFrequency() { return frequency + frequencyShift; } @Override public void setFrequency(long frequency) { long actualFrequency = frequency - frequencyShift; // re-tune the hackrf: if (hackrf != null) { try { hackrf.setFrequency(actualFrequency); } catch (HackrfUsbException e) { Log.e(LOGTAG, "setFrequency: Error while setting frequency: " + e.getMessage()); reportError("Error while setting frequency"); return; } } // Flush the queue: this.flushQueue(); // Store the new frequency this.frequency = actualFrequency; this.iqConverter.setFrequency(frequency); } @Override public long getMaxFrequency() { return MAX_FREQUENCY + frequencyShift; } @Override public long getMinFrequency() { return MIN_FREQUENCY + frequencyShift; } @Override public int getMaxSampleRate() { return MAX_SAMPLERATE; } @Override public int getMinSampleRate() { return MIN_SAMPLERATE; } @Override public int getSampleRate() { return sampleRate; } @Override public int getNextHigherOptimalSampleRate(int sampleRate) { for (int opt : OPTIMAL_SAMPLE_RATES) { if (sampleRate < opt) return opt; } return OPTIMAL_SAMPLE_RATES[OPTIMAL_SAMPLE_RATES.length - 1]; } @Override public int getNextLowerOptimalSampleRate(int sampleRate) { for (int i = 1; i < OPTIMAL_SAMPLE_RATES.length; i++) { if (sampleRate <= OPTIMAL_SAMPLE_RATES[i]) return OPTIMAL_SAMPLE_RATES[i - 1]; } return OPTIMAL_SAMPLE_RATES[OPTIMAL_SAMPLE_RATES.length - 1]; } @Override public int[] getSupportedSampleRates() { return OPTIMAL_SAMPLE_RATES; } @Override public void setSampleRate(int sampleRate) { if (isAutomaticBBFilterCalculation()) setBasebandFilterWidth((int) (sampleRate * 0.75)); // set the hackrf to the new sample rate: if (hackrf != null) { try { hackrf.setSampleRate(sampleRate, 1); hackrf.setBasebandFilterBandwidth(basebandFilterWidth); } catch (HackrfUsbException e) { Log.e(LOGTAG, "setSampleRate: Error while setting sample rate: " + e.getMessage()); reportError("Error while setting sample rate"); return; } } // Flush the queue this.flushQueue(); Log.d(LOGTAG, "setSampleRate: setting sample rate to " + sampleRate); this.sampleRate = sampleRate; this.iqConverter.setSampleRate(sampleRate); } public int getBasebandFilterWidth() { return basebandFilterWidth; } public boolean isAutomaticBBFilterCalculation() { return automaticBBFilterCalculation; } public int getVgaRxGain() { return vgaRxGain; } public int getVgaTxGain() { return vgaTxGain; } public int getLnaGain() { return lnaGain; } public boolean isAmplifierOn() { return amplifier; } public boolean isAntennaPowerOn() { return antennaPower; } public void setBasebandFilterWidth(int basebandFilterWidth) { this.basebandFilterWidth = Hackrf.computeBasebandFilterBandwidth(basebandFilterWidth); Log.d(LOGTAG, "setBasebandFilterWidth: Setting BB filter width to " + this.basebandFilterWidth); if (hackrf != null) { try { hackrf.setBasebandFilterBandwidth(this.basebandFilterWidth); } catch (HackrfUsbException e) { Log.e(LOGTAG, "setBasebandFilterWidth: Error while setting base band filter width: " + e.getMessage()); reportError("Error while setting base band filter width"); } } } public void setAutomaticBBFilterCalculation(boolean automaticBBFilterCalculation) { this.automaticBBFilterCalculation = automaticBBFilterCalculation; } public void setVgaRxGain(int vgaRxGain) { if (vgaRxGain > MAX_VGA_RX_GAIN) { Log.e(LOGTAG, "setVgaRxGain: Value (" + vgaRxGain + ") too high. Maximum is: " + MAX_VGA_RX_GAIN); return; } if (hackrf != null) { try { hackrf.setRxVGAGain(vgaRxGain); } catch (HackrfUsbException e) { Log.e(LOGTAG, "setVgaRxGain: Error while setting vga gain: " + e.getMessage()); reportError("Error while setting vga gain"); return; } } this.vgaRxGain = vgaRxGain; } public void setVgaTxGain(int vgaTxGain) { if (vgaTxGain > MAX_VGA_TX_GAIN) { Log.e(LOGTAG, "setVgaTxGain: Value (" + vgaTxGain + ") too high. Maximum is: " + MAX_VGA_TX_GAIN); return; } if (hackrf != null) { try { hackrf.setTxVGAGain(vgaTxGain); } catch (HackrfUsbException e) { Log.e(LOGTAG, "setVgaTxGain: Error while setting vga gain: " + e.getMessage()); reportError("Error while setting vga gain"); return; } } this.vgaTxGain = vgaTxGain; } public void setLnaGain(int lnaGain) { if (lnaGain > MAX_LNA_GAIN) { Log.e(LOGTAG, "setLnaGain: Value (" + lnaGain + ") too high. Maximum is: " + MAX_LNA_GAIN); return; } if (hackrf != null) { try { hackrf.setRxLNAGain(lnaGain); } catch (HackrfUsbException e) { Log.e(LOGTAG, "setLnaGain: Error while setting lna gain: " + e.getMessage()); reportError("Error while setting lna gain"); return; } } this.lnaGain = lnaGain; } public void setAmplifier(boolean amplifier) { if (hackrf != null) { try { hackrf.setAmp(amplifier); } catch (HackrfUsbException e) { Log.e(LOGTAG, "setAmplifier: Error while setting amplifier: " + e.getMessage()); reportError("Error while setting amplifier state"); return; } } this.amplifier = amplifier; } public void setAntennaPower(boolean antennaPower) { if (hackrf != null) { try { hackrf.setAntennaPower(antennaPower); } catch (HackrfUsbException e) { Log.e(LOGTAG, "setAntennaPower: Error while setting antenna power: " + e.getMessage()); reportError("Error while setting antenna power state"); return; } } this.antennaPower = antennaPower; } public int getFrequencyShift() { return frequencyShift; } public void setFrequencyShift(int frequencyShift) { this.frequencyShift = frequencyShift; this.iqConverter.setFrequency(frequency + frequencyShift); } @Override public int getPacketSize() { if (hackrf != null) return hackrf.getPacketSize(); else { Log.e(LOGTAG, "getPacketSize: Hackrf instance is null"); return 0; } } @Override public byte[] getPacket(int timeout) { if (queue != null && hackrf != null) { try { byte[] packet = queue.poll(timeout, TimeUnit.MILLISECONDS); if (packet == null && (hackrf.getTransceiverMode() != Hackrf.HACKRF_TRANSCEIVER_MODE_RECEIVE)) { Log.e(LOGTAG, "getPacket: HackRF is not in receiving mode!"); reportError("HackRF stopped receiving"); } return packet; } catch (InterruptedException e) { Log.e(LOGTAG, "getPacket: Interrupted while waiting on queue"); return null; } } else { Log.e(LOGTAG, "getPacket: Queue is null"); return null; } } @Override public void returnPacket(byte[] buffer) { if (hackrf != null) hackrf.returnBufferToBufferPool(buffer); else { Log.e(LOGTAG, "returnPacket: Hackrf instance is null"); } } @Override public void startSampling() { if (hackrf != null) { try { hackrf.setSampleRate(sampleRate, 1); hackrf.setFrequency(frequency); hackrf.setBasebandFilterBandwidth(basebandFilterWidth); hackrf.setRxVGAGain(vgaRxGain); hackrf.setRxLNAGain(lnaGain); hackrf.setAmp(amplifier); hackrf.setAntennaPower(antennaPower); this.queue = hackrf.startRX(); Log.i(LOGTAG, "startSampling: Started HackRF with: sampleRate=" + sampleRate + " frequency=" + frequency + " basebandFilterWidth=" + basebandFilterWidth + " rxVgaGain=" + vgaRxGain + " lnaGain=" + lnaGain + " amplifier=" + amplifier + " antennaPower=" + antennaPower); } catch (HackrfUsbException e) { Log.e(LOGTAG, "startSampling: Error while set up hackrf: " + e.getMessage()); } } else { Log.e(LOGTAG, "startSampling: Hackrf instance is null"); } } @Override public void stopSampling() { if (hackrf != null) { try { hackrf.stop(); } catch (HackrfUsbException e) { Log.e(LOGTAG, "stopSampling: Error while tear down hackrf: " + e.getMessage()); } } else { Log.e(LOGTAG, "stopSampling: Hackrf instance is null"); } } @Override public int fillPacketIntoSamplePacket(byte[] packet, SamplePacket samplePacket) { return this.iqConverter.fillPacketIntoSamplePacket(packet, samplePacket); } public int mixPacketIntoSamplePacket(byte[] packet, SamplePacket samplePacket, long channelFrequency) { return this.iqConverter.mixPacketIntoSamplePacket(packet, samplePacket, channelFrequency); } /** * Will empty the queue */ public void flushQueue() { byte[] buffer; if (hackrf == null || queue == null) return; // nothing to flush... for (int i = 0; i < queue.size(); i++) { buffer = queue.poll(); if (buffer == null) return; // we are done; the queue is empty. hackrf.returnBufferToBufferPool(buffer); } } @Override public SourceType getType() { return SourceType.HACKRF_SOURCE; } @Override public boolean isTunerSettled() { return true; } @Override public boolean allowsScanning() { return true; } }