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