mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-11 08:07:24 -05:00
WIP - identifier framework and restructuring P25 messages to provide
identifiers.
This commit is contained in:
@@ -760,26 +760,28 @@ public class FilterFactory
|
||||
* Polyphase M2 synthesizer sync filter. Designed for multiple M2 oversampled channel inputs and
|
||||
* an M*channel count output.
|
||||
*
|
||||
* @param channelBandwidth per channel
|
||||
* @param channels count
|
||||
* @param channelSampleRate per input channel
|
||||
* @param channelBandwidth per input channel
|
||||
* @param channels input count
|
||||
* @param tapsPerChannel minumum. This may be increased to meet the band edge -6.02dB requirement
|
||||
* @return filter
|
||||
* @throws FilterDesignException
|
||||
*/
|
||||
public static float[] getSincM2Synthesizer(double channelBandwidth, int channels, int tapsPerChannel)
|
||||
throws FilterDesignException
|
||||
public static float[] getSincM2Synthesizer(double channelSampleRate, double channelBandwidth, int channels,
|
||||
int tapsPerChannel) throws FilterDesignException
|
||||
{
|
||||
int filterLength = (channels * tapsPerChannel) - 1;
|
||||
|
||||
double cutoff = (channelBandwidth * 1.05) / (channelSampleRate * (double)channels);
|
||||
|
||||
//Design the prototype synthesizer with 105% of the channel bandwidth produced by the channelizer.
|
||||
float[] taps = FilterFactory.getKaiserSinc(filterLength, channelBandwidth * 1.05, 80.0);
|
||||
float[] taps = FilterFactory.getKaiserSinc(filterLength, cutoff, 80.0);
|
||||
|
||||
//This is an odd length filter - increase the length by 1 by pre-padding a zero coefficient
|
||||
float[] extendedTaps = new float[taps.length + 1];
|
||||
System.arraycopy(taps, 0, extendedTaps, 1, taps.length);
|
||||
|
||||
//Adjust the overall gain of the filter to an objective of 1.0 divided by the number of channels
|
||||
return normalize(extendedTaps, 1.0f);
|
||||
return extendedTaps;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -918,17 +920,18 @@ public class FilterFactory
|
||||
mLog.debug("Filter Length: " + (extendedTaps.length));
|
||||
mLog.debug("Requested Cutoff Frequency: " + (sampleRate * bandEdge));
|
||||
mLog.debug("Actual Cutoff Frequency: " + (sampleRate * cutoffFrequency));
|
||||
mLog.debug("Attenuation at 0.0 OBJECTIVE: " + 0.0);
|
||||
mLog.debug("Attenuation at 1.0 OBJECTIVE: " + PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE);
|
||||
mLog.debug("Attenuation at 1.00 Channels: " + evaluate(taps, bandEdge * 1.00) + "\tFrequency: " + (sampleRate * bandEdge * 1.0));
|
||||
mLog.debug("Attenuation at 1.25 Channels: " + evaluate(taps, bandEdge * 1.25) + "\tFrequency: " + (sampleRate * bandEdge * 1.25));
|
||||
mLog.debug("Attenuation at 1.50 Channels: " + evaluate(taps, bandEdge * 1.50) + "\tFrequency: " + (sampleRate * bandEdge * 1.5));
|
||||
mLog.debug("Attenuation at 1.75 Channels: " + evaluate(taps, bandEdge * 1.75) + "\tFrequency: " + (sampleRate * bandEdge * 1.75));
|
||||
mLog.debug("Attenuation at 2.00 Channels: " + evaluate(taps, bandEdge * 2.00) + "\tFrequency: " + (sampleRate * bandEdge * 2.0));
|
||||
mLog.debug("Attenuation at 0.25 Channels: " + evaluate(taps, bandEdge * 0.25) + "\tFrequency: " + (sampleRate * bandEdge * 0.25) + " [" + (bandEdge * 0.25) + "]");
|
||||
mLog.debug("Attenuation at 0.50 Channels: " + evaluate(taps, bandEdge * 0.50) + "\tFrequency: " + (sampleRate * bandEdge * 0.50) + " [" + (bandEdge * 0.50) + "]");
|
||||
mLog.debug("Attenuation at 0.75 Channels: " + evaluate(taps, bandEdge * 0.75) + "\tFrequency: " + (sampleRate * bandEdge * 0.75) + " [" + (bandEdge * 0.75) + "]");
|
||||
mLog.debug("Attenuation OBJECTIVE: " + PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE);
|
||||
mLog.debug("Attenuation at 1.00 Channels: " + evaluate(taps, bandEdge * 1.00) + "\tFrequency: " + (sampleRate * bandEdge * 1.0) + " [" + (bandEdge * 1.0) + "]");
|
||||
mLog.debug("Attenuation at 1.25 Channels: " + evaluate(taps, bandEdge * 1.25) + "\tFrequency: " + (sampleRate * bandEdge * 1.25) + " [" + (bandEdge * 1.25) + "]");
|
||||
mLog.debug("Attenuation at 1.50 Channels: " + evaluate(taps, bandEdge * 1.50) + "\tFrequency: " + (sampleRate * bandEdge * 1.5) + " [" + (bandEdge * 1.5) + "]");
|
||||
mLog.debug("Attenuation at 1.75 Channels: " + evaluate(taps, bandEdge * 1.75) + "\tFrequency: " + (sampleRate * bandEdge * 1.75) + " [" + (bandEdge * 1.75) + "]");
|
||||
mLog.debug("Attenuation at 2.00 Channels: " + evaluate(taps, bandEdge * 2.00) + "\tFrequency: " + (sampleRate * bandEdge * 2.0) + " [" + (bandEdge * 2.0) + "]");
|
||||
}
|
||||
|
||||
//Adjust the overall gain of the filter to an objective of 1.0 times the number of channels
|
||||
return normalize(extendedTaps, 1.0f * (float)channels);
|
||||
return extendedTaps;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -213,12 +213,14 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
switch(indexes.size())
|
||||
{
|
||||
case 1:
|
||||
return new OneChannelOutputProcessor(mChannelCalculator.getChannelSampleRate(), indexes);
|
||||
return new OneChannelOutputProcessor(mChannelCalculator.getChannelSampleRate(), indexes,
|
||||
mChannelCalculator.getChannelCount());
|
||||
case 2:
|
||||
try
|
||||
{
|
||||
float[] filter = getOutputProcessorFilter(2);
|
||||
return new TwoChannelOutputProcessor(mChannelCalculator.getChannelSampleRate(), indexes, filter);
|
||||
return new TwoChannelOutputProcessor(mChannelCalculator.getChannelSampleRate(), indexes, filter,
|
||||
mChannelCalculator.getChannelCount());
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
@@ -504,8 +506,8 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
|
||||
if(taps == null)
|
||||
{
|
||||
taps = FilterFactory.getSincM2Synthesizer(mChannelCalculator.getChannelBandwidth(), channels,
|
||||
POLYPHASE_SYNTHESIZER_TAPS_PER_CHANNEL);
|
||||
taps = FilterFactory.getSincM2Synthesizer(mChannelCalculator.getChannelSampleRate(),
|
||||
mChannelCalculator.getChannelBandwidth(), channels, POLYPHASE_SYNTHESIZER_TAPS_PER_CHANNEL);
|
||||
|
||||
mOutputProcessorFilters.put(channels, taps);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
private int mInputChannelCount;
|
||||
private IOscillator mFrequencyCorrectionMixer;
|
||||
private boolean mFrequencyCorrectionEnabled;
|
||||
private double mGain = 1.0;
|
||||
|
||||
/**
|
||||
* Base class for polyphase channelizer output channel processing. Provides built-in frequency translation
|
||||
@@ -51,9 +52,10 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
* @param sampleRate of the output channel. This is used to match the oscillator's sample rate to the output
|
||||
* channel sample rate for frequency translation/correction.
|
||||
*/
|
||||
public ChannelOutputProcessor(int inputChannelCount, double sampleRate)
|
||||
public ChannelOutputProcessor(int inputChannelCount, double sampleRate, double gain)
|
||||
{
|
||||
mInputChannelCount = inputChannelCount;
|
||||
mGain = gain;
|
||||
|
||||
//TODO: swap this out and use the LowPhaseNoiseOscillator
|
||||
mFrequencyCorrectionMixer = new Oscillator(0, sampleRate);
|
||||
@@ -62,6 +64,11 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
mChannelResultsQueue = new OverflowableReusableBufferTransferQueue<>((int)(sampleRate * 3), (int)(sampleRate * 0.5));
|
||||
}
|
||||
|
||||
protected double getGain()
|
||||
{
|
||||
return mGain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPolyphaseChannelIndexCount()
|
||||
{
|
||||
|
||||
@@ -38,10 +38,11 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor
|
||||
*
|
||||
* @param sampleRate of the output sample stream.
|
||||
* @param channelIndexes containing a single channel index.
|
||||
* @param gain value to apply. Typically this is the same as the channelizer's channel count.
|
||||
*/
|
||||
public OneChannelOutputProcessor(double sampleRate, List<Integer> channelIndexes)
|
||||
public OneChannelOutputProcessor(double sampleRate, List<Integer> channelIndexes, double gain)
|
||||
{
|
||||
super(1, sampleRate);
|
||||
super(1, sampleRate, gain);
|
||||
setPolyphaseChannelIndices(channelIndexes);
|
||||
}
|
||||
|
||||
@@ -91,6 +92,8 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor
|
||||
getFrequencyCorrectionMixer().mixComplex(channelBuffer.getSamples());
|
||||
}
|
||||
|
||||
channelBuffer.applyGain(getGain());
|
||||
|
||||
reusableComplexBufferAssembler.receive(channelBuffer);
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
|
||||
@@ -49,12 +49,13 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
*
|
||||
* @param sampleRate of the output sample stream.
|
||||
* @param channelIndexes containing two channel indices.
|
||||
* @param gain to apply to output. Typically this is equal to the channelizer's channel count.
|
||||
*/
|
||||
public TwoChannelOutputProcessor(double sampleRate, List<Integer> channelIndexes, float[] filter)
|
||||
public TwoChannelOutputProcessor(double sampleRate, List<Integer> channelIndexes, float[] filter, double gain)
|
||||
{
|
||||
//Set the frequency correction oscillator to 2 x output sample rate since we'll be correcting the frequency
|
||||
//after synthesizing both input channels
|
||||
super(2, sampleRate);
|
||||
super(2, sampleRate, gain);
|
||||
setPolyphaseChannelIndices(channelIndexes);
|
||||
setSynthesisFilter(filter);
|
||||
|
||||
@@ -141,6 +142,8 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
|
||||
ReusableComplexBuffer lowPassFiltered = mLowPassFilter.filter(synthesized);
|
||||
|
||||
lowPassFiltered.applyGain(getGain());
|
||||
|
||||
reusableComplexBufferAssembler.receive(lowPassFiltered);
|
||||
|
||||
buffer.decrementUserCount();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.github.dsheirer.dsp.filter.design;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
@@ -81,28 +81,39 @@ public class FilterViewer extends Application
|
||||
// catch(FilterDesignException fde)
|
||||
// {
|
||||
// mLog.error("Error");
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
|
||||
//
|
||||
// if(designer.isValid())
|
||||
// {
|
||||
// taps = designer.getImpulseResponse();
|
||||
// }
|
||||
// }
|
||||
// catch(FilterDesignException fde)
|
||||
// {
|
||||
// mLog.error("Filter design error", fde);
|
||||
// }
|
||||
//
|
||||
// if(taps == null)
|
||||
// {
|
||||
// throw new IllegalStateException("Couldn't design filter");
|
||||
// }
|
||||
|
||||
try
|
||||
{
|
||||
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
|
||||
// taps = FilterFactory.getSincM2Synthesizer(0.25, 2, 9);
|
||||
// taps = FilterFactory.getSincM2Channelizer(12500.0, 2, 9, true);
|
||||
taps = FilterFactory.getSincM2Synthesizer( 25000.0, 12500.0, 2, 9);
|
||||
|
||||
if(designer.isValid())
|
||||
{
|
||||
taps = designer.getImpulseResponse();
|
||||
}
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
catch(FilterDesignException e)
|
||||
{
|
||||
mLog.error("Filter design error", fde);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if(taps == null)
|
||||
{
|
||||
throw new IllegalStateException("Couldn't design filter");
|
||||
}
|
||||
|
||||
|
||||
return taps;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ public class SynthesizerViewer extends JFrame
|
||||
{
|
||||
try
|
||||
{
|
||||
float[] taps = FilterFactory.getSincM2Synthesizer(12500.0, 2, 12);
|
||||
float[] taps = FilterFactory.getSincM2Synthesizer( 25000.0, 12500.0, 2, 12);
|
||||
mSynthesizer = new TwoChannelSynthesizerM2(taps);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
|
||||
@@ -56,4 +56,17 @@ public class ReusableComplexBuffer extends ReusableFloatBuffer
|
||||
{
|
||||
return getSamples().length / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the gain value to the samples contained in this buffer
|
||||
*/
|
||||
public void applyGain(double gain)
|
||||
{
|
||||
float[] samples = getSamples();
|
||||
|
||||
for(int x = 0; x < samples.length; x++)
|
||||
{
|
||||
samples[x] *= gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,14 +416,17 @@ public class USBTransferProcessor implements TransferCallback
|
||||
|
||||
while(transfer != null)
|
||||
{
|
||||
if(mRunning.get() && mComplexBufferListener != null)
|
||||
if(mRunning.get())
|
||||
{
|
||||
ByteBuffer nativeBuffer = transfer.buffer();
|
||||
|
||||
ReusableComplexBuffer reusableComplexBuffer =
|
||||
mNativeBufferConverter.convert(nativeBuffer, transfer.actualLength());
|
||||
|
||||
mComplexBufferListener.receive(reusableComplexBuffer);
|
||||
if(mComplexBufferListener != null)
|
||||
{
|
||||
mComplexBufferListener.receive(reusableComplexBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
transfer.buffer().rewind();
|
||||
|
||||
Reference in New Issue
Block a user