/******************************************************************************* * sdrtrunk * Copyright (C) 2014-2017 Dennis Sheirer * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 program. If not, see <http://www.gnu.org/licenses/> * ******************************************************************************/ package source.tuner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.Device; import org.usb4java.DeviceDescriptor; import org.usb4java.DeviceList; import org.usb4java.LibUsb; import source.SourceException; import source.mixer.MixerManager; import source.tuner.airspy.AirspyTuner; import source.tuner.airspy.AirspyTunerController; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.proV1.FCD1TunerController; import source.tuner.fcd.proplusV2.FCD2TunerController; import source.tuner.hackrf.HackRFTuner; import source.tuner.hackrf.HackRFTunerController; import source.tuner.rtl.RTL2832Tuner; import source.tuner.rtl.RTL2832TunerController; import source.tuner.rtl.e4k.E4KTunerController; import source.tuner.rtl.r820t.R820TTunerController; import java.util.Collection; public class TunerManager { private final static Logger mLog = LoggerFactory.getLogger(TunerManager.class); private MixerManager mMixerManager; private TunerModel mTunerModel; /** * Application-wide LibUSB timeout processor for transfer buffers. All classes that need to use USB transfer * buffers can register with this processor and the processor will auto-start and auto-stop while USB transfer * processors are registered. */ public static final LibUSBTransferProcessor LIBUSB_TRANSFER_PROCESSOR; static { LIBUSB_TRANSFER_PROCESSOR = new LibUSBTransferProcessor(); } public TunerManager(MixerManager mixerManager, TunerModel tunerModel) { mMixerManager = mixerManager; mTunerModel = tunerModel; initTuners(); } /** * Performs cleanup of USB related issues */ public void dispose() { LibUsb.exit(null); } /** * Loads all USB tuners and USB/Mixer tuner devices */ private void initTuners() { DeviceList deviceList = new DeviceList(); int result = LibUsb.init(null); if(result != LibUsb.SUCCESS) { mLog.error("unable to initialize libusb [" + LibUsb.errorName(result) + "]"); } else { mLog.info("LibUSB API Version: " + LibUsb.getApiVersion()); mLog.info("LibUSB Version: " + LibUsb.getVersion()); result = LibUsb.getDeviceList(null, deviceList); if(result < 0) { mLog.error("unable to get device list from libusb [" + result + " / " + LibUsb.errorName(result) + "]"); } else { mLog.info("discovered [" + result + "] attached USB devices"); } } for(Device device : deviceList) { DeviceDescriptor descriptor = new DeviceDescriptor(); result = LibUsb.getDeviceDescriptor(device, descriptor); if(result != LibUsb.SUCCESS) { mLog.error("unable to read device descriptor [" + LibUsb.errorName(result) + "]"); } else { TunerInitStatus status = initTuner(device, descriptor); StringBuilder sb = new StringBuilder(); sb.append("usb device ["); sb.append(String.format("%04X", descriptor.idVendor())); sb.append(":"); sb.append(String.format("%04X", descriptor.idProduct())); if(status.isLoaded()) { Tuner tuner = status.getTuner(); try { mTunerModel.addTuner(tuner); sb.append("] LOADED: "); sb.append(tuner.toString()); } catch(Exception e) { sb.append("] NOT LOADED: "); sb.append(status.getInfo()); sb.append(" Error:" + e.getMessage()); } } else { sb.append("] NOT LOADED: "); sb.append(status.getInfo()); } mLog.info(sb.toString()); } } LibUsb.freeDeviceList(deviceList, true); } private TunerInitStatus initTuner(Device device, DeviceDescriptor descriptor) { if(device != null && descriptor != null) { TunerClass tunerClass = TunerClass.valueOf(descriptor.idVendor(), descriptor.idProduct()); switch(tunerClass) { case AIRSPY: return initAirspyTuner(device, descriptor); case ETTUS_USRP_B100: return initEttusB100Tuner(device, descriptor); case FUNCUBE_DONGLE_PRO: return initFuncubeProTuner(device, descriptor); case FUNCUBE_DONGLE_PRO_PLUS: return initFuncubeProPlusTuner(device, descriptor); case HACKRF_ONE: case RAD1O: return initHackRFTuner(device, descriptor); case COMPRO_VIDEOMATE_U620F: case COMPRO_VIDEOMATE_U650F: case COMPRO_VIDEOMATE_U680F: case GENERIC_2832: case GENERIC_2838: case DEXATEK_5217_DVBT: case DEXATEK_DIGIVOX_MINI_II_REV3: case DEXATEK_LOGILINK_VG002A: case GIGABYTE_GTU7300: case GTEK_T803: case LIFEVIEW_LV5T_DELUXE: case MYGICA_TD312: case PEAK_102569AGPK: case PROLECTRIX_DV107669: case SVEON_STV20: case TERRATEC_CINERGY_T_REV1: case TERRATEC_CINERGY_T_REV3: case TERRATEC_NOXON_REV1_B3: case TERRATEC_NOXON_REV1_B4: case TERRATEC_NOXON_REV1_B7: case TERRATEC_NOXON_REV1_C6: case TERRATEC_NOXON_REV2: case TERRATEC_T_STICK_PLUS: case TWINTECH_UT40: case ZAAPA_ZTMINDVBZP: return initRTL2832Tuner(tunerClass, device, descriptor); case UNKNOWN: default: break; } } return new TunerInitStatus(null, "Unknown Device"); } private TunerInitStatus initAirspyTuner(Device device, DeviceDescriptor descriptor) { try { AirspyTunerController airspyController = new AirspyTunerController(device); airspyController.init(); AirspyTuner tuner = new AirspyTuner(airspyController); return new TunerInitStatus(tuner, "LOADED"); } catch(SourceException se) { mLog.error("couldn't construct Airspy controller/tuner", se); return new TunerInitStatus(null, "error constructing Airspy tuner controller"); } } private TunerInitStatus initEttusB100Tuner(Device device, DeviceDescriptor descriptor) { return new TunerInitStatus(null, "Ettus B100 tuner not currently " + "supported"); } private TunerInitStatus initFuncubeProTuner(Device device, DeviceDescriptor descriptor) { String reason = "NOT LOADED"; MixerTunerDataLine dataline = getMixerTunerDataLine( TunerClass.FUNCUBE_DONGLE_PRO.getTunerType()); if(dataline != null) { FCD1TunerController controller = new FCD1TunerController(device, descriptor); try { controller.init(); FCDTuner tuner = new FCDTuner(dataline, controller); return new TunerInitStatus(tuner, "LOADED"); } catch(SourceException e) { mLog.error("couldn't load funcube dongle pro tuner", e); reason = "error during initialization - " + e.getLocalizedMessage(); } } else { reason = "couldn't find matching mixer dataline"; } return new TunerInitStatus(null, "Funcube Dongle Pro tuner not " + "loaded - " + reason); } private TunerInitStatus initFuncubeProPlusTuner(Device device, DeviceDescriptor descriptor) { String reason = "NOT LOADED"; MixerTunerDataLine dataline = getMixerTunerDataLine( TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType()); if(dataline != null) { FCD2TunerController controller = new FCD2TunerController(device, descriptor); try { controller.init(); FCDTuner tuner = new FCDTuner(dataline, controller); return new TunerInitStatus(tuner, "LOADED"); } catch(SourceException e) { mLog.error("couldn't load funcube dongle pro plus tuner", e); reason = "error during initialization - " + e.getLocalizedMessage(); } } else { reason = "couldn't find matching mixer dataline"; } return new TunerInitStatus(null, "Funcube Dongle Pro tuner not " + "loaded - " + reason); } private TunerInitStatus initHackRFTuner(Device device, DeviceDescriptor descriptor) { try { HackRFTunerController hackRFController = new HackRFTunerController(device, descriptor); hackRFController.init(); HackRFTuner tuner = new HackRFTuner(hackRFController); return new TunerInitStatus(tuner, "LOADED"); } catch(SourceException se) { mLog.error("couldn't construct HackRF controller/tuner", se); return new TunerInitStatus(null, "error constructing HackRF tuner controller"); } } private TunerInitStatus initRTL2832Tuner(TunerClass tunerClass, Device device, DeviceDescriptor deviceDescriptor) { String reason = "NOT LOADED"; TunerType tunerType = tunerClass.getTunerType(); if(tunerType == TunerType.RTL2832_VARIOUS) { try { tunerType = RTL2832TunerController.identifyTunerType(device); } catch(SourceException e) { mLog.error("couldn't determine RTL2832 tuner type", e); tunerType = TunerType.UNKNOWN; } } switch(tunerType) { case ELONICS_E4000: try { E4KTunerController controller = new E4KTunerController(device, deviceDescriptor); controller.init(); RTL2832Tuner rtlTuner = new RTL2832Tuner(tunerClass, controller); return new TunerInitStatus(rtlTuner, "LOADED"); } catch(SourceException se) { return new TunerInitStatus(null, "Error constructing E4K tuner controller - " + se.getLocalizedMessage()); } case RAFAELMICRO_R820T: try { R820TTunerController controller = new R820TTunerController(device, deviceDescriptor); controller.init(); RTL2832Tuner rtlTuner = new RTL2832Tuner(tunerClass, controller); return new TunerInitStatus(rtlTuner, "LOADED"); } catch(SourceException se) { mLog.error("error constructing tuner", se); return new TunerInitStatus(null, "Error constructing R820T " + "tuner controller - " + se.getLocalizedMessage()); } case FITIPOWER_FC0012: case FITIPOWER_FC0013: case RAFAELMICRO_R828D: case UNKNOWN: default: reason = "SDRTRunk doesn't currently support RTL2832 " + "Dongle with [" + tunerType.toString() + "] tuner for tuner class[" + tunerClass.toString() + "]"; break; } return new TunerInitStatus(null, reason); } /** * Gets the first tuner mixer dataline that corresponds to the tuner class. * * Note: this method is not currently able to align multiple tuner mixer * data lines of the same tuner type. If you have multiple Funcube Dongle * tuners of the same TYPE, there is no guarantee that you will get the * correct mixer. * * @param tunerClass * @return */ private MixerTunerDataLine getMixerTunerDataLine(TunerType tunerClass) { Collection<MixerTunerDataLine> datalines = mMixerManager.getMixerTunerDataLines(); for(MixerTunerDataLine mixerTDL : datalines) { if(mixerTDL.getMixerTunerType().getTunerClass() == tunerClass) { return mixerTDL; } } return null; } public class TunerInitStatus { private Tuner mTuner; private String mInfo; public TunerInitStatus(Tuner tuner, String info) { mTuner = tuner; mInfo = info; } public Tuner getTuner() { return mTuner; } public String getInfo() { return mInfo; } public boolean isLoaded() { return mTuner != null; } } }