package source.tuner.airspy;
import dsp.filter.dc.DCRemovalFilter_RB;
import dsp.filter.hilbert.HilbertTransform;
import org.apache.commons.io.EndianUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.usb4java.Device;
import org.usb4java.DeviceHandle;
import org.usb4java.LibUsb;
import org.usb4java.LibUsbException;
import org.usb4java.Transfer;
import org.usb4java.TransferCallback;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.TunerController;
import source.tuner.configuration.TunerConfiguration;
import source.tuner.usb.USBTransferProcessor;
import util.ThreadPool;
import javax.usb.UsbException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* SDR Trunk
* Copyright (C) 2015-2017 Dennis Sheirer
*
* Ported from libairspy at:
* https://github.com/airspy/host/tree/master/libairspy
* -----------------------------------------------------------------------------
* Copyright (c) 2013, Michael Ossmann <mike@ossmann.com>
* Copyright (c) 2012, Jared Boone <jared@sharebrained.com>
* Copyright (c) 2014, Youssef Touil <youssef@airspy.com>
* Copyright (c) 2014, Benjamin Vernoux <bvernoux@airspy.com>
* Copyright (c) 2015, Ian Gilmour <ian@sdrsharp.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of AirSpy nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
public class AirspyTunerController extends TunerController
{
public static final Gain LINEARITY_GAIN_DEFAULT = Gain.LINEARITY_14;
public static final Gain SENSITIVITY_GAIN_DEFAULT = Gain.SENSITIVITY_10;
public static final int GAIN_MIN = 1;
public static final int GAIN_MAX = 22;
public static final int LNA_GAIN_MIN = 0;
public static final int LNA_GAIN_MAX = 14;
public static final int LNA_GAIN_DEFAULT = 7;
public static final int MIXER_GAIN_MIN = 0;
public static final int MIXER_GAIN_MAX = 15;
public static final int MIXER_GAIN_DEFAULT = 9;
public static final int IF_GAIN_MIN = 0;
public static final int IF_GAIN_MAX = 15;
public static final int IF_GAIN_DEFAULT = 9;
public static final long FREQUENCY_MIN = 24000000l;
public static final long FREQUENCY_MAX = 1800000000l;
public static final long FREQUENCY_DEFAULT = 101100000;
public static final double USABLE_BANDWIDTH_PERCENT = 0.90;
public static final AirspySampleRate DEFAULT_SAMPLE_RATE =
new AirspySampleRate(0, 10000000, "10.00 MHz");
private static final long USB_TIMEOUT_MS = 2000l; //milliseconds
private static final byte USB_INTERFACE = (byte) 0x0;
private static final int USB_TRANSFER_BUFFER_SIZE = 262144;
private static final byte USB_REQUEST_IN = (byte) (LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE);
private static final byte USB_REQUEST_OUT = (byte) (LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE);
public static final DecimalFormat MHZ_FORMATTER = new DecimalFormat("#.00 MHz");
private final static Logger mLog = LoggerFactory.getLogger(AirspyTunerController.class);
private Device mDevice;
private DeviceHandle mDeviceHandle;
private AirspySampleAdapter mSampleAdapter = new AirspySampleAdapter();
private AirspyDeviceInformation mDeviceInfo;
private List<AirspySampleRate> mSampleRates = new ArrayList<>();
private int mSampleRate = 0;
private USBTransferProcessor mUSBTransferProcessor;
public AirspyTunerController(Device device) throws SourceException
{
super(FREQUENCY_MIN, FREQUENCY_MAX, 0, USABLE_BANDWIDTH_PERCENT);
mDevice = device;
}
public void init() throws SourceException
{
mDeviceHandle = new DeviceHandle();
int result = LibUsb.open(mDevice, mDeviceHandle);
if(result != LibUsb.SUCCESS)
{
if(result == LibUsb.ERROR_ACCESS)
{
mLog.error("Unable to access Airspy - insufficient permissions."
+ " If you are running a Linux OS, have you installed the "
+ "airspy rules file in \\etc\\udev\\rules.d ??");
}
throw new SourceException("Couldn't open airspy device - " +
LibUsb.strError(result));
}
try
{
claimInterface();
}
catch(Exception e)
{
throw new SourceException("Airspy Tuner Controller - error while "
+ "setting USB configuration, claiming USB interface or "
+ "reset the kernel mode driver", e);
}
try
{
setSamplePacking(false);
}
catch(LibUsbException | UsbException | UnsupportedOperationException e)
{
mLog.info("Sample packing is not supported by airspy firmware");
}
try
{
setReceiverMode(true);
}
catch(Exception e)
{
mLog.error("Couldn't enable airspy receiver mode", e);
}
setFrequency(FREQUENCY_DEFAULT);
try
{
determineAvailableSampleRates();
}
catch(LibUsbException | UsbException e)
{
mLog.error("Error identifying available samples rates", e);
}
try
{
setSampleRate(DEFAULT_SAMPLE_RATE);
}
catch(IllegalArgumentException | LibUsbException | UsbException e)
{
mLog.error("Setting sample rate is not supported by firmware", e);
}
String deviceName = "Airspy " + getDeviceInfo().getSerialNumber();
mUSBTransferProcessor = new USBTransferProcessor(deviceName, mDeviceHandle, mSampleAdapter,
USB_TRANSFER_BUFFER_SIZE);
}
/**
* Claims the USB interface. If another application currently has
* the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting
* will dictate if the interface is forcibly claimed from the other
* application
*/
private void claimInterface() throws SourceException
{
if(mDeviceHandle != null)
{
int result = LibUsb.kernelDriverActive(mDeviceHandle, USB_INTERFACE);
if(result == 1)
{
result = LibUsb.detachKernelDriver(mDeviceHandle, USB_INTERFACE);
if(result != LibUsb.SUCCESS)
{
mLog.error("failed attempt to detach kernel driver [" +
LibUsb.errorName(result) + "]");
throw new SourceException("couldn't detach kernel driver "
+ "from device");
}
}
result = LibUsb.setConfiguration(mDeviceHandle, 1);
if(result != LibUsb.SUCCESS)
{
throw new SourceException("couldn't set USB configuration 1 [" +
LibUsb.errorName(result) + "]");
}
result = LibUsb.claimInterface(mDeviceHandle, USB_INTERFACE);
if(result != LibUsb.SUCCESS)
{
throw new SourceException("couldn't claim usb interface [" +
LibUsb.errorName(result) + "]");
}
}
else
{
throw new SourceException("couldn't claim usb interface - no "
+ "device handle");
}
}
public static String getTransferStatus(int status)
{
switch(status)
{
case 0:
return "TRANSFER COMPLETED (0)";
case 1:
return "TRANSFER ERROR (1)";
case 2:
return "TRANSFER TIMED OUT (2)";
case 3:
return "TRANSFER CANCELLED (3)";
case 4:
return "TRANSFER STALL (4)";
case 5:
return "TRANSFER NO DEVICE (5)";
case 6:
return "TRANSFER OVERFLOW (6)";
default:
return "UNKNOWN TRANSFER STATUS (" + status + ")";
}
}
@Override
public void apply(TunerConfiguration config) throws SourceException
{
if(config instanceof AirspyTunerConfiguration)
{
AirspyTunerConfiguration airspy = (AirspyTunerConfiguration) config;
int sampleRate = airspy.getSampleRate();
AirspySampleRate rate = getSampleRate(sampleRate);
if(rate == null)
{
if(!mSampleRates.isEmpty())
{
rate = mSampleRates.get(0);
}
else
{
rate = DEFAULT_SAMPLE_RATE;
}
}
try
{
setSampleRate(rate);
}
catch(UsbException e)
{
throw new SourceException("Couldn't set sample rate [" +
rate.toString() + "]", e);
}
try
{
setIFGain(airspy.getIFGain());
setMixerGain(airspy.getMixerGain());
setLNAGain(airspy.getLNAGain());
setMixerAGC(airspy.isMixerAGC());
setLNAAGC(airspy.isLNAAGC());
//Set the gain mode last, so custom values are already set, and
//linearity and sensitivity modes will automatically override
//the custom values.
setGain(airspy.getGain());
setFrequencyCorrection(airspy.getFrequencyCorrection());
}
catch(Exception e)
{
throw new SourceException("Couldn't apply gain settings from "
+ "airspy config", e);
}
try
{
setFrequency(airspy.getFrequency());
}
catch(SourceException se)
{
//Do nothing, we couldn't set the frequency
}
}
else
{
throw new IllegalArgumentException("Invalid tuner config:" +
config.getClass());
}
}
@Override
public long getTunedFrequency() throws SourceException
{
return mFrequencyController.getTunedFrequency();
}
@Override
public void setTunedFrequency(long frequency) throws SourceException
{
if(FREQUENCY_MIN <= frequency && frequency <= FREQUENCY_MAX)
{
ByteBuffer buffer = ByteBuffer.allocateDirect(4);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt((int) frequency);
buffer.rewind();
try
{
write(Command.SET_FREQUENCY, 0, 0, buffer);
}
catch(UsbException e)
{
mLog.error("error setting frequency [" + frequency + "]", e);
throw new SourceException("error setting frequency [" +
frequency + "]", e);
}
}
else
{
throw new SourceException("Frequency [" + frequency + "] outside "
+ "of tunable range " + FREQUENCY_MIN + "-" + FREQUENCY_MAX);
}
}
@Override
public int getCurrentSampleRate() throws SourceException
{
return mSampleRate;
}
/**
* Sets the sample rate to the rate specified by the index value in the
* available sample rates map
*
* @param rate to a sample rate in the available samples rates map.
* @throws IllegalArgumentException if index is not a valid rate index
* @throws LibUsbException if there was a read error or if this operation
* is not supported by the current firmware
* @throws UsbException if there was a USB error
*/
public void setSampleRate(AirspySampleRate rate) throws
LibUsbException, UsbException, SourceException
{
if(rate.getRate() != mSampleRate)
{
int result = readByte(Command.SET_SAMPLE_RATE, 0, rate.getIndex(), true);
if(result != 1)
{
throw new UsbException("Error setting sample rate [" +
rate + "] rate - return value [" + result + "]");
}
else
{
mSampleRate = rate.getRate();
mFrequencyController.setSampleRate(mSampleRate);
}
}
}
/**
* Returns a list of sample rates supported by the firmware version
*/
public List<AirspySampleRate> getSampleRates()
{
return mSampleRates;
}
/**
* Airspy sample rate object that matches the current sample rate setting.
*/
public AirspySampleRate getAirspySampleRate()
{
return getSampleRate(mSampleRate);
}
/**
* Airspy sample rate object that matches the specified rate in hertz, or
* null if there are no available sample rates for the tuner that match the
* argument value.
*/
public AirspySampleRate getSampleRate(int rate)
{
for(AirspySampleRate sampleRate : mSampleRates)
{
if(sampleRate.getRate() == rate)
{
return sampleRate;
}
}
//We should never get to here ...
return null;
}
/**
* Enables/Disables sample packing to allow two 12-bit samples to be packed
* into 3 bytes (enabled) or 4 bytes (disabled).
*
* @param enabled
* @throws UsbException if sample packing is not supported by the current
* device firmware or if there were usb communication issues
*/
public void setSamplePacking(boolean enabled)
throws LibUsbException, UsbException
{
int result = readByte(Command.SET_PACKING, 0, (enabled ? 1 : 0), true);
if(result != 1)
{
throw new UsbException("Couldnt set sample packing enabled: " + enabled);
}
/* If we didn't throw an exception above, then update the sample adapter
* to process samples accordingly */
mSampleAdapter.setSamplePacking(enabled);
}
/**
* Enables/disables the mixer automatic gain setting
*
* @param enabled
* @throws LibUsbException on unsuccessful read operation
* @throws UsbException on USB error
*/
public void setMixerAGC(boolean enabled)
throws LibUsbException, UsbException
{
int result = readByte(Command.SET_MIXER_AGC, 0, (enabled ? 1 : 0), true);
if(result != LibUsb.SUCCESS)
{
throw new UsbException("Couldnt set mixer AGC enabled: " + enabled);
}
}
/**
* Enables/disables the low noise amplifier automatic gain setting
*
* @param enabled
* @throws LibUsbException on unsuccessful read operation
* @throws UsbException on USB error
*/
public void setLNAAGC(boolean enabled) throws LibUsbException, UsbException
{
int result = readByte(Command.SET_LNA_AGC, 0, (enabled ? 1 : 0), true);
if(result != LibUsb.SUCCESS)
{
throw new UsbException("Couldnt set LNA AGC enabled: " + enabled);
}
}
public void setGain(Gain gain) throws UsbException
{
if(gain != Gain.CUSTOM)
{
setMixerAGC(false);
setLNAAGC(false);
setLNAGain(gain.getLNA());
setMixerGain(gain.getMixer());
setIFGain(gain.getIF());
}
}
/**
* Sets LNA gain
*
* @param gain - value within range of LNA_GAIN_MIN to LNA_GAIN_MAX
* @throws LibUsbException on error in java USB wrapper
* @throws UsbException on error in USB transfer
* @throws IllegalArgumentException if gain value is invalid
*/
public void setLNAGain(int gain)
throws LibUsbException, UsbException, IllegalArgumentException
{
if(LNA_GAIN_MIN <= gain && gain <= LNA_GAIN_MAX)
{
int result = readByte(Command.SET_LNA_GAIN, 0, gain, true);
if(result != LibUsb.SUCCESS)
{
throw new UsbException("Couldnt set LNA gain to: " + gain);
}
}
else
{
throw new IllegalArgumentException("LNA gain value [" + gain +
"] is outside value range: " + LNA_GAIN_MIN + "-" + LNA_GAIN_MAX);
}
}
/**
* Sets Mixer gain
*
* @param gain - value within range of MIXER_GAIN_MIN to MIXER_GAIN_MAX
* @throws LibUsbException on error in java USB wrapper
* @throws UsbException on error in USB transfer
* @throws IllegalArgumentException if gain value is invalid
*/
public void setMixerGain(int gain)
throws LibUsbException, UsbException, IllegalArgumentException
{
if(MIXER_GAIN_MIN <= gain && gain <= MIXER_GAIN_MAX)
{
int result = readByte(Command.SET_MIXER_GAIN, 0, gain, true);
if(result != LibUsb.SUCCESS)
{
throw new UsbException("Couldnt set mixer gain to: " + gain);
}
}
else
{
throw new IllegalArgumentException("Mixer gain value [" + gain +
"] is outside value range: " + MIXER_GAIN_MIN + "-" + MIXER_GAIN_MAX);
}
}
/**
* Sets IF (VGA) gain
*
* @param gain - value within range of VGA_GAIN_MIN to VGA_GAIN_MAX
* @throws LibUsbException on error in java USB wrapper
* @throws UsbException on error in USB transfer
* @throws IllegalArgumentException if gain value is invalid
*/
public void setIFGain(int gain)
throws LibUsbException, UsbException, IllegalArgumentException
{
if(IF_GAIN_MIN <= gain && gain <= IF_GAIN_MAX)
{
int result = readByte(Command.SET_VGA_GAIN, 0, gain, true);
if(result != LibUsb.SUCCESS)
{
throw new UsbException("Couldnt set VGA gain to: " + gain);
}
}
else
{
throw new IllegalArgumentException("VGA gain value [" + gain +
"] is outside value range: " + IF_GAIN_MIN + "-" + IF_GAIN_MAX);
}
}
public void setReceiverMode(boolean enabled) throws LibUsbException, UsbException
{
//Empty buffer to throw away
ByteBuffer buffer = ByteBuffer.allocateDirect(0);
write(Command.RECEIVER_MODE, (enabled ? 1 : 0), 0, buffer);
}
/**
* Queries the device for available sample rates. Will always provide at
* least the default 10 MHz sample rate.
*/
private void determineAvailableSampleRates()
throws LibUsbException, UsbException
{
mSampleRates.clear();
//Get a count of available sample rates. If we get an exception, then
//we're using an older firmware revision and only the default 10 MHz
//rate is supported
try
{
byte[] rawCount = readArray(Command.GET_SAMPLE_RATES, 0, 0, 4);
if(rawCount != null)
{
int count = EndianUtils.readSwappedInteger(rawCount, 0);
byte[] rawRates = readArray(Command.GET_SAMPLE_RATES, 0,
count, (count * 4));
for(int x = 0; x < count; x++)
{
int rate = EndianUtils.readSwappedInteger(rawRates, (x * 4));
mSampleRates.add(new AirspySampleRate(x, rate, formatSampleRate(rate)));
}
}
}
catch(LibUsbException e)
{
//Press on, nothing else to do here ..
}
if(mSampleRates.isEmpty())
{
mSampleRates.add(DEFAULT_SAMPLE_RATE);
}
}
/**
* Formats the rate in hertz for display as megahertz
*/
private static String formatSampleRate(int rate)
{
return MHZ_FORMATTER.format((double) rate / 1E6d);
}
/**
* Device information
*/
public AirspyDeviceInformation getDeviceInfo()
{
//Lazy initialization
if(mDeviceInfo == null)
{
readDeviceInfo();
}
return mDeviceInfo;
}
/**
* Reads version information from the device and populates the info object
*/
private void readDeviceInfo()
{
if(mDeviceInfo == null)
{
mDeviceInfo = new AirspyDeviceInformation();
}
/* Board ID */
try
{
int boardID = readByte(Command.BOARD_ID_READ, 0, 0, true);
mDeviceInfo.setBoardID(boardID);
}
catch(LibUsbException | UsbException e)
{
mLog.error("Error reading airspy board ID", e);
}
/* Version String */
try
{
//NOTE: libairspy is internally reading 127 bytes, however airspy_info
//script is telling it to read 255 bytes ... things that make you go hmmmm
byte[] version = readArray(Command.VERSION_STRING_READ, 0, 0, 127);
mDeviceInfo.setVersion(version);
}
catch(LibUsbException | UsbException e)
{
mLog.error("Error reading airspy version string", e);
}
/* Part ID and Serial Number */
try
{
//Read 6 x 32-bit integers = 24 bytes
byte[] serial = readArray(
Command.BOARD_PART_ID_SERIAL_NUMBER_READ, 0, 0, 24);
mDeviceInfo.setPartAndSerialNumber(serial);
}
catch(LibUsbException | UsbException e)
{
mLog.error("Error reading airspy version string", e);
}
}
/**
* Reads a single byte value from the device.
*
* @param command - airspy command
* @param value - value field for usb setup packet
* @param index - index field for usb setup packet
* @return - byte value as an integer
* @throws LibUsbException if the operation is unsuccesful
* @throws UsbException on any usb errors
*/
private int readByte(Command command, int value, int index, boolean signed)
throws LibUsbException, UsbException
{
if(mDeviceHandle != null)
{
ByteBuffer buffer = ByteBuffer.allocateDirect(1);
int transferred = LibUsb.controlTransfer(mDeviceHandle,
USB_REQUEST_IN,
command.getValue(),
(short) value,
(short) index,
buffer,
USB_TIMEOUT_MS);
if(transferred < 0)
{
throw new LibUsbException("read error", transferred);
}
byte result = buffer.get(0);
if(signed)
{
return (result & 0xFF);
}
else
{
return result;
}
}
else
{
throw new LibUsbException("device handle is null",
LibUsb.ERROR_NO_DEVICE);
}
}
/**
* Reads a multi-byte value from the device
*
* @param command - airspy command
* @param value - usb packet value
* @param index - usb packet index
* @param length - number of bytes to read
* @return - bytes read from the device
* @throws LibUsbException if quantity of bytes read doesn't equal the
* requested number of bytes
* @throws UsbException on error communicating with the device
*/
private byte[] readArray(Command command, int value, int index, int length)
throws LibUsbException, UsbException
{
if(mDeviceHandle != null)
{
ByteBuffer buffer = ByteBuffer.allocateDirect(length);
int transferred = LibUsb.controlTransfer(mDeviceHandle,
USB_REQUEST_IN,
command.getValue(),
(short) value,
(short) index,
buffer,
USB_TIMEOUT_MS);
if(transferred < 0)
{
throw new LibUsbException("read error", transferred);
}
byte[] results = new byte[transferred];
buffer.get(results);
return results;
}
else
{
throw new LibUsbException("device handle is null",
LibUsb.ERROR_NO_DEVICE);
}
}
/**
* Writes the buffer contents to the device
*
* @param command - airspy command
* @param value - usb packet value
* @param index - usb packet index
* @param buffer - data to write to the device
* @throws UsbException on error
*/
public void write(Command command, int value, int index, ByteBuffer buffer)
throws UsbException
{
if(mDeviceHandle != null)
{
int transferred = LibUsb.controlTransfer(mDeviceHandle,
USB_REQUEST_OUT,
command.getValue(),
(short) value,
(short) index,
buffer,
USB_TIMEOUT_MS);
if(transferred < 0)
{
throw new LibUsbException("error writing byte buffer",
transferred);
}
else if(transferred != buffer.capacity())
{
throw new LibUsbException("transferred bytes [" +
transferred + "] is not what was expected [" +
buffer.capacity() + "]", transferred);
}
}
else
{
throw new LibUsbException("device handle is null",
LibUsb.ERROR_NO_DEVICE);
}
}
public enum GainMode
{
LINEARITY,
SENSITIVITY,
CUSTOM;
}
public enum Gain
{
LINEARITY_1(1, 4, 0, 0),
LINEARITY_2(2, 5, 0, 0),
LINEARITY_3(3, 6, 1, 0),
LINEARITY_4(4, 7, 1, 0),
LINEARITY_5(5, 8, 1, 0),
LINEARITY_6(6, 9, 1, 0),
LINEARITY_7(7, 10, 2, 0),
LINEARITY_8(8, 10, 2, 1),
LINEARITY_9(9, 10, 0, 3),
LINEARITY_10(10, 10, 0, 5),
LINEARITY_11(11, 10, 1, 6),
LINEARITY_12(12, 10, 0, 8),
LINEARITY_13(13, 10, 0, 9),
LINEARITY_14(14, 10, 5, 8),
LINEARITY_15(15, 10, 6, 9),
LINEARITY_16(16, 11, 6, 9),
LINEARITY_17(17, 11, 7, 10),
LINEARITY_18(18, 11, 8, 12),
LINEARITY_19(19, 11, 9, 13),
LINEARITY_20(20, 11, 11, 14),
LINEARITY_21(21, 12, 12, 14),
LINEARITY_22(22, 13, 12, 14),
SENSITIVITY_1(1, 4, 0, 0),
SENSITIVITY_2(2, 4, 0, 1),
SENSITIVITY_3(3, 4, 0, 2),
SENSITIVITY_4(4, 4, 0, 3),
SENSITIVITY_5(5, 4, 1, 5),
SENSITIVITY_6(6, 4, 2, 6),
SENSITIVITY_7(7, 4, 2, 7),
SENSITIVITY_8(8, 4, 3, 8),
SENSITIVITY_9(9, 4, 4, 9),
SENSITIVITY_10(10, 5, 4, 9),
SENSITIVITY_11(11, 5, 4, 12),
SENSITIVITY_12(12, 5, 7, 12),
SENSITIVITY_13(13, 5, 8, 13),
SENSITIVITY_14(14, 5, 9, 14),
SENSITIVITY_15(15, 6, 9, 14),
SENSITIVITY_16(16, 7, 10, 14),
SENSITIVITY_17(17, 8, 10, 14),
SENSITIVITY_18(18, 9, 11, 14),
SENSITIVITY_19(19, 10, 12, 14),
SENSITIVITY_20(20, 11, 12, 14),
SENSITIVITY_21(21, 12, 12, 14),
SENSITIVITY_22(22, 13, 12, 14),
CUSTOM(1, 0, 0, 0);
private int mValue;
private int mIF;
private int mMixer;
private int mLNA;
private Gain(int value, int ifGain, int mixer, int lna)
{
mValue = value;
mIF = ifGain;
mMixer = mixer;
mLNA = lna;
}
public int getValue()
{
return mValue;
}
public int getIF()
{
return mIF;
}
public int getMixer()
{
return mMixer;
}
public int getLNA()
{
return mLNA;
}
public static Gain getGain(GainMode mode, int value)
{
assert (GAIN_MIN <= value && value <= GAIN_MAX);
switch(mode)
{
case LINEARITY:
for(Gain gain : getLinearityGains())
{
if(gain.getValue() == value)
{
return gain;
}
}
return LINEARITY_GAIN_DEFAULT;
case SENSITIVITY:
for(Gain gain : getSensitivityGains())
{
if(gain.getValue() == value)
{
return gain;
}
}
return SENSITIVITY_GAIN_DEFAULT;
case CUSTOM:
default:
return Gain.CUSTOM;
}
}
public static GainMode getGainMode(Gain gain)
{
if(gain == CUSTOM)
{
return GainMode.CUSTOM;
}
else if(getLinearityGains().contains(gain))
{
return GainMode.LINEARITY;
}
else if(getSensitivityGains().contains(gain))
{
return GainMode.SENSITIVITY;
}
return GainMode.CUSTOM;
}
public static EnumSet<Gain> getLinearityGains()
{
return EnumSet.range(LINEARITY_1, LINEARITY_22);
}
public static EnumSet<Gain> getSensitivityGains()
{
return EnumSet.range(SENSITIVITY_1, SENSITIVITY_22);
}
}
/**
* Airspy Board Identifier
*/
public enum BoardID
{
AIRSPY(0, "Airspy"),
UNKNOWN(-1, "Unknown");
private int mValue;
private String mLabel;
private BoardID(int value, String label)
{
mValue = value;
mLabel = label;
}
public int getValue()
{
return mValue;
}
public String getLabel()
{
return mLabel;
}
public static BoardID fromValue(int value)
{
if(value == 0)
{
return AIRSPY;
}
return UNKNOWN;
}
}
/**
* Airspy Commands
*/
public enum Command
{
INVALID(0),
RECEIVER_MODE(1),
SI5351C_WRITE(2),
SI5351C_READ(3),
R820T_WRITE(4),
R820T_READ(5),
SPIFLASH_ERASE(6),
SPIFLASH_WRITE(7),
SPIFLASH_READ(8),
BOARD_ID_READ(9),
VERSION_STRING_READ(10),
BOARD_PART_ID_SERIAL_NUMBER_READ(11),
SET_SAMPLE_RATE(12),
SET_FREQUENCY(13),
SET_LNA_GAIN(14),
SET_MIXER_GAIN(15),
SET_VGA_GAIN(16),
SET_LNA_AGC(17),
SET_MIXER_AGC(18),
MS_VENDOR_COMMAND(19),
SET_RF_BIAS_COMMAND(20),
GPIO_WRITE(21),
GPIO_READ(22),
GPIO_DIR__WRITE(23),
GPIO_DIR_READ(24),
GET_SAMPLE_RATES(25),
SET_PACKING(26);
private int mValue;
private Command(int value)
{
mValue = value;
}
public byte getValue()
{
return (byte) mValue;
}
public static Command fromValue(int value)
{
if(0 <= value && value <= 25)
{
return Command.values()[value];
}
return INVALID;
}
}
public enum ReceiverMode
{
OFF(0),
ON(1);
private int mValue;
private ReceiverMode(int value)
{
mValue = value;
}
public int getValue()
{
return mValue;
}
}
/**
* General Purpose Input/Output Ports (accessible on the airspy board)
*/
public enum GPIOPort
{
PORT_0(0),
PORT_1(1),
PORT_2(2),
PORT_3(3),
PORT_4(4),
PORT_5(5),
PORT_6(6),
PORT_7(7);
private int mValue;
private GPIOPort(int value)
{
mValue = value;
}
public int getValue()
{
return mValue;
}
}
/**
* General Purpose Input/Output Pins (accessible on the airspy board)
*/
public enum GPIOPin
{
PIN_0(0),
PIN_1(1),
PIN_2(2),
PIN_3(3),
PIN_4(4),
PIN_5(5),
PIN_6(6),
PIN_7(7),
PIN_8(8),
PIN_9(9),
PIN_10(10),
PIN_11(11),
PIN_12(12),
PIN_13(13),
PIN_14(14),
PIN_15(15),
PIN_16(16),
PIN_17(17),
PIN_18(18),
PIN_19(19),
PIN_20(20),
PIN_21(21),
PIN_22(22),
PIN_23(23),
PIN_24(24),
PIN_25(25),
PIN_26(26),
PIN_27(27),
PIN_28(28),
PIN_29(29),
PIN_30(30),
PIN_31(31);
private int mValue;
private GPIOPin(int value)
{
mValue = value;
}
public int getValue()
{
return mValue;
}
}
/**
* Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started.
*/
public void addListener(Listener<ComplexBuffer> listener)
{
if(mUSBTransferProcessor != null)
{
mUSBTransferProcessor.addListener(listener);
}
else
{
mLog.error("Couldn't add IQ buffer listener to Airspy tuner - processor is null");
}
}
/**
* Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners.
*/
public void removeListener(Listener<ComplexBuffer> listener)
{
if(mUSBTransferProcessor != null)
{
mUSBTransferProcessor.removeListener(listener);
}
else
{
mLog.error("Couldn't remove IQ buffer listener from Airspy tuner - processor is null");
}
}
}