#895 Cleanup and Testing of FCD Pro and Pro Plus tuners. (#896)

Co-authored-by: Denny <denny@denny-desktop>
This commit is contained in:
Denny Sheirer
2020-07-25 16:27:04 -04:00
committed by GitHub
parent 838016c89f
commit eaa3433034
7 changed files with 188 additions and 149 deletions

View File

@@ -20,7 +20,6 @@ import io.github.dsheirer.sample.adapter.RealChannelShortAdapter;
import io.github.dsheirer.sample.adapter.RealShortAdapter;
import io.github.dsheirer.source.config.SourceConfigMixer;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.MixerTunerDataLine;
import io.github.dsheirer.source.tuner.MixerTunerType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,7 +34,6 @@ import javax.sound.sampled.Port;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
@@ -46,7 +44,10 @@ public class MixerManager
{
private final static Logger mLog = LoggerFactory.getLogger(MixerManager.class);
public MixerManager() {}
public MixerManager()
{
}
public static RealMixerSource getSource(SourceConfiguration config)
{
@@ -193,33 +194,69 @@ public class MixerManager
return outputMixers;
}
public static Collection<MixerTunerDataLine> getMixerTunerDataLines()
/**
* Obtains a target data line for the specified mixer tuner type
* @param mixerTunerType to find
* @return target data line or null.
*/
public static TargetDataLine getTunerTargetDataLine(MixerTunerType mixerTunerType)
{
List<MixerTunerDataLine> tuners = new ArrayList<>();
for(Mixer.Info mixerInfo : AudioSystem.getMixerInfo())
{
//Sort between the mixers and the tuner mixers, and load each
MixerTunerType mixerTunerType = MixerTunerType.getMixerTunerType(mixerInfo);
MixerTunerType type = MixerTunerType.getMixerTunerType(mixerInfo);
if(mixerTunerType != MixerTunerType.UNKNOWN)
if(type != null && type == mixerTunerType)
{
TargetDataLine tdl = getTargetDataLine(mixerInfo, mixerTunerType.getAudioFormat());
Mixer mixer = AudioSystem.getMixer(mixerInfo);
if(tdl != null)
if(mixer != null)
{
switch(mixerTunerType)
for(Line.Info info : mixer.getTargetLineInfo())
{
case FUNCUBE_DONGLE_PRO:
case FUNCUBE_DONGLE_PRO_PLUS:
tuners.add(new MixerTunerDataLine(tdl, mixerTunerType));
break;
if(info instanceof DataLine.Info)
{
try
{
Line line = mixer.getLine(info);
if(line instanceof TargetDataLine)
{
return (TargetDataLine)line;
}
}
catch(LineUnavailableException lue)
{
mLog.error("Line Unavailable. Unable to get TargetDataLine for Mixer Tuner: " + mixerTunerType);
}
}
}
}
}
}
return tuners;
return null;
}
/**
* Finds the target data line supported format that most closely matches the specified audio format without
* matching sample rates.
* @param info for a data line
* @param audioFormat to find a match
* @return matching audio format or null.
*/
public static AudioFormat getMatchingFormat(DataLine.Info info, AudioFormat audioFormat)
{
for(AudioFormat audioFormatToTest: info.getFormats())
{
if(audioFormat.getSampleSizeInBits() == audioFormatToTest.getSampleSizeInBits() &&
audioFormat.getChannels() == audioFormatToTest.getChannels() &&
!(audioFormat.isBigEndian() ^ audioFormatToTest.isBigEndian()))
{
return audioFormatToTest;
}
}
return null;
}
private static TargetDataLine getTargetDataLine(Mixer.Info mixerInfo, AudioFormat format)
@@ -232,6 +269,20 @@ public class MixerManager
{
try
{
for(Line line: mixer.getTargetLines())
{
mLog.debug("Line: " + line.getLineInfo().toString());
}
for(Line line: mixer.getSourceLines())
{
mLog.debug("Line: " + line.getLineInfo().toString());
}
Mixer.Info info = mixer.getMixerInfo();
mLog.debug(info.toString());
DataLine.Info datalineInfo = new DataLine.Info(TargetDataLine.class, format);
retVal = (TargetDataLine)mixer.getLine(datalineInfo);
@@ -318,6 +369,11 @@ public class MixerManager
.append("\n");
Mixer mixer = AudioSystem.getMixer(mixerInfo);
Line.Info lineInfo1 = mixer.getLineInfo();
String a = lineInfo1.toString();
Line.Info[] infos = mixer.getTargetLineInfo();
Line.Info[] sourceLines = mixer.getSourceLineInfo();
@@ -337,6 +393,41 @@ public class MixerManager
.append("\n class:").append(lineInfo.getClass())
.append("\n lineclass:").append(lineInfo.getLineClass())
.append("\n");
if(lineInfo instanceof DataLine.Info)
{
DataLine.Info dli = (DataLine.Info)lineInfo;
for(AudioFormat format: dli.getFormats())
{
sb.append(" FORMAT:").append(format.toString()).append("\n");
if(mixerInfo.getName().startsWith("V"))
{
sb.append("Iterating formats for " + mixerInfo.getName() + " " + mixerInfo.getDescription()).append("\n");
try
{
Line line = mixer.getLine(lineInfo);
sb.append(line.getLineInfo().toString()).append("\n");
if(line instanceof TargetDataLine)
{
TargetDataLine tdl = (TargetDataLine)line;
tdl.open();
tdl.start();
byte[] bytes = new byte[1024];
int read = tdl.read(bytes, 0, bytes.length);
sb.append("READ:" + read).append("\n");
tdl.close();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
}
Line.Info portInfo = new Line.Info(Port.class);
@@ -389,9 +480,35 @@ public class MixerManager
public static void main(String[] args)
{
for(MixerChannelConfiguration config : getOutputMixers())
MixerTunerType mixerTunerType = MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS;
TargetDataLine targetDataLine = getTunerTargetDataLine(mixerTunerType);
if(targetDataLine != null)
{
mLog.debug(config.toString());
try
{
targetDataLine.open(mixerTunerType.getAudioFormat());
mLog.info(targetDataLine.getFormat().toString());
targetDataLine.start();
byte[] bytes = new byte[1024];
int read = targetDataLine.read(bytes, 0, bytes.length);
while(read > 0)
{
mLog.debug("Read:" + read);
read = 0;
// read = targetDataLine.read(bytes, 0, bytes.length);
}
targetDataLine.close();
mLog.debug("Done!");
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}

View File

@@ -66,10 +66,9 @@ public class MixerReader<T extends ReusableFloatBuffer> implements Runnable
mBufferSize = (int)(mAudioFormat.getSampleRate() * 0.1) * mAudioFormat.getFrameSize();
}
public MixerReader(AudioFormat audioFormat, TargetDataLine targetDataLine,
AbstractSampleAdapter<T> abstractSampleAdapter)
public MixerReader(AudioFormat audioFormat, TargetDataLine targetDataLine, AbstractSampleAdapter<T> abstractSampleAdapter)
{
this(audioFormat, targetDataLine, abstractSampleAdapter, null);
this(audioFormat, targetDataLine, abstractSampleAdapter, new HeartbeatManager());
}
/**
@@ -104,8 +103,12 @@ public class MixerReader<T extends ReusableFloatBuffer> implements Runnable
mBuffer = new byte[mBufferSize];
mTargetDataLine.open(mAudioFormat);
mTargetDataLine.open(getAudioFormat());
mLog.info("TDL Open:" + mTargetDataLine.isOpen() + " Active:" + mTargetDataLine.isActive() + " Running:" + mTargetDataLine.isRunning());
mLog.info("Format:" + mTargetDataLine.getFormat().toString());
mTargetDataLine.start();
mLog.info("TDL Open:" + mTargetDataLine.isOpen() + " Active:" + mTargetDataLine.isActive() + " Running:" + mTargetDataLine.isRunning());
}
/**
@@ -115,7 +118,15 @@ public class MixerReader<T extends ReusableFloatBuffer> implements Runnable
{
if(mTargetDataLine != null && mTargetDataLine.isOpen())
{
mTargetDataLine.close();
if(mTargetDataLine.isRunning())
{
mTargetDataLine.stop();
}
if(mTargetDataLine.isOpen())
{
mTargetDataLine.close();
}
}
}
@@ -215,9 +226,9 @@ public class MixerReader<T extends ReusableFloatBuffer> implements Runnable
}
}
}
catch(Exception e)
catch(Throwable t)
{
mLog.error("MixerSource - error while reading from the mixer target data line", e);
mLog.error("MixerSource - error while reading from the mixer target data line", t);
stop();
}
}

View File

@@ -1,42 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 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 io.github.dsheirer.source.tuner;
import javax.sound.sampled.TargetDataLine;
public class MixerTunerDataLine
{
private TargetDataLine mTargetDataLine;
protected MixerTunerType mMixerTunerType;
public MixerTunerDataLine( TargetDataLine line, MixerTunerType type )
{
mTargetDataLine = line;
mMixerTunerType = type;
}
public TargetDataLine getTargetDataLine()
{
return mTargetDataLine;
}
public MixerTunerType getMixerTunerType()
{
return mMixerTunerType;
}
}

View File

@@ -43,8 +43,8 @@ import org.usb4java.DeviceDescriptor;
import org.usb4java.DeviceList;
import org.usb4java.LibUsb;
import javax.sound.sampled.TargetDataLine;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -285,13 +285,11 @@ public class TunerManager
}
}
private TunerInitStatus initTuner(Device device,
DeviceDescriptor descriptor)
private TunerInitStatus initTuner(Device device, DeviceDescriptor descriptor)
{
if(device != null && descriptor != null)
{
TunerClass tunerClass = TunerClass.valueOf(descriptor.idVendor(),
descriptor.idProduct());
TunerClass tunerClass = TunerClass.valueOf(descriptor.idVendor(), descriptor.idProduct());
switch(tunerClass)
{
@@ -358,8 +356,7 @@ public class TunerManager
}
}
private TunerInitStatus initAirspyTuner(Device device,
DeviceDescriptor descriptor)
private TunerInitStatus initAirspyTuner(Device device, DeviceDescriptor descriptor)
{
try
{
@@ -381,35 +378,30 @@ public class TunerManager
}
private TunerInitStatus initEttusB100Tuner(Device device,
DeviceDescriptor descriptor)
private TunerInitStatus initEttusB100Tuner(Device device, DeviceDescriptor descriptor)
{
return new TunerInitStatus(null, "Ettus B100 tuner not currently "
+ "supported");
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());
TargetDataLine tdl = MixerManager.getTunerTargetDataLine(MixerTunerType.FUNCUBE_DONGLE_PRO);
if(dataline != null)
if(tdl != null)
{
FCD1TunerController controller = new FCD1TunerController(dataline, device, descriptor);
FCD1TunerController controller = new FCD1TunerController(tdl, device, descriptor);
try
{
controller.init();
FCDTuner tuner = new FCDTuner(controller, mUserPreferences);
return new TunerInitStatus(tuner, "LOADED");
}
catch(SourceException e)
{
mLog.error("couldn't load funcube dongle pro tuner", e);
reason = "error during initialization - " + e.getLocalizedMessage();
}
}
@@ -418,15 +410,14 @@ public class TunerManager
reason = "couldn't find matching mixer dataline";
}
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not "
+ "loaded - " + reason);
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());
TargetDataLine dataline = MixerManager.getTunerTargetDataLine(MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS);
if(dataline != null)
{
@@ -435,17 +426,13 @@ public class TunerManager
try
{
controller.init();
FCDTuner tuner = new FCDTuner(controller, mUserPreferences);
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();
reason = "error during initialization - " + e.getLocalizedMessage();
}
}
else
@@ -453,8 +440,7 @@ public class TunerManager
reason = "couldn't find matching mixer dataline";
}
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not "
+ "loaded - " + reason);
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not loaded - " + reason);
}
private TunerInitStatus initHackRFTuner(Device device, DeviceDescriptor descriptor)
@@ -473,14 +459,11 @@ public class TunerManager
{
mLog.error("couldn't construct HackRF controller/tuner", se);
return new TunerInitStatus(null,
"error constructing HackRF tuner controller");
return new TunerInitStatus(null, "error constructing HackRF tuner controller");
}
}
private TunerInitStatus initRTL2832Tuner(TunerClass tunerClass,
Device device,
DeviceDescriptor deviceDescriptor)
private TunerInitStatus initRTL2832Tuner(TunerClass tunerClass, Device device, DeviceDescriptor deviceDescriptor)
{
String reason = "NOT LOADED";
@@ -540,8 +523,7 @@ public class TunerManager
case RAFAELMICRO_R828D:
case UNKNOWN:
default:
reason = "SDRTRunk doesn't currently support RTL2832 "
+ "Dongle with [" + tunerType.toString() +
reason = "SDRTRunk doesn't currently support RTL2832 Dongle with [" + tunerType.toString() +
"] tuner for tuner class[" + tunerClass.toString() + "]";
break;
}
@@ -549,32 +531,6 @@ public class TunerManager
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 = MixerManager.getMixerTunerDataLines();
for(MixerTunerDataLine mixerTDL : datalines)
{
if(mixerTDL.getMixerTunerType().getTunerClass() == tunerClass)
{
return mixerTDL;
}
}
return null;
}
public class TunerInitStatus
{
private Tuner mTuner;

View File

@@ -23,7 +23,7 @@ import io.github.dsheirer.sample.adapter.ComplexShortAdapter;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.mixer.ComplexMixer;
import io.github.dsheirer.source.tuner.MixerTunerDataLine;
import io.github.dsheirer.source.tuner.MixerTunerType;
import io.github.dsheirer.source.tuner.TunerClass;
import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.source.tuner.TunerType;
@@ -36,7 +36,7 @@ import org.usb4java.DeviceHandle;
import org.usb4java.LibUsb;
import org.usb4java.LibUsbException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.TargetDataLine;
import javax.usb.UsbClaimException;
import javax.usb.UsbException;
import java.nio.ByteBuffer;
@@ -71,8 +71,8 @@ public abstract class FCDTunerController extends TunerController
* @param minTunableFrequency
* @param maxTunableFrequency
*/
public FCDTunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor, String tunerName,
int minTunableFrequency, int maxTunableFrequency, AudioFormat audioFormat)
public FCDTunerController(MixerTunerType tunerType, TargetDataLine mixerTDL, Device device,
DeviceDescriptor descriptor, int minTunableFrequency, int maxTunableFrequency)
{
super(minTunableFrequency, maxTunableFrequency, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT);
mDevice = device;
@@ -80,17 +80,17 @@ public abstract class FCDTunerController extends TunerController
try
{
mFrequencyController.setSampleRate((int)audioFormat.getSampleRate());
mFrequencyController.setSampleRate((int)tunerType.getAudioFormat().getSampleRate());
}
catch(SourceException se)
{
mLog.error("Error setting sample rate to [" + audioFormat.getSampleRate() + "]", se);
mLog.error("Error setting sample rate to [" + tunerType.getAudioFormat().getSampleRate() + "]", se);
}
mComplexMixer = new ComplexMixer( mixerTDL.getTargetDataLine(), audioFormat, tunerName,
new ComplexShortAdapter(mixerTDL.getMixerTunerType().getDisplayString()));
mComplexMixer = new ComplexMixer( mixerTDL, tunerType.getAudioFormat(), tunerType.getDisplayString(),
new ComplexShortAdapter(tunerType.getDisplayString()));
mComplexMixer.setBufferSize(audioFormat.getFrameSize() * getBufferSampleCount());
mComplexMixer.setBufferSize(tunerType.getAudioFormat().getFrameSize() * getBufferSampleCount());
mComplexMixer.setBufferListener(mReusableBufferBroadcaster);
}

View File

@@ -18,7 +18,6 @@
package io.github.dsheirer.source.tuner.fcd.proV1;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.MixerTunerDataLine;
import io.github.dsheirer.source.tuner.MixerTunerType;
import io.github.dsheirer.source.tuner.TunerClass;
import io.github.dsheirer.source.tuner.TunerType;
@@ -30,6 +29,7 @@ import org.slf4j.LoggerFactory;
import org.usb4java.Device;
import org.usb4java.DeviceDescriptor;
import javax.sound.sampled.TargetDataLine;
import javax.usb.UsbClaimException;
import javax.usb.UsbException;
import java.nio.ByteBuffer;
@@ -53,10 +53,10 @@ public class FCD1TunerController extends FCDTunerController
private FCD1TunerConfiguration mTunerConfiguration;
private FCD1TunerEditor mEditor;
public FCD1TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
public FCD1TunerController(TargetDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
{
super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO.getDisplayString(), MINIMUM_TUNABLE_FREQUENCY,
MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat());
super(MixerTunerType.FUNCUBE_DONGLE_PRO, mixerTDL, device, descriptor,
MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY);
}
public void init() throws SourceException
@@ -78,10 +78,7 @@ public class FCD1TunerController extends FCDTunerController
}
catch(Exception e)
{
e.printStackTrace();
throw new SourceException("FCDTunerController error " +
"during construction", e);
throw new SourceException("FCDTunerController error during construction", e);
}
}

View File

@@ -19,7 +19,6 @@
package io.github.dsheirer.source.tuner.fcd.proplusV2;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.MixerTunerDataLine;
import io.github.dsheirer.source.tuner.MixerTunerType;
import io.github.dsheirer.source.tuner.TunerClass;
import io.github.dsheirer.source.tuner.TunerType;
@@ -31,6 +30,7 @@ import org.slf4j.LoggerFactory;
import org.usb4java.Device;
import org.usb4java.DeviceDescriptor;
import javax.sound.sampled.TargetDataLine;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -42,10 +42,10 @@ public class FCD2TunerController extends FCDTunerController
public static final int MAXIMUM_TUNABLE_FREQUENCY = 2050000000;
public static final int SAMPLE_RATE = 192000;
public FCD2TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
public FCD2TunerController(TargetDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
{
super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getDisplayString(),
MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getAudioFormat());
super(MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS, mixerTDL, device, descriptor,
MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY);
}
public void init() throws SourceException