mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-10 23:57:25 -05:00
Compare commits
57 Commits
v0.4.0-alp
...
v0.4.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d95c78c0f1 | ||
|
|
aee5ece8d2 | ||
|
|
ba358a7d48 | ||
|
|
09b209496b | ||
|
|
cc209683bd | ||
|
|
2771c28dba | ||
|
|
d752917073 | ||
|
|
28bc02e172 | ||
|
|
0f6044d48b | ||
|
|
58ecb83e2c | ||
|
|
9e1de922e4 | ||
|
|
84d0b827d8 | ||
|
|
a17132be0d | ||
|
|
dae68515a5 | ||
|
|
0aaec8ff54 | ||
|
|
001883edc2 | ||
|
|
335fb51456 | ||
|
|
e94b6ae172 | ||
|
|
785125da4f | ||
|
|
81b8f5a423 | ||
|
|
bf186a7408 | ||
|
|
43fc326f93 | ||
|
|
7cdbb75b42 | ||
|
|
96a56cab68 | ||
|
|
29d4b36cf7 | ||
|
|
b15a3e59a0 | ||
|
|
9950e46e5a | ||
|
|
570321da3e | ||
|
|
bb0bcd3198 | ||
|
|
cb54850f99 | ||
|
|
5bb8c46bd5 | ||
|
|
2a3fd2c5a3 | ||
|
|
d712feb45a | ||
|
|
fbb24a4c05 | ||
|
|
3992f8178c | ||
|
|
8642def77e | ||
|
|
38e6de6cb2 | ||
|
|
994e78f42b | ||
|
|
b8a46af836 | ||
|
|
1c6d19959a | ||
|
|
50bc3088b7 | ||
|
|
09356848eb | ||
|
|
316bfb19c2 | ||
|
|
c045c8f16d | ||
|
|
8946d789ff | ||
|
|
19399d9138 | ||
|
|
9acdf22b91 | ||
|
|
f963d821a1 | ||
|
|
ade0cef50b | ||
|
|
2044c2a2de | ||
|
|
1c312a50b6 | ||
|
|
54f6bd09e2 | ||
|
|
5f8f2ec330 | ||
|
|
d7e018f66e | ||
|
|
c2a232fdee | ||
|
|
c5d2dcd0cf | ||
|
|
8e4e4129ca |
@@ -15,7 +15,7 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
version = '0.4.0-alpha.2'
|
||||
version = '0.4.0-alpha.5'
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -27,7 +27,7 @@ import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.slf4j.LoggerFactory;
|
||||
* This class is designed to support 8 kHz sample rate demodulated audio.
|
||||
*/
|
||||
public class AudioModule extends Module implements IAudioPacketProvider, IReusableBufferListener,
|
||||
ISquelchStateListener, Listener<ReusableBuffer>
|
||||
ISquelchStateListener, Listener<ReusableFloatBuffer>
|
||||
{
|
||||
protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
|
||||
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("AudioModule");
|
||||
@@ -139,11 +139,11 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
|
||||
{
|
||||
ReusableBuffer highPassFiltered = mHighPassFilter.filter(reusableBuffer);
|
||||
ReusableFloatBuffer highPassFiltered = mHighPassFilter.filter(reusableFloatBuffer);
|
||||
|
||||
ReusableAudioPacket audioPacket = mAudioPacketQueue.getBuffer(highPassFiltered.getSampleCount());
|
||||
audioPacket.loadAudioFrom(highPassFiltered);
|
||||
@@ -155,7 +155,7 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
||||
}
|
||||
else
|
||||
{
|
||||
reusableBuffer.decrementUserCount();
|
||||
reusableFloatBuffer.decrementUserCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package io.github.dsheirer.channel.metadata;
|
||||
|
||||
public enum Attribute
|
||||
{
|
||||
BUFFER_OVERFLOW,
|
||||
CHANNEL_CONFIGURATION_NAME,
|
||||
CHANNEL_CONFIGURATION_SITE,
|
||||
CHANNEL_CONFIGURATION_SYSTEM,
|
||||
|
||||
@@ -116,6 +116,16 @@ public class AttributeChangeRequest<T>
|
||||
return null;
|
||||
}
|
||||
|
||||
public Boolean getBooleanValue()
|
||||
{
|
||||
if(mValue instanceof Boolean)
|
||||
{
|
||||
return (Boolean)mValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value as a DecoderType or null if the value is not a DecoderType instance.
|
||||
*/
|
||||
|
||||
@@ -123,6 +123,11 @@ public class AttributeMonitor<T>
|
||||
{
|
||||
mCurrentValue = null;
|
||||
mOccurrenceCounts.clear();
|
||||
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive(new AttributeChangeRequest(mAttribute, null, null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 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.
|
||||
* 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/>
|
||||
* 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.channel.metadata;
|
||||
@@ -21,9 +18,11 @@ package io.github.dsheirer.channel.metadata;
|
||||
import io.github.dsheirer.channel.state.State;
|
||||
import io.github.dsheirer.controller.channel.Channel;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.awt.*;
|
||||
import java.awt.EventQueue;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -32,6 +31,8 @@ import java.util.Map;
|
||||
|
||||
public class ChannelMetadataModel extends AbstractTableModel implements Listener<MutableMetadataChangeEvent>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelMetadataModel.class);
|
||||
|
||||
private final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.00000" );
|
||||
|
||||
public static final int COLUMN_STATE = 0;
|
||||
@@ -45,7 +46,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
|
||||
public static final int COLUMN_CONFIGURATION_NAME = 8;
|
||||
public static final int COLUMN_MESSAGE = 9;
|
||||
|
||||
private static final String[] COLUMNS = {"State", "Decoder", "Channel", "Frequency", "Primary From", "Primary To",
|
||||
private static final String[] COLUMNS = {"Status", "Decoder", "Channel", "Frequency", "Primary From", "Primary To",
|
||||
"Secondary From", "Secondary To", "Channel Name", "Message"};
|
||||
|
||||
private List<MutableMetadata> mChannelMetadata = new ArrayList();
|
||||
@@ -184,7 +185,11 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
|
||||
|
||||
return null;
|
||||
case COLUMN_MESSAGE:
|
||||
if(metadata.hasMessageType() | metadata.hasMessage())
|
||||
if(metadata.isBufferOverflow())
|
||||
{
|
||||
return "**OVERFLOW**";
|
||||
}
|
||||
else if(metadata.hasMessageType() || metadata.hasMessage())
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(metadata.hasMessageType())
|
||||
@@ -248,6 +253,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
|
||||
case CHANNEL_STATE:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_STATE);
|
||||
break;
|
||||
case BUFFER_OVERFLOW:
|
||||
case MESSAGE:
|
||||
case MESSAGE_TYPE:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_MESSAGE);
|
||||
|
||||
@@ -25,32 +25,36 @@ import io.github.dsheirer.controller.channel.ChannelProcessingManager;
|
||||
import io.github.dsheirer.controller.channel.ChannelUtils;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.module.ProcessingChain;
|
||||
import io.github.dsheirer.properties.SystemProperties;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChannelMetadataPanel extends JPanel implements ListSelectionListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelMetadataPanel.class);
|
||||
|
||||
private static final String PROPERTY_PREFIX_BACKGROUND = "channel.metadata.panel.state.color.";
|
||||
private static final String PROPERTY_PREFIX_FOREGROUND = "channel.metadata.panel.text.color.";
|
||||
private ChannelProcessingManager mChannelProcessingManager;
|
||||
private IconManager mIconManager;
|
||||
private JTable mTable;
|
||||
private Broadcaster<ProcessingChain> mSelectedProcessingChainBroadcaster = new Broadcaster<>();
|
||||
private Map<State,Color> mBackgroundColors = new HashMap<>();
|
||||
private Map<State,Color> mForegroundColors = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Table view for currently decoding channel metadata
|
||||
@@ -94,7 +98,32 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
||||
|
||||
add(scrollPane);
|
||||
|
||||
// new TableColumnWidthMonitor(mTable, PROPERTY_PREFIX, new int[] {15,15,15,15,25,25,25,25,15,40});
|
||||
setColors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the background and foreground color palette for the various channel states.
|
||||
*/
|
||||
private void setColors()
|
||||
{
|
||||
mBackgroundColors.put(State.ACTIVE, Color.CYAN);
|
||||
mForegroundColors.put(State.ACTIVE, Color.YELLOW);
|
||||
mBackgroundColors.put(State.CALL, Color.BLUE);
|
||||
mForegroundColors.put(State.CALL, Color.YELLOW);
|
||||
mBackgroundColors.put(State.CONTROL, Color.ORANGE);
|
||||
mForegroundColors.put(State.CONTROL, Color.BLUE);
|
||||
mBackgroundColors.put(State.DATA, Color.GREEN);
|
||||
mForegroundColors.put(State.DATA, Color.BLUE);
|
||||
mBackgroundColors.put(State.ENCRYPTED, Color.MAGENTA);
|
||||
mForegroundColors.put(State.ENCRYPTED, Color.WHITE);
|
||||
mBackgroundColors.put(State.FADE, Color.LIGHT_GRAY);
|
||||
mForegroundColors.put(State.FADE, Color.DARK_GRAY);
|
||||
mBackgroundColors.put(State.IDLE, Color.WHITE);
|
||||
mForegroundColors.put(State.IDLE, Color.DARK_GRAY);
|
||||
mBackgroundColors.put(State.RESET, Color.PINK);
|
||||
mForegroundColors.put(State.RESET, Color.YELLOW);
|
||||
mBackgroundColors.put(State.TEARDOWN, Color.DARK_GRAY);
|
||||
mForegroundColors.put(State.TEARDOWN, Color.WHITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -185,44 +214,14 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
||||
State state = (State)value;
|
||||
label.setText(state.getDisplayValue());
|
||||
|
||||
switch(state)
|
||||
if(mBackgroundColors.containsKey(state))
|
||||
{
|
||||
case CALL:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "call", Color.BLUE);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "call", Color.YELLOW);
|
||||
break;
|
||||
case CONTROL:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "control", Color.ORANGE);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "control", Color.BLUE);
|
||||
break;
|
||||
case DATA:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "data", Color.GREEN);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "data", Color.BLUE);
|
||||
break;
|
||||
case ENCRYPTED:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "encrypted", Color.MAGENTA);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "encrypted", Color.WHITE);
|
||||
break;
|
||||
case FADE:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "fade", Color.LIGHT_GRAY.BLUE);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "fade", Color.YELLOW);
|
||||
break;
|
||||
case IDLE:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE);
|
||||
break;
|
||||
case OVERFLOW:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "overflow", Color.RED);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "overflow", Color.YELLOW);
|
||||
break;
|
||||
case RESET:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW);
|
||||
break;
|
||||
case TEARDOWN:
|
||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "teardown", Color.DARK_GRAY);
|
||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "teardown", Color.WHITE);
|
||||
break;
|
||||
background = mBackgroundColors.get(state);
|
||||
}
|
||||
|
||||
if(mForegroundColors.containsKey(state))
|
||||
{
|
||||
foreground = mForegroundColors.get(state);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -41,6 +41,7 @@ public class Metadata
|
||||
protected boolean mUpdated;
|
||||
protected DecoderType mPrimaryDecoderType;
|
||||
protected boolean mSelected;
|
||||
protected boolean mBufferOverflow;
|
||||
protected State mState = State.IDLE;
|
||||
protected String mChannelConfigurationSystem;
|
||||
protected String mChannelConfigurationSite;
|
||||
@@ -173,6 +174,14 @@ public class Metadata
|
||||
return mSelected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the channel for this metadata is in a buffer overflow state.
|
||||
*/
|
||||
public boolean isBufferOverflow()
|
||||
{
|
||||
return mBufferOverflow;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of de-duplicated broadcast channels aggregated from the primary and secondary TO/FROM aliases.
|
||||
*/
|
||||
|
||||
@@ -80,6 +80,10 @@ public class MutableMetadata extends Metadata implements Listener<AttributeChang
|
||||
{
|
||||
switch(request.getAttribute())
|
||||
{
|
||||
case BUFFER_OVERFLOW:
|
||||
mBufferOverflow = request.getBooleanValue();
|
||||
broadcast(Attribute.BUFFER_OVERFLOW);
|
||||
break;
|
||||
case CHANNEL_CONFIGURATION_SYSTEM:
|
||||
mChannelConfigurationSystem = request.getStringValue();
|
||||
broadcast(Attribute.CHANNEL_CONFIGURATION_SYSTEM);
|
||||
@@ -164,6 +168,7 @@ public class MutableMetadata extends Metadata implements Listener<AttributeChang
|
||||
public void resetTemporalAttributes()
|
||||
{
|
||||
mDoNotRecord = null;
|
||||
mBufferOverflow = false;
|
||||
|
||||
if(hasMessage())
|
||||
{
|
||||
|
||||
@@ -135,18 +135,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
mMutableMetadata.resetAllAttributes();
|
||||
|
||||
mTrafficChannelEndListener = null;
|
||||
|
||||
if(mTrafficChannelCallEvent != null)
|
||||
{
|
||||
mTrafficChannelCallEvent.end();
|
||||
broadcast(mTrafficChannelCallEvent);
|
||||
}
|
||||
|
||||
mTrafficChannelCallEvent = null;
|
||||
|
||||
processTeardownState();
|
||||
mSquelchLocked = false;
|
||||
}
|
||||
|
||||
@@ -303,6 +292,11 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case ACTIVE:
|
||||
broadcast(SquelchState.SQUELCH);
|
||||
updateFadeTimeout();
|
||||
mState = state;
|
||||
break;
|
||||
case CONTROL:
|
||||
//Don't allow traffic channels to be control channels, otherwise they can't transition to teardown
|
||||
if(isStandardChannel())
|
||||
@@ -339,8 +333,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
||||
break;
|
||||
}
|
||||
|
||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
|
||||
mSourceOverflow ? State.OVERFLOW : mState));
|
||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,8 +356,19 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
||||
{
|
||||
mSourceOverflow = overflow;
|
||||
|
||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
|
||||
mSourceOverflow ? State.OVERFLOW : mState));
|
||||
mMutableMetadata.receive(new AttributeChangeRequest<Boolean>(Attribute.BUFFER_OVERFLOW, mSourceOverflow));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this channel's sample buffer is in overflow state, meaning that the inbound sample
|
||||
* stream is not being processed fast enough and samples are being thrown away until the processing can
|
||||
* catch up.
|
||||
*
|
||||
* @return true if the channel is in overflow state.
|
||||
*/
|
||||
public boolean isOverflow()
|
||||
{
|
||||
return mSourceOverflow;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,9 +407,13 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
||||
|
||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
||||
|
||||
if(mTrafficChannelEndListener != null)
|
||||
if(mTrafficChannelEndListener != null && mTrafficChannelCallEvent != null)
|
||||
{
|
||||
mTrafficChannelCallEvent.end();
|
||||
broadcast(mTrafficChannelCallEvent);
|
||||
mTrafficChannelEndListener.callEnd(mTrafficChannelCallEvent.getChannel());
|
||||
mTrafficChannelEndListener = null;
|
||||
mTrafficChannelCallEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,62 +24,109 @@ import java.util.EnumSet;
|
||||
*/
|
||||
public enum State
|
||||
{
|
||||
IDLE("IDLE")
|
||||
/**
|
||||
* Active state indicates that valid messages are being decoded that do not indicate any call or data
|
||||
* activity. For example, this is used for P25 when a channel is actively transmitting Terminator Data
|
||||
* Units in between data packets or at the beginning or end of a call.
|
||||
*/
|
||||
ACTIVE("ACTIVE")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state != TEARDOWN && state != RESET && state != OVERFLOW;
|
||||
return state == CALL ||
|
||||
state == CONTROL ||
|
||||
state == DATA ||
|
||||
state == ENCRYPTED ||
|
||||
state == FADE ||
|
||||
state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Indicates that call messages are being decoded, or a call is in progress (ie producing audio).
|
||||
*/
|
||||
CALL("CALL")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == CONTROL || state == DATA || state == ENCRYPTED || state == FADE || state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
DATA("DATA")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == CALL || state == CONTROL || state == ENCRYPTED || state == FADE || state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
ENCRYPTED("ENCRYPTED")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == FADE || state == TEARDOWN;
|
||||
return state == ACTIVE ||
|
||||
state == CONTROL ||
|
||||
state == DATA ||
|
||||
state == ENCRYPTED ||
|
||||
state == FADE ||
|
||||
state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Indicates a trunking control channel is being decoded.
|
||||
*/
|
||||
CONTROL("CONTROL")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == IDLE || state == FADE;
|
||||
return state == IDLE ||
|
||||
state == FADE;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Indicates data packets are being decoded
|
||||
*/
|
||||
DATA("DATA")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == ACTIVE ||
|
||||
state == CALL ||
|
||||
state == CONTROL ||
|
||||
state == ENCRYPTED ||
|
||||
state == FADE ||
|
||||
state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Indicates encrypted audio is detected.
|
||||
*/
|
||||
ENCRYPTED("ENCRYPTED")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == FADE ||
|
||||
state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Fade state occurs when the channel is no longer actively decoding an messages. This is a transitory
|
||||
* state before transition to IDLE or back to an active state.
|
||||
*/
|
||||
FADE("FADE")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state != FADE && state != RESET && state != OVERFLOW;
|
||||
return state != FADE &&
|
||||
state != RESET;
|
||||
}
|
||||
},
|
||||
OVERFLOW("OVERFLOW")
|
||||
/**
|
||||
* Indicates the channel is idle and not actively decoding any messages
|
||||
*/
|
||||
IDLE("IDLE")
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return false;
|
||||
return state != TEARDOWN &&
|
||||
state != RESET;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates that the channel has been reset and is ready to use for a new decoding session.
|
||||
*/
|
||||
RESET("RESET")
|
||||
{
|
||||
@Override
|
||||
@@ -88,6 +135,10 @@ public enum State
|
||||
return state == IDLE;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Call or data teardown. This state is used by traffic channels to indicate that a call or data session
|
||||
* has concluded and the channel is ready for tear down and reuse.
|
||||
*/
|
||||
TEARDOWN("TEARDOWN")
|
||||
{
|
||||
@Override
|
||||
@@ -99,7 +150,7 @@ public enum State
|
||||
|
||||
private String mDisplayValue;
|
||||
|
||||
public static final EnumSet<State> CALL_STATES = EnumSet.of(CALL, CONTROL, DATA, ENCRYPTED);
|
||||
public static final EnumSet<State> CALL_STATES = EnumSet.of(ACTIVE, CALL, CONTROL, DATA, ENCRYPTED);
|
||||
public static final EnumSet<State> IDLE_STATES = EnumSet.of(IDLE, FADE);
|
||||
|
||||
private State(String displayValue)
|
||||
@@ -124,4 +175,10 @@ public enum State
|
||||
{
|
||||
return mDisplayValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mDisplayValue;
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
|
||||
if(frequency > 0)
|
||||
{
|
||||
Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(frequency,
|
||||
mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
|
||||
mDecodeConfiguration.getChannelSpecification().getBandwidth()));
|
||||
|
||||
if(channel != null)
|
||||
{
|
||||
|
||||
@@ -489,14 +489,14 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration;
|
||||
|
||||
mTunerChannel = new TunerChannel(config.getFrequency(),
|
||||
mDecodeConfiguration.getDecoderType().getChannelBandwidth());
|
||||
mDecodeConfiguration.getChannelSpecification().getBandwidth());
|
||||
}
|
||||
else if(mSourceConfiguration.getSourceType() == SourceType.RECORDING)
|
||||
{
|
||||
SourceConfigRecording config = (SourceConfigRecording)mSourceConfiguration;
|
||||
|
||||
mTunerChannel = new TunerChannel(config.getFrequency(),
|
||||
mDecodeConfiguration.getDecoderType().getChannelBandwidth());
|
||||
mDecodeConfiguration.getChannelSpecification().getBandwidth());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.controller.channel;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
@@ -24,8 +39,7 @@ import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class ChannelController extends JPanel
|
||||
implements ActionListener, ListSelectionListener
|
||||
public class ChannelController extends JPanel implements ActionListener, ListSelectionListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -168,9 +182,13 @@ public class ChannelController extends JPanel
|
||||
if( index >= 0 )
|
||||
{
|
||||
int translatedIndex = mChannelTable.convertRowIndexToView( index );
|
||||
mChannelTable.setRowSelectionInterval( translatedIndex, translatedIndex );
|
||||
mChannelTable.scrollRectToVisible(
|
||||
new Rectangle( mChannelTable.getCellRect( translatedIndex, 0, true ) ) );
|
||||
|
||||
if(translatedIndex >= 0)
|
||||
{
|
||||
mChannelTable.setRowSelectionInterval( translatedIndex, translatedIndex );
|
||||
mChannelTable.scrollRectToVisible(
|
||||
new Rectangle( mChannelTable.getCellRect( translatedIndex, 0, true ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import io.github.dsheirer.module.decode.event.MessageActivityModel;
|
||||
import io.github.dsheirer.module.log.EventLogManager;
|
||||
import io.github.dsheirer.record.RecorderManager;
|
||||
import io.github.dsheirer.record.RecorderType;
|
||||
import io.github.dsheirer.record.binary.BinaryRecorder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.source.Source;
|
||||
@@ -211,7 +212,7 @@ public class ChannelProcessingManager implements ChannelEventListener
|
||||
try
|
||||
{
|
||||
source = mSourceManager.getSource(channel.getSourceConfiguration(),
|
||||
channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth());
|
||||
channel.getDecodeConfiguration().getChannelSpecification());
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
@@ -308,6 +309,25 @@ public class ChannelProcessingManager implements ChannelEventListener
|
||||
{
|
||||
processingChain.addModule(mRecorderManager.getBasebandRecorder(channel.toString()));
|
||||
}
|
||||
|
||||
/* Add decoded bit stream recorder if the decoder supports bitstream output */
|
||||
if(DecoderFactory.getBitstreamDecoders().contains(channel.getDecodeConfiguration().getDecoderType()))
|
||||
{
|
||||
if((recorders.contains(RecorderType.DEMODULATED_BIT_STREAM) &&
|
||||
channel.getChannelType() == Channel.ChannelType.STANDARD))
|
||||
{
|
||||
processingChain.addModule(new BinaryRecorder(mRecorderManager.getRecordingBasePath(),
|
||||
channel.toString()));
|
||||
}
|
||||
|
||||
/* Add traffic channel decoded bit stream recorder */
|
||||
if(recorders.contains(RecorderType.TRAFFIC_DEMODULATED_BIT_STREAM) &&
|
||||
channel.getChannelType() == Channel.ChannelType.TRAFFIC)
|
||||
{
|
||||
processingChain.addModule(new BinaryRecorder(mRecorderManager.getRecordingBasePath(),
|
||||
channel.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processingChain.setSource(source);
|
||||
|
||||
@@ -21,7 +21,7 @@ import io.github.dsheirer.dsp.filter.resample.RealResampler;
|
||||
import io.github.dsheirer.dsp.mixer.IOscillator;
|
||||
import io.github.dsheirer.dsp.mixer.Oscillator;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
* Provides normal or inverted decoded output.
|
||||
*/
|
||||
public class AFSK1200Decoder implements Listener<ReusableBuffer>
|
||||
public class AFSK1200Decoder implements Listener<ReusableFloatBuffer>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AFSK1200Decoder.class);
|
||||
|
||||
@@ -110,7 +110,7 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>
|
||||
* @param buffer containing 8.0 kHz unfiltered FM demodulated audio samples with sub-audible LTR signalling.
|
||||
*/
|
||||
@Override
|
||||
public void receive(ReusableBuffer buffer)
|
||||
public void receive(ReusableFloatBuffer buffer)
|
||||
{
|
||||
mResampler.resample(buffer);
|
||||
}
|
||||
@@ -142,10 +142,10 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>
|
||||
}
|
||||
|
||||
|
||||
public class Decoder implements Listener<ReusableBuffer>
|
||||
public class Decoder implements Listener<ReusableFloatBuffer>
|
||||
{
|
||||
@Override
|
||||
public void receive(ReusableBuffer buffer)
|
||||
public void receive(ReusableFloatBuffer buffer)
|
||||
{
|
||||
//Calculate correlation values against each 1200/1800 reference signal
|
||||
mCorrelationValuesMark = mCorrelatorMark.process(buffer);
|
||||
@@ -212,17 +212,17 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>
|
||||
* Processes each sample in the incoming sample buffer against a generated reference sample set for one symbol
|
||||
* period to derive a correlation value that is in-turn averaged over one symbol period.
|
||||
*
|
||||
* @param reusableBuffer containing FM demodulated samples
|
||||
* @param reusableFloatBuffer containing FM demodulated samples
|
||||
* @return a reusable array of correlation values for each sample
|
||||
*/
|
||||
public float[] process(ReusableBuffer reusableBuffer)
|
||||
public float[] process(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
if(mCorrelationValues == null || mCorrelationValues.length != reusableBuffer.getSampleCount())
|
||||
if(mCorrelationValues == null || mCorrelationValues.length != reusableFloatBuffer.getSampleCount())
|
||||
{
|
||||
mCorrelationValues = new float[reusableBuffer.getSampleCount()];
|
||||
mCorrelationValues = new float[reusableFloatBuffer.getSampleCount()];
|
||||
}
|
||||
|
||||
float[] samples = reusableBuffer.getSamples();
|
||||
float[] samples = reusableFloatBuffer.getSamples();
|
||||
|
||||
int y;
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.am;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
/**
|
||||
* Performs AM demodulation on baseband I/Q samples to produce demodulated float output.
|
||||
@@ -63,11 +63,11 @@ public class AMDemodulator
|
||||
* @param complexBuffer to demodulate
|
||||
* @return demodulated audio buffer.
|
||||
*/
|
||||
public ReusableBuffer demodulate(ReusableComplexBuffer complexBuffer)
|
||||
public ReusableFloatBuffer demodulate(ReusableComplexBuffer complexBuffer)
|
||||
{
|
||||
ReusableBuffer reusableBuffer = mReusableBufferQueue.getBuffer(complexBuffer.getSampleCount());
|
||||
ReusableFloatBuffer reusableFloatBuffer = mReusableBufferQueue.getBuffer(complexBuffer.getSampleCount());
|
||||
float[] input = complexBuffer.getSamples();
|
||||
float[] output = reusableBuffer.getSamples();
|
||||
float[] output = reusableFloatBuffer.getSamples();
|
||||
mOutputBufferPointer = 0;
|
||||
|
||||
for(int x= 0; x < complexBuffer.getSamples().length; x += 2)
|
||||
@@ -77,6 +77,6 @@ public class AMDemodulator
|
||||
|
||||
complexBuffer.decrementUserCount();
|
||||
|
||||
return reusableBuffer;
|
||||
return reusableFloatBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.filter;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.cic.ComplexPrimeCICDecimate;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
@@ -358,15 +357,6 @@ public class FilterFactory
|
||||
return (int)(Math.round((double)attenuation / (22.0d * frequency)));
|
||||
}
|
||||
|
||||
public static ComplexPrimeCICDecimate getDecimationFilter(int sampleRate, int decimatedRate, int order,
|
||||
int passFrequency, int attenuation, Window.WindowType windowType)
|
||||
{
|
||||
int decimationRate = (int)(sampleRate / decimatedRate);
|
||||
|
||||
return new ComplexPrimeCICDecimate(decimationRate, order,
|
||||
passFrequency, attenuation, windowType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines decimation rate(s) for a polyphase decimation filter.
|
||||
*
|
||||
@@ -760,26 +750,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.10) / (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 +910,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1719,7 +1719,32 @@ public enum Filters
|
||||
0.011873357051047719f, 0.0f, -0.007901319195893647f, 0.0f,
|
||||
0.005055504379767936f, 0.0f, -0.003054430179754289f, 0.0f,
|
||||
0.001695637278417295f, 0.0f, -0.000998606272947510f
|
||||
} );
|
||||
}),
|
||||
|
||||
/**
|
||||
* FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
* .sampleRate(100)
|
||||
* .gridDensity(16)
|
||||
* .oddLength(true)
|
||||
* .passBandCutoff(21)
|
||||
* .passBandAmplitude(1.0)
|
||||
* .passBandRipple(0.01)
|
||||
* .stopBandStart(29)
|
||||
* .stopBandAmplitude(0.0)
|
||||
* .stopBandRipple(0.01) //Approximately -85 dB suppression
|
||||
* .build();
|
||||
*/
|
||||
HALF_BAND_FILTER_27T(new float[]
|
||||
{
|
||||
0.007495115f, 0.0f, -0.011045938f, 0.0f,
|
||||
0.019129561f, 0.0f, -0.031747308f, 0.0f,
|
||||
0.053220540f, 0.0f, -0.099569574f, 0.0f,
|
||||
0.316083550f, 0.5f, 0.316083550f, 0.0f,
|
||||
-0.099569574f, 0.0f, 0.053220540f, 0.0f,
|
||||
-0.031747308f, 0.0f, 0.019129561f, 0.0f,
|
||||
-0.011045938f, 0.0f, 0.007495115f
|
||||
|
||||
});
|
||||
|
||||
float[] mCoefficients;
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ComplexPolyphaseChannelizerM2.class);
|
||||
|
||||
private static final int DEFAULT_MINIMUM_CHANNEL_BANDWIDTH = 12500;
|
||||
private static final int DEFAULT_MINIMUM_CHANNEL_BANDWIDTH = 25000;
|
||||
|
||||
//Sized at 152 buffers a second where max = 5 seconds and reset = 2 seconds worth of buffers
|
||||
private IFFTProcessor mIFFTProcessor = new IFFTProcessor((5 * 152), (2 * 152));
|
||||
|
||||
@@ -136,6 +136,46 @@ public class ContinuousBufferProcessor<E> implements Listener<E>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops this buffer processor,stops queuing of incoming buffers and flushes the internal buffer queue
|
||||
* contents to the listener.
|
||||
*/
|
||||
public void flushAndStop()
|
||||
{
|
||||
if(mRunning.compareAndSet(true, false))
|
||||
{
|
||||
if(mScheduledFuture != null)
|
||||
{
|
||||
mScheduledFuture.cancel(true);
|
||||
mScheduledFuture = null;
|
||||
}
|
||||
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Distributes queued buffers to the listener
|
||||
*/
|
||||
private void process()
|
||||
{
|
||||
List<E> buffers = new ArrayList<>();
|
||||
|
||||
try
|
||||
{
|
||||
mQueue.drainTo(buffers);
|
||||
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive(buffers);
|
||||
}
|
||||
}
|
||||
catch(Throwable throwable)
|
||||
{
|
||||
mLog.error("Error while dispatching buffers to listener", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any buffers from the queue
|
||||
*/
|
||||
@@ -162,21 +202,7 @@ public class ContinuousBufferProcessor<E> implements Listener<E>
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
List<E> buffers = new ArrayList<>();
|
||||
|
||||
try
|
||||
{
|
||||
mQueue.drainTo(buffers);
|
||||
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive(buffers);
|
||||
}
|
||||
}
|
||||
catch(Throwable throwable)
|
||||
{
|
||||
mLog.error("Error while dispatching buffers to listeners", throwable);
|
||||
}
|
||||
process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.filter.channelizer;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.AbstractReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
|
||||
public class ContinuousReusableBufferProcessor<T extends ReusableBuffer> extends ContinuousBufferProcessor<T>
|
||||
public class ContinuousReusableBufferProcessor<T extends AbstractReusableBuffer> extends ContinuousBufferProcessor<T>
|
||||
{
|
||||
/**
|
||||
* Scheduled Reusable Buffer Processor combines an internal overflowable buffer with a scheduled runnable processing
|
||||
|
||||
@@ -29,12 +29,12 @@ import io.github.dsheirer.source.Source;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -63,7 +63,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelManager.class);
|
||||
private static final double MINIMUM_CHANNEL_BANDWIDTH = 12500.0;
|
||||
private static final double MINIMUM_CHANNEL_BANDWIDTH = 25000.0;
|
||||
private static final double CHANNEL_OVERSAMPLING = 2.0;
|
||||
private static final int POLYPHASE_CHANNELIZER_TAPS_PER_CHANNEL = 9;
|
||||
private static final int POLYPHASE_SYNTHESIZER_TAPS_PER_CHANNEL = 9;
|
||||
@@ -142,7 +142,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
* @param tunerChannel specifying center frequency and bandwidth.
|
||||
* @return source or null.
|
||||
*/
|
||||
public TunerChannelSource getChannel(TunerChannel tunerChannel)
|
||||
public TunerChannelSource getChannel(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
|
||||
{
|
||||
PolyphaseChannelSource channelSource = null;
|
||||
|
||||
@@ -156,10 +156,17 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
{
|
||||
long centerFrequency = mChannelCalculator.getCenterFrequencyForIndexes(polyphaseIndexes);
|
||||
|
||||
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener,
|
||||
mChannelCalculator.getChannelSampleRate(), centerFrequency);
|
||||
try
|
||||
{
|
||||
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener,
|
||||
mChannelCalculator.getChannelSampleRate(), centerFrequency, channelSpecification);
|
||||
|
||||
mChannelSources.add(channelSource);
|
||||
mChannelSources.add(channelSource);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.debug("Couldn't design final output low pass filter for polyphase channel source");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
@@ -172,36 +179,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
return channelSource;
|
||||
}
|
||||
|
||||
public TunerChannelSource getTestChannel(int index)
|
||||
{
|
||||
PolyphaseChannelSource channelSource = null;
|
||||
|
||||
try
|
||||
{
|
||||
List<Integer> polyphaseIndexes = new ArrayList<>();
|
||||
polyphaseIndexes.add(index);
|
||||
|
||||
IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes);
|
||||
|
||||
|
||||
if(outputProcessor != null)
|
||||
{
|
||||
long centerFrequency = mChannelCalculator.getCenterFrequencyForIndexes(polyphaseIndexes);
|
||||
|
||||
TunerChannel tunerChannel = new TunerChannel(centerFrequency, 12500);
|
||||
|
||||
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener,
|
||||
mChannelCalculator.getChannelSampleRate(), centerFrequency);
|
||||
}
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
{
|
||||
mLog.error("Couldn't get test channel with index:" + index, iae);
|
||||
}
|
||||
|
||||
return channelSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a processor to process the channelizer channel indexes into a composite output stream providing
|
||||
* channelized complex sample buffers to a registered source listener.
|
||||
@@ -213,12 +190,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 +483,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);
|
||||
}
|
||||
|
||||
@@ -16,22 +16,35 @@
|
||||
package io.github.dsheirer.dsp.filter.channelizer;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2;
|
||||
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PolyphaseChannelSource extends TunerChannelSource
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class);
|
||||
|
||||
private ReusableComplexBufferAssembler mReusableComplexBufferAssembler;
|
||||
private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor;
|
||||
private IPolyphaseChannelOutputProcessor mReplacementPolyphaseChannelOutputProcessor;
|
||||
private long mReplacementFrequency;
|
||||
private ComplexFIRFilter2 mLowPassFilter;
|
||||
private static Map<Integer,float[]> sLowPassFilters = new HashMap();
|
||||
|
||||
private double mChannelSampleRate;
|
||||
private long mIndexCenterFrequency;
|
||||
private long mChannelFrequencyCorrection;
|
||||
@@ -46,15 +59,22 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
* @param producerSourceEventListener to receive source event requests (e.g. start/stop sample stream)
|
||||
* @param channelSampleRate for the downstream sample output
|
||||
* @param centerFrequency of the incoming polyphase channel(s)
|
||||
* @throws FilterDesignException if a channel low pass filter can't be designed to the channel specification
|
||||
*/
|
||||
public PolyphaseChannelSource(TunerChannel tunerChannel, IPolyphaseChannelOutputProcessor outputProcessor,
|
||||
Listener<SourceEvent> producerSourceEventListener, double channelSampleRate,
|
||||
long centerFrequency)
|
||||
long centerFrequency, ChannelSpecification channelSpecification)
|
||||
throws FilterDesignException
|
||||
{
|
||||
super(producerSourceEventListener, tunerChannel);
|
||||
mPolyphaseChannelOutputProcessor = outputProcessor;
|
||||
mChannelSampleRate = channelSampleRate;
|
||||
mReusableComplexBufferAssembler = new ReusableComplexBufferAssembler(2500, mChannelSampleRate);
|
||||
|
||||
float[] filterCoefficients = getLowPassFilter(channelSampleRate, channelSpecification.getPassFrequency(),
|
||||
channelSpecification.getStopFrequency());
|
||||
mLowPassFilter = new ComplexFIRFilter2(filterCoefficients, 1.0f);
|
||||
|
||||
setFrequency(centerFrequency);
|
||||
}
|
||||
|
||||
@@ -93,14 +113,33 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
*/
|
||||
public void setPolyphaseChannelOutputProcessor(IPolyphaseChannelOutputProcessor outputProcessor, long frequency)
|
||||
{
|
||||
//Lock on the channel output processor to block the channel results processor thread from servicing the
|
||||
//channel output processor's buffer while we change out the processors.
|
||||
synchronized(mPolyphaseChannelOutputProcessor)
|
||||
//If this is the first time, simply assign the output processor
|
||||
if(mPolyphaseChannelOutputProcessor == null)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor = outputProcessor;
|
||||
}
|
||||
//Otherwise, we have to swap out the processor on the sample processing thread
|
||||
else
|
||||
{
|
||||
mReplacementPolyphaseChannelOutputProcessor = outputProcessor;
|
||||
mReplacementFrequency = frequency;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the output processor to use the new output processor provided by the external
|
||||
* polyphase channel manager. This method should only be executed on the sample processing
|
||||
* thread, within the processChannelResults() method.
|
||||
*/
|
||||
private void swapOutputProcessor()
|
||||
{
|
||||
if(mReplacementPolyphaseChannelOutputProcessor != null)
|
||||
{
|
||||
IPolyphaseChannelOutputProcessor existingProcessor = mPolyphaseChannelOutputProcessor;
|
||||
|
||||
//Swap out the processor so that incoming samples can accumulate in the new channel output processor
|
||||
mPolyphaseChannelOutputProcessor = outputProcessor;
|
||||
mPolyphaseChannelOutputProcessor = mReplacementPolyphaseChannelOutputProcessor;
|
||||
mReplacementPolyphaseChannelOutputProcessor = null;
|
||||
|
||||
//Remove the buffer overflow listener and clear any previous buffer overflow state
|
||||
existingProcessor.setSourceOverflowListener(null);
|
||||
@@ -109,18 +148,9 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
//Register to receive buffer overflow notifications so we can push them up through the source chain
|
||||
mPolyphaseChannelOutputProcessor.setSourceOverflowListener(this);
|
||||
|
||||
//Fully process the residual channel results buffer of the previous channel output processor
|
||||
if(existingProcessor != null)
|
||||
{
|
||||
existingProcessor.processChannelResults(mReusableComplexBufferAssembler);
|
||||
}
|
||||
|
||||
//Finally, setup the frequency offset for the output processor and reset the frequency correction value
|
||||
//to allow consumers to adjust and calculate a new frequency correction value, if needed.
|
||||
mIndexCenterFrequency = frequency;
|
||||
mChannelFrequencyCorrection = 0;
|
||||
//Finally, setup the frequency offset for the output processor.
|
||||
mIndexCenterFrequency = mReplacementFrequency;
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
|
||||
broadcastConsumerSourceEvent(SourceEvent.frequencyCorrectionChange(mChannelFrequencyCorrection));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +163,11 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
*/
|
||||
public void receiveChannelResults(ReusableChannelResultsBuffer channelResultsBuffer)
|
||||
{
|
||||
if(mReplacementPolyphaseChannelOutputProcessor != null)
|
||||
{
|
||||
swapOutputProcessor();
|
||||
}
|
||||
|
||||
mReusableComplexBufferAssembler.updateTimestamp(channelResultsBuffer.getTimestamp());
|
||||
mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsBuffer);
|
||||
}
|
||||
@@ -169,7 +204,6 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
if(mPolyphaseChannelOutputProcessor != null)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.dispose();
|
||||
mPolyphaseChannelOutputProcessor = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +231,7 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
* @param frequency in hertz
|
||||
*/
|
||||
@Override
|
||||
protected void setFrequency(long frequency)
|
||||
public void setFrequency(long frequency)
|
||||
{
|
||||
mIndexCenterFrequency = frequency;
|
||||
|
||||
@@ -209,11 +243,7 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
@Override
|
||||
protected void processSamples()
|
||||
{
|
||||
//Lock on the output processor so that it can't be changed in the middle of processing
|
||||
synchronized(mPolyphaseChannelOutputProcessor)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.processChannelResults(mReusableComplexBufferAssembler);
|
||||
}
|
||||
mPolyphaseChannelOutputProcessor.processChannelResults(mReusableComplexBufferAssembler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,10 +262,7 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
*/
|
||||
private void updateFrequencyOffset()
|
||||
{
|
||||
synchronized(mPolyphaseChannelOutputProcessor)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
|
||||
}
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -244,4 +271,42 @@ public class PolyphaseChannelSource extends TunerChannelSource
|
||||
return "POLYPHASE [" + mPolyphaseChannelOutputProcessor.getInputChannelCount() + "] " +
|
||||
getTunerChannel().getFrequency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a low-pass filter to use as the final cleanup filter for the decimated output stream
|
||||
*
|
||||
* @param sampleRate for the final output channel rate
|
||||
* @param passFrequency for half of the desired channel rate
|
||||
* @param stopFrequency for the attenuated band
|
||||
* @return a newly designed filter or a previously designed (cached) filter
|
||||
* @throws FilterDesignException
|
||||
*/
|
||||
private float[] getLowPassFilter(double sampleRate, double passFrequency, double stopFrequency) throws FilterDesignException
|
||||
{
|
||||
//Use existing filter if we've already designed one
|
||||
if(sLowPassFilters.containsKey((int)sampleRate))
|
||||
{
|
||||
return sLowPassFilters.get((int)sampleRate);
|
||||
}
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(sampleRate)
|
||||
.gridDensity(16)
|
||||
.passBandCutoff(passFrequency)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart(stopFrequency)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.01)
|
||||
.build();
|
||||
|
||||
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
|
||||
|
||||
//This will throw an exception if the filter cannot be designed
|
||||
float[] taps = designer.getImpulseResponse();
|
||||
|
||||
sLowPassFilters.put((int)sampleRate, taps);
|
||||
|
||||
return 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)
|
||||
|
||||
@@ -16,29 +16,18 @@
|
||||
package io.github.dsheirer.dsp.filter.channelizer.output;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.channelizer.TwoChannelSynthesizerM2;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2;
|
||||
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
import io.github.dsheirer.dsp.mixer.FS4DownConverter;
|
||||
import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(TwoChannelOutputProcessor.class);
|
||||
|
||||
private static Map<Integer,float[]> sLowPassFilterKernelMap = new HashMap<>();
|
||||
// private final static Logger mLog = LoggerFactory.getLogger(TwoChannelOutputProcessor.class);
|
||||
private TwoChannelSynthesizerM2 mSynthesizer;
|
||||
private FS4DownConverter mFS4DownConverter = new FS4DownConverter();
|
||||
private ComplexFIRFilter2 mLowPassFilter;
|
||||
|
||||
private int mChannelOffset1;
|
||||
private int mChannelOffset2;
|
||||
@@ -49,36 +38,20 @@ 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);
|
||||
|
||||
float[] lowPassFilter = sLowPassFilterKernelMap.get((int)sampleRate);
|
||||
|
||||
if(lowPassFilter == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
lowPassFilter = getLowPassFilter(sampleRate);
|
||||
sLowPassFilterKernelMap.put((int)sampleRate, lowPassFilter);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.error("Couldn't design a low-pass filter for 2-channel synthesizer", fde);
|
||||
throw new IllegalStateException("Cannot design low-pass filter for 2 channel synthesizer", fde);
|
||||
}
|
||||
}
|
||||
|
||||
mLowPassFilter = new ComplexFIRFilter2(lowPassFilter, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the frequency offset to apply to the incoming samples to mix the desired signal to baseband.
|
||||
*
|
||||
* @param frequencyOffset in hertz
|
||||
*/
|
||||
@Override
|
||||
@@ -125,7 +98,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
public void process(List<ReusableChannelResultsBuffer> channelResultsBuffers,
|
||||
ReusableComplexBufferAssembler reusableComplexBufferAssembler)
|
||||
{
|
||||
for(ReusableChannelResultsBuffer buffer: channelResultsBuffers)
|
||||
for(ReusableChannelResultsBuffer buffer : channelResultsBuffers)
|
||||
{
|
||||
ReusableComplexBuffer channel1 = buffer.getChannel(mChannelOffset1);
|
||||
ReusableComplexBuffer channel2 = buffer.getChannel(mChannelOffset2);
|
||||
@@ -139,46 +112,11 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
//Apply offset and frequency correction to center the signal of interest within the synthesized channel
|
||||
getFrequencyCorrectionMixer().mixComplex(synthesized.getSamples());
|
||||
|
||||
ReusableComplexBuffer lowPassFiltered = mLowPassFilter.filter(synthesized);
|
||||
synthesized.applyGain(getGain());
|
||||
|
||||
reusableComplexBufferAssembler.receive(lowPassFiltered);
|
||||
reusableComplexBufferAssembler.receive(synthesized);
|
||||
|
||||
buffer.decrementUserCount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a low-pass filter kernel for the specified sample rate.
|
||||
* @param sampleRate for the channel
|
||||
* @return filter
|
||||
* @throws FilterDesignException if the filter cannot be designed
|
||||
*/
|
||||
private static float[] getLowPassFilter(double sampleRate) throws FilterDesignException
|
||||
{
|
||||
double cutoff = sampleRate / 4.0;
|
||||
int passBandCutoff = (int)(cutoff * 0.92);
|
||||
int stopBandStart = (int)(cutoff * 1.08);
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(sampleRate)
|
||||
.gridDensity(16)
|
||||
.oddLength(true)
|
||||
.passBandCutoff((int)passBandCutoff)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart((int)stopBandStart)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.005) //Approximately 94 dB attenuation
|
||||
.build();
|
||||
|
||||
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
|
||||
|
||||
if(designer.isValid())
|
||||
{
|
||||
return designer.getImpulseResponse();
|
||||
}
|
||||
|
||||
throw new FilterDesignException("Couldn't design low-pass filter for sample rate: " + sampleRate +
|
||||
" Pass End:" + passBandCutoff + " Stop Begin:" + stopBandStart);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.filter.cic;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.Filters;
|
||||
import io.github.dsheirer.dsp.filter.Window;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2;
|
||||
import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandFilter;
|
||||
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler;
|
||||
@@ -28,52 +27,93 @@ import io.github.dsheirer.sample.decimator.ComplexDecimator;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Non-Recursive Prime-Factor CIC Filter.
|
||||
*
|
||||
* Implements the CIC filter described in Understanding Digital Signal Processing, 3e, Lyons, on page 769. This
|
||||
* filter is comprised of multiple decimating stages each with a prime factor decimation rate. Multiple stages are
|
||||
* cascaded to achieve the overall decimation rate.
|
||||
*
|
||||
* This filter currently supports a maximum decimation rate of 2801. Higher decimation rates can be added by
|
||||
* adding additional prime factors to the PRIMES array.
|
||||
*/
|
||||
public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
{
|
||||
// private final static Logger mLog = LoggerFactory.getLogger(ComplexPrimeCICDecimate.class);
|
||||
|
||||
/**
|
||||
* PRIME numbers to use for decimation stage sizing. This list contains values that should
|
||||
* support input sample rates up to 70 MHz with an output channel rate of at least 25 kHz.
|
||||
*/
|
||||
public static int[] PRIMES = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
|
||||
59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
|
||||
157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
|
||||
257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359,
|
||||
367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
|
||||
467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593,
|
||||
599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691};
|
||||
599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
|
||||
709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827,
|
||||
829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953,
|
||||
967, 971, 977, 983, 991, 997, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051,
|
||||
1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163,
|
||||
1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279,
|
||||
1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399,
|
||||
1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489,
|
||||
1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601,
|
||||
1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709,
|
||||
1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831,
|
||||
1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951,
|
||||
1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069,
|
||||
2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179,
|
||||
2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297,
|
||||
2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399,
|
||||
2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543,
|
||||
2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671,
|
||||
2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753,
|
||||
2767, 2777, 2789, 2791, 2797, 2801
|
||||
};
|
||||
|
||||
private ArrayList<DecimatingStage> mDecimatingStages =
|
||||
new ArrayList<DecimatingStage>();
|
||||
private static Map<Integer,float[]> sLowPassFilters = new HashMap();
|
||||
private static Map<Integer,List<Integer>> sPrimeFactors = new HashMap();
|
||||
|
||||
private List<DecimatingStage> mDecimatingStages = new ArrayList<DecimatingStage>();
|
||||
private DecimatingStage mFirstDecimatingStage;
|
||||
|
||||
private Output mOutput;
|
||||
|
||||
/**
|
||||
* Non-Recursive Prime-Factor CIC Filter with float sample array inputs and
|
||||
* decimated, single paired i/q float sample output.
|
||||
* Constructs a new decimation filter.
|
||||
*
|
||||
* Implements the CIC filter described in Understanding Digital Signal
|
||||
* Processing, 3e, Lyons, on page 769. This filter is comprised of multiple
|
||||
* decimating stages each with a prime factor decimation rate. Multiple
|
||||
* stages are cascaded to achieve the overall decimation rate.
|
||||
* An example setup for a 2.4 MHz input sample rate and 25 kHz output channel rate with a desired 12.5 kHz channel
|
||||
* pass band might be:
|
||||
* sampleRate = 2,400,000.0
|
||||
* decimation = 96
|
||||
* passFrequency = 6250 (1/4 of the channel rate)
|
||||
* stopFrequency = 7000
|
||||
*
|
||||
* This filter supports a maximum decimation rate of 700. This filter can
|
||||
* be adapted to higher decimation rates by adding additional prime factors
|
||||
* to the PRIMES array.
|
||||
* Note: the transition bandwidth (stop - start) directly impacts the size of the low-pass cleanup filter,
|
||||
* where smaller transition bandwidth creates a longer filter that requires more processing effort.
|
||||
*
|
||||
* @param sampleRate of the input sample stream
|
||||
* @param decimation - overall decimation rate
|
||||
* @param order - filter order
|
||||
* @param passFrequency for the final cleanup filter
|
||||
* @param stopFrequency for the final cleanup filter
|
||||
* @throws FilterDesignException if a final low-pass cleanup filter cannot be created for the output channel rate
|
||||
* and specified pass/stop frequencies.
|
||||
*/
|
||||
public ComplexPrimeCICDecimate(int decimation, int order,
|
||||
int passFrequency, int attenuation, Window.WindowType windowType)
|
||||
public ComplexPrimeCICDecimate(double sampleRate, int decimation, double passFrequency, double stopFrequency)
|
||||
throws FilterDesignException
|
||||
{
|
||||
Validate.isTrue(decimation <= 700);
|
||||
|
||||
Validate.isTrue(decimation <= PRIMES[PRIMES.length - 1]);
|
||||
|
||||
List<Integer> stageSizes = getPrimeFactors(decimation);
|
||||
|
||||
for(int x = 0; x < stageSizes.size(); x++)
|
||||
{
|
||||
DecimatingStage stage = new DecimatingStage(stageSizes.get(x), order);
|
||||
DecimatingStage stage = new DecimatingStage(stageSizes.get(x), 1);
|
||||
|
||||
mDecimatingStages.add(stage);
|
||||
|
||||
@@ -89,10 +129,11 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
}
|
||||
}
|
||||
|
||||
mOutput = new Output(48000, passFrequency, attenuation, windowType);
|
||||
double channelRate = sampleRate / (double)decimation;
|
||||
|
||||
mDecimatingStages.get(mDecimatingStages.size() - 1)
|
||||
.setListener(mOutput);
|
||||
mOutput = new Output(channelRate, passFrequency, stopFrequency);
|
||||
|
||||
mDecimatingStages.get(mDecimatingStages.size() - 1).setListener(mOutput);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
@@ -127,16 +168,26 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the prime factors of the decimation rate up to a maximum
|
||||
* decimation rate of 700. If you wish to have decimation rates higher
|
||||
* than 700, then add additional prime factors to the PRIMES array.
|
||||
* Calculates the prime factors of the decimation rate. If you wish to have decimation rates higher than the
|
||||
* highest rate listed in the PRIMES array, then add additional prime factors to the PRIMES array.
|
||||
*
|
||||
* @param decimation - integral decimation rate
|
||||
* @return - ordered list (smallest to largest) of prime factors
|
||||
*/
|
||||
public static List<Integer> getPrimeFactors(int decimation)
|
||||
{
|
||||
ArrayList<Integer> stages = new ArrayList<Integer>();
|
||||
if(decimation > PRIMES[PRIMES.length - 1])
|
||||
{
|
||||
throw new IllegalArgumentException("Decimation rates higher than [" + PRIMES[PRIMES.length - 1] +
|
||||
"] are not supported. Update the PRIMES array to add support for higher decimation rates");
|
||||
}
|
||||
|
||||
if(sPrimeFactors.containsKey(decimation))
|
||||
{
|
||||
return sPrimeFactors.get(decimation);
|
||||
}
|
||||
|
||||
List<Integer> primeFactors = new ArrayList<Integer>();
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
@@ -146,8 +197,7 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
|
||||
if(decimation % prime == 0)
|
||||
{
|
||||
stages.add(prime);
|
||||
|
||||
primeFactors.add(prime);
|
||||
decimation /= prime;
|
||||
}
|
||||
else
|
||||
@@ -156,7 +206,9 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
}
|
||||
}
|
||||
|
||||
return stages;
|
||||
sPrimeFactors.put(decimation, primeFactors);
|
||||
|
||||
return primeFactors;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,7 +237,7 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
*/
|
||||
public class DecimatingStage implements ComplexSampleListener
|
||||
{
|
||||
private ArrayList<Stage> mStages = new ArrayList<Stage>();
|
||||
private List<Stage> mStages = new ArrayList<Stage>();
|
||||
private Stage mFirstStage;
|
||||
private ComplexDecimator mDecimator;
|
||||
|
||||
@@ -217,7 +269,6 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
}
|
||||
|
||||
mDecimator = new ComplexDecimator(size);
|
||||
|
||||
mStages.get(mStages.size() - 1).setListener(mDecimator);
|
||||
}
|
||||
|
||||
@@ -248,9 +299,8 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
}
|
||||
|
||||
/**
|
||||
* Single non-decimating CIC stage component. Uses a circular buffer and a
|
||||
* running average internally to implement the stage so that stage size has
|
||||
* essentially no impact on the computational requirements of the stage
|
||||
* Single non-decimating CIC stage component. Uses a circular buffer and a running average internally to implement
|
||||
* the stage so that stage size has essentially no impact on the computational requirements of the stage
|
||||
*/
|
||||
public class Stage implements ComplexSampleListener
|
||||
{
|
||||
@@ -267,7 +317,7 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
|
||||
protected float mGain;
|
||||
|
||||
public Stage()
|
||||
protected Stage()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -278,7 +328,8 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
mISamples = new float[mSize];
|
||||
mQSamples = new float[mSize];
|
||||
|
||||
mGain = 1.0f / (float)size;
|
||||
float baseGain = 1.0f / (float)size;
|
||||
mGain = baseGain * (1.0f / (1.0f - baseGain));
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
@@ -288,7 +339,7 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
|
||||
public void receive(float i, float q)
|
||||
{
|
||||
/* Subtract the oldest sample and add back in the newest */
|
||||
/* Subtract the oldest sample and add in the newest sample */
|
||||
mISum = mISum - mISamples[mSamplePointer] + i;
|
||||
mQSum = mQSum - mQSamples[mSamplePointer] + q;
|
||||
|
||||
@@ -316,30 +367,24 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
}
|
||||
|
||||
/**
|
||||
* Size 2 stage that removes the unnecessary circular buffer management for
|
||||
* a two-stage.
|
||||
* Size 2 stage that eliminates the unnecessary circular buffer management for a two-stage.
|
||||
*/
|
||||
public class TwoStage extends Stage
|
||||
{
|
||||
public TwoStage()
|
||||
{
|
||||
super();
|
||||
|
||||
mGain = 0.5f;
|
||||
}
|
||||
|
||||
public void receive(float i, float q)
|
||||
{
|
||||
float iSum = mISum + i;
|
||||
float qSum = mQSum + q;
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive(((mISum + i) * mGain), ((mQSum + q) * mGain));
|
||||
}
|
||||
|
||||
mISum = i;
|
||||
mQSum = q;
|
||||
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive((iSum * mGain), (qSum * mGain));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,31 +395,29 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
*/
|
||||
public class Output implements ComplexSampleListener
|
||||
{
|
||||
/* Decimated output buffers will contain 1024 samples */
|
||||
private ReusableComplexBufferAssembler mAssembler = new ReusableComplexBufferAssembler(2048, 48000.0);
|
||||
|
||||
private ComplexFIRFilter2 mCleanupFilter;
|
||||
private ComplexHalfBandFilter mHalfBandFilter = new ComplexHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 0.4f, false);
|
||||
/* Decimated output buffers will contain 1024 complex samples */
|
||||
private ReusableComplexBufferAssembler mBufferAssembler;
|
||||
private ComplexFIRFilter2 mLowPassFilter;
|
||||
private Listener<ReusableComplexBuffer> mReusableComplexBufferListener;
|
||||
|
||||
public Output(int outputSampleRate, int passFrequency, int attenuation,
|
||||
Window.WindowType windowType)
|
||||
public Output(double outputSampleRate, double passFrequency, double stopFrequency) throws FilterDesignException
|
||||
{
|
||||
mCleanupFilter = new ComplexFIRFilter2(FilterFactory
|
||||
.getCICCleanupFilter(outputSampleRate, passFrequency, attenuation, windowType), 0.4f);
|
||||
mBufferAssembler = new ReusableComplexBufferAssembler(2400, outputSampleRate);
|
||||
|
||||
//Bypassing the CIC cleanup filter for now
|
||||
mAssembler.setListener(new Listener<ReusableComplexBuffer>()
|
||||
//This may throw an exception if we can't design a filter for the sample rate and pass/stop frequencies
|
||||
float[] filterCoefficients = getLowPassFilter(outputSampleRate, passFrequency, stopFrequency);
|
||||
mLowPassFilter = new ComplexFIRFilter2(filterCoefficients, 1.0f);
|
||||
|
||||
mBufferAssembler.setListener(new Listener<ReusableComplexBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
{
|
||||
if(mReusableComplexBufferListener != null)
|
||||
{
|
||||
ReusableComplexBuffer cleanupFiltered = mCleanupFilter.filter(reusableComplexBuffer);
|
||||
ReusableComplexBuffer halfBandFiltered = mHalfBandFilter.filter(cleanupFiltered);
|
||||
mReusableComplexBufferListener.receive(halfBandFiltered);
|
||||
ReusableComplexBuffer filteredBuffer = mLowPassFilter.filter(reusableComplexBuffer);
|
||||
mReusableComplexBufferListener.receive(filteredBuffer);
|
||||
// mReusableComplexBufferListener.receive(reusableComplexBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -386,20 +429,17 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mAssembler.dispose();
|
||||
mCleanupFilter.dispose();
|
||||
mHalfBandFilter.dispose();
|
||||
mBufferAssembler.dispose();
|
||||
mLowPassFilter.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Receiver method for the output adapter to receive a filtered,
|
||||
* decimated sample, apply gain correction, apply cleanup filtering
|
||||
* and output the sample values as a complex sample.
|
||||
* Interface for receiving CIC decimated output samples
|
||||
*/
|
||||
@Override
|
||||
public void receive(float inphase, float quadrature)
|
||||
{
|
||||
mAssembler.receive(new float[]{inphase, quadrature});
|
||||
mBufferAssembler.receive(inphase, quadrature);
|
||||
}
|
||||
|
||||
public void setListener(Listener<ReusableComplexBuffer> listener)
|
||||
@@ -411,5 +451,43 @@ public class ComplexPrimeCICDecimate implements Listener<ReusableComplexBuffer>
|
||||
{
|
||||
mReusableComplexBufferListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a low-pass filter to use as the final cleanup filter for the decimated output stream
|
||||
*
|
||||
* @param sampleRate for the final output channel rate
|
||||
* @param passFrequency for half of the desired channel rate
|
||||
* @param stopFrequency for the attenuated band
|
||||
* @return a newly designed filter or a previously designed (cached) filter
|
||||
* @throws FilterDesignException
|
||||
*/
|
||||
private float[] getLowPassFilter(double sampleRate, double passFrequency, double stopFrequency) throws FilterDesignException
|
||||
{
|
||||
//Use existing filter if we've already designed one
|
||||
if(sLowPassFilters.containsKey((int)sampleRate))
|
||||
{
|
||||
return sLowPassFilters.get((int)sampleRate);
|
||||
}
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(sampleRate)
|
||||
.gridDensity(16)
|
||||
.passBandCutoff(passFrequency)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart(stopFrequency)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.01)
|
||||
.build();
|
||||
|
||||
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
|
||||
|
||||
//This will throw an exception if the filter cannot be designed
|
||||
float[] taps = designer.getImpulseResponse();
|
||||
|
||||
sLowPassFilters.put((int)sampleRate, taps);
|
||||
|
||||
return taps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ import io.github.dsheirer.dsp.filter.Window.WindowType;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter2;
|
||||
import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferAssembler;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.decimator.RealDecimator;
|
||||
import io.github.dsheirer.sample.real.RealSampleListener;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
@@ -124,7 +124,7 @@ public class RealPrimeCICDecimate
|
||||
/**
|
||||
* Adds a listener to receive the output of this CIC decimation filter
|
||||
*/
|
||||
public void setListener(Listener<ReusableBuffer> listener)
|
||||
public void setListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mOutput.setListener(listener);
|
||||
}
|
||||
@@ -173,7 +173,7 @@ public class RealPrimeCICDecimate
|
||||
/**
|
||||
* Primary input method for receiving real buffers.
|
||||
*/
|
||||
public void receive(ReusableBuffer buffer)
|
||||
public void receive(ReusableFloatBuffer buffer)
|
||||
{
|
||||
if(mFirstDecimatingStage != null)
|
||||
{
|
||||
@@ -353,7 +353,7 @@ public class RealPrimeCICDecimate
|
||||
{
|
||||
/* Decimated output buffers will contain 1024 samples */
|
||||
private ReusableBufferAssembler mAssembler;
|
||||
private Listener<ReusableBuffer> mReusableBufferListener;
|
||||
private Listener<ReusableFloatBuffer> mReusableBufferListener;
|
||||
|
||||
private RealFIRFilter2 mCleanupFilter;
|
||||
private HalfBandFilter mHalfBandFilter = new HalfBandFilter(
|
||||
@@ -365,21 +365,21 @@ public class RealPrimeCICDecimate
|
||||
attenuation, windowType), 0.4f);
|
||||
|
||||
mAssembler = new ReusableBufferAssembler(outputBufferSize);
|
||||
mAssembler.setListener(new Listener<ReusableBuffer>()
|
||||
mAssembler.setListener(new Listener<ReusableFloatBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
|
||||
if(mReusableBufferListener != null)
|
||||
{
|
||||
ReusableBuffer cleanupFiltered = mCleanupFilter.filter(reusableBuffer);
|
||||
ReusableBuffer halfBandFiltered = mHalfBandFilter.filter(cleanupFiltered);
|
||||
ReusableFloatBuffer cleanupFiltered = mCleanupFilter.filter(reusableFloatBuffer);
|
||||
ReusableFloatBuffer halfBandFiltered = mHalfBandFilter.filter(cleanupFiltered);
|
||||
mReusableBufferListener.receive(halfBandFiltered);
|
||||
}
|
||||
else
|
||||
{
|
||||
reusableBuffer.decrementUserCount();
|
||||
reusableFloatBuffer.decrementUserCount();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -406,7 +406,7 @@ public class RealPrimeCICDecimate
|
||||
/**
|
||||
* Adds a listener to receive output samples
|
||||
*/
|
||||
public void setListener(Listener<ReusableBuffer> listener)
|
||||
public void setListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mReusableBufferListener = listener;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.filter.dc;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -77,11 +77,11 @@ public class BufferDCFilter
|
||||
}
|
||||
}
|
||||
|
||||
public ReusableBuffer filter(ReusableBuffer unfilteredBuffer)
|
||||
public ReusableFloatBuffer filter(ReusableFloatBuffer unfilteredBuffer)
|
||||
{
|
||||
float[] unfilteredSamples = unfilteredBuffer.getSamples();
|
||||
|
||||
ReusableBuffer filteredBuffer = mReusableBufferQueue.getBuffer(unfilteredSamples.length);
|
||||
ReusableFloatBuffer filteredBuffer = mReusableBufferQueue.getBuffer(unfilteredSamples.length);
|
||||
float[] filteredSamples = filteredBuffer.getSamples();
|
||||
|
||||
float offset = getOffset(unfilteredSamples);
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.filter.dc;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
/**
|
||||
* DC removal filter for removing any DC offset that may be present when a signal is not aligned within the
|
||||
@@ -77,9 +77,9 @@ public class DCRemovalFilter
|
||||
* @param unfilteredBuffer containing samples to filter
|
||||
* @return a new reusable buffer containing the filtered samples with the user count set to 1.
|
||||
*/
|
||||
public ReusableBuffer filter(ReusableBuffer unfilteredBuffer)
|
||||
public ReusableFloatBuffer filter(ReusableFloatBuffer unfilteredBuffer)
|
||||
{
|
||||
ReusableBuffer filteredBuffer = mReusableBufferQueue.getBuffer(unfilteredBuffer.getSampleCount());
|
||||
ReusableFloatBuffer filteredBuffer = mReusableBufferQueue.getBuffer(unfilteredBuffer.getSampleCount());
|
||||
|
||||
float[] unfilteredSamples = unfilteredBuffer.getSamples();
|
||||
float[] filteredSamples = filteredBuffer.getSamples();
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.filter.dc;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.real.RealSampleListener;
|
||||
|
||||
public class IIRSinglePoleDCRemovalFilter implements RealSampleListener
|
||||
@@ -67,9 +67,9 @@ public class IIRSinglePoleDCRemovalFilter implements RealSampleListener
|
||||
* @param inputBuffer to filter
|
||||
* @return filtered samples in a new (ie reused) output buffer
|
||||
*/
|
||||
public ReusableBuffer filter(ReusableBuffer inputBuffer)
|
||||
public ReusableFloatBuffer filter(ReusableFloatBuffer inputBuffer)
|
||||
{
|
||||
ReusableBuffer outputBuffer = mReusableBufferQueue.getBuffer(inputBuffer.getSampleCount());
|
||||
ReusableFloatBuffer outputBuffer = mReusableBufferQueue.getBuffer(inputBuffer.getSampleCount());
|
||||
float[] inputSamples = inputBuffer.getSamples();
|
||||
float[] outputSamples = outputBuffer.getSamples();
|
||||
|
||||
|
||||
@@ -61,14 +61,15 @@ public class FilterViewer extends Application
|
||||
// .passBandRipple(0.01)
|
||||
// .build();
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
|
||||
.sampleRate(8000)
|
||||
.stopFrequency1(1000)
|
||||
.passFrequencyBegin(1100)
|
||||
.passFrequencyEnd(1900)
|
||||
.stopFrequency2(2000)
|
||||
.stopRipple(0.000001)
|
||||
.passRipple(0.00001)
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(25000.0)
|
||||
.gridDensity(16)
|
||||
.passBandCutoff(11800)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart(12400)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.01)
|
||||
.build();
|
||||
|
||||
float[] taps = null;
|
||||
@@ -102,7 +103,6 @@ public class FilterViewer extends Application
|
||||
throw new IllegalStateException("Couldn't design filter");
|
||||
}
|
||||
|
||||
|
||||
return taps;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,8 +299,8 @@ public class FIRFilterSpecification
|
||||
private int mOrder;
|
||||
private Boolean mOddLength;
|
||||
private int mGridDensity = 16;
|
||||
private int mPassBandEndFrequency;
|
||||
private int mStopBandStartFrequency;
|
||||
private double mPassBandEndFrequency;
|
||||
private double mStopBandStartFrequency;
|
||||
private double mPassBandRipple;
|
||||
private double mStopBandRipple;
|
||||
private double mPassBandAmplitude = 1.0;
|
||||
@@ -342,13 +342,13 @@ public class FIRFilterSpecification
|
||||
return this;
|
||||
}
|
||||
|
||||
public LowPassBuilder passBandCutoff(int frequencyHz)
|
||||
public LowPassBuilder passBandCutoff(double frequencyHz)
|
||||
{
|
||||
mPassBandEndFrequency = frequencyHz;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LowPassBuilder stopBandStart(int frequencyHz)
|
||||
public LowPassBuilder stopBandStart(double frequencyHz)
|
||||
{
|
||||
mStopBandStartFrequency = frequencyHz;
|
||||
return this;
|
||||
@@ -905,7 +905,7 @@ public class FIRFilterSpecification
|
||||
* @param stopBandRipple stop band ripple in dB
|
||||
* @return estimated filter length
|
||||
*/
|
||||
public static int estimateFilterOrder(double sampleRate, int frequency1, int frequency2,
|
||||
public static int estimateFilterOrder(double sampleRate, double frequency1, double frequency2,
|
||||
double passBandRipple, double stopBandRipple)
|
||||
{
|
||||
double df = Math.abs(frequency2 - frequency1) / sampleRate;
|
||||
@@ -1013,13 +1013,13 @@ public class FIRFilterSpecification
|
||||
* @param amplitude of the frequency band relative to unity (0.0 - 1.0)
|
||||
* @param ripple of the frequency band specified as db ripple
|
||||
*/
|
||||
public FrequencyBand(double sampleRate, int start, int end, double amplitude, double ripple)
|
||||
public FrequencyBand(double sampleRate, double start, double end, double amplitude, double ripple)
|
||||
{
|
||||
this((double)start / sampleRate, (double)end / sampleRate,
|
||||
this(start / sampleRate, end / sampleRate,
|
||||
amplitude, ripple);
|
||||
}
|
||||
|
||||
public FrequencyBand(double sampleRate, int start, int end, double amplitude, double ripple, double weight)
|
||||
public FrequencyBand(double sampleRate, double start, double end, double amplitude, double ripple, double weight)
|
||||
{
|
||||
this(sampleRate, start, end, amplitude, ripple);
|
||||
mWeight = weight;
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
package io.github.dsheirer.dsp.filter.fir.real;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilter;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
/**
|
||||
* Finite Impulse Response (FIR) filter for filtering individual float samples or float sample arrays.
|
||||
@@ -113,11 +113,11 @@ public class RealFIRFilter2 extends FIRFilter
|
||||
* @param unfilteredBuffer containing a sample array to be filtered
|
||||
* @return a new reusable buffer with the filtered samples.
|
||||
*/
|
||||
public ReusableBuffer filter(ReusableBuffer unfilteredBuffer)
|
||||
public ReusableFloatBuffer filter(ReusableFloatBuffer unfilteredBuffer)
|
||||
{
|
||||
float[] unfilteredSamples = unfilteredBuffer.getSamples();
|
||||
|
||||
ReusableBuffer filteredBuffer = mReusableBufferQueue.getBuffer(unfilteredSamples.length);
|
||||
ReusableFloatBuffer filteredBuffer = mReusableBufferQueue.getBuffer(unfilteredSamples.length);
|
||||
float[] filteredSamples = filteredBuffer.getSamples();
|
||||
|
||||
for(int x = 0; x < unfilteredSamples.length; x++)
|
||||
|
||||
@@ -15,92 +15,94 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.filter.halfband.complex;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter;
|
||||
import io.github.dsheirer.dsp.filter.Filters;
|
||||
import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter2;
|
||||
import io.github.dsheirer.dsp.mixer.Oscillator;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue;
|
||||
|
||||
public class ComplexHalfBandFilter
|
||||
{
|
||||
private HalfBandFilter mIFilter;
|
||||
private HalfBandFilter mQFilter;
|
||||
private boolean mDecimate;
|
||||
private boolean mDecimateFlag;
|
||||
private ReusableComplexBufferQueue mBufferQueue = new ReusableComplexBufferQueue("ComplexHalfBandFilter");
|
||||
|
||||
/**
|
||||
* @param coefficients
|
||||
* @param gain
|
||||
* @param decimate
|
||||
*/
|
||||
public ComplexHalfBandFilter(float[] coefficients, float gain, boolean decimate)
|
||||
private HalfBandFilter2 mIFilter;
|
||||
private HalfBandFilter2 mQFilter;
|
||||
private float mResidualISample;
|
||||
private float mResidualQSample;
|
||||
private boolean mHasResidual;
|
||||
private int mOutputBufferLength;
|
||||
private float[] mSamples;
|
||||
private int mSamplesPointer;
|
||||
private float[] mFilteredSamples;
|
||||
private int mFilteredSamplesPointer;
|
||||
|
||||
public ComplexHalfBandFilter(float[] coefficients, float gain)
|
||||
{
|
||||
mIFilter = new HalfBandFilter(coefficients, gain);
|
||||
mQFilter = new HalfBandFilter(coefficients, gain);
|
||||
mDecimate = decimate;
|
||||
mIFilter = new HalfBandFilter2(coefficients, gain);
|
||||
mQFilter = new HalfBandFilter2(coefficients, gain);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
public ReusableComplexBuffer filter(ReusableComplexBuffer originalBuffer)
|
||||
{
|
||||
mIFilter.dispose();
|
||||
mQFilter.dispose();
|
||||
mSamples = originalBuffer.getSamples();
|
||||
mSamplesPointer = 0;
|
||||
|
||||
mOutputBufferLength = (mHasResidual ? (mSamples.length + 2) : mSamples.length) / 4 * 2;
|
||||
|
||||
ReusableComplexBuffer filteredBuffer = mBufferQueue.getBuffer(mOutputBufferLength);
|
||||
mFilteredSamples = filteredBuffer.getSamples();
|
||||
mFilteredSamplesPointer = 0;
|
||||
|
||||
if(mHasResidual)
|
||||
{
|
||||
mFilteredSamples[mFilteredSamplesPointer++] = mIFilter.filter(mResidualISample, mSamples[mSamplesPointer++]);
|
||||
mFilteredSamples[mFilteredSamplesPointer++] = mQFilter.filter(mResidualQSample, mSamples[mSamplesPointer++]);
|
||||
mHasResidual = false;
|
||||
}
|
||||
|
||||
while(mSamplesPointer + 3 < mSamples.length)
|
||||
{
|
||||
mFilteredSamples[mFilteredSamplesPointer++] = mIFilter.filter(mSamples[mSamplesPointer], mSamples[mSamplesPointer + 2]);
|
||||
mFilteredSamples[mFilteredSamplesPointer++] = mIFilter.filter(mSamples[mSamplesPointer + 1], mSamples[mSamplesPointer + 3]);
|
||||
mSamplesPointer += 4;
|
||||
}
|
||||
|
||||
if(mSamplesPointer < mSamples.length)
|
||||
{
|
||||
mHasResidual = true;
|
||||
mResidualISample = mSamples[mSamplesPointer++];
|
||||
mResidualQSample = mSamples[mSamplesPointer];
|
||||
}
|
||||
|
||||
originalBuffer.decrementUserCount();
|
||||
return filteredBuffer;
|
||||
}
|
||||
|
||||
public ReusableComplexBuffer filter(ReusableComplexBuffer buffer)
|
||||
public static void main(String[] args)
|
||||
{
|
||||
throw new IllegalStateException("This filter has to be updated to work with reusable buffers");
|
||||
Oscillator oscillator = new Oscillator(1, 16);
|
||||
ComplexHalfBandFilter filter = new ComplexHalfBandFilter(Filters.HALF_BAND_FILTER_27T.getCoefficients(), 1.0f);
|
||||
|
||||
ReusableComplexBufferQueue bufferQueue = new ReusableComplexBufferQueue("Test");
|
||||
|
||||
ReusableComplexBuffer complexBuffer = bufferQueue.getBuffer(8);
|
||||
oscillator.generateComplex(complexBuffer);
|
||||
ReusableComplexBuffer filteredBuffer = filter.filter(complexBuffer);
|
||||
filteredBuffer.decrementUserCount();
|
||||
|
||||
ReusableComplexBuffer complexBuffer2 = bufferQueue.getBuffer(10);
|
||||
oscillator.generateComplex(complexBuffer2);
|
||||
ReusableComplexBuffer filteredBuffer2 = filter.filter(complexBuffer2);
|
||||
filteredBuffer2.decrementUserCount();
|
||||
|
||||
ReusableComplexBuffer complexBuffer3 = bufferQueue.getBuffer(8);
|
||||
oscillator.generateComplex(complexBuffer3);
|
||||
ReusableComplexBuffer filteredBuffer3 = filter.filter(complexBuffer3);
|
||||
filteredBuffer3.decrementUserCount();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// if(mDecimate)
|
||||
// {
|
||||
// float[] samples = buffer.getSamples();
|
||||
//
|
||||
// int half = samples.length / 2;
|
||||
//
|
||||
// float[] decimated;
|
||||
//
|
||||
// if(half % 2 == 0 || mDecimateFlag)
|
||||
// {
|
||||
// decimated = new float[half];
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// /* If inbound buffer size is odd-length, then we have to
|
||||
// * adjust when the first operation is non-decimation, since
|
||||
// * that will produce an outbound buffer 1 sample larger */
|
||||
// decimated = new float[half + 1];
|
||||
// }
|
||||
//
|
||||
// int decimatedPointer = 0;
|
||||
//
|
||||
// for(int x = 0; x < samples.length; x += 2)
|
||||
// {
|
||||
// /* Insert the sample but don't filter */
|
||||
// if(mDecimateFlag)
|
||||
// {
|
||||
// mIFilter.insert(samples[x]);
|
||||
// mQFilter.insert(samples[x + 1]);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// decimated[decimatedPointer++] = mIFilter.filter(samples[x]);
|
||||
// decimated[decimatedPointer++] = mQFilter.filter(samples[x + 1]);
|
||||
// }
|
||||
//
|
||||
// /* Toggle the decimation flag for every sample */
|
||||
// mDecimateFlag = !mDecimateFlag;
|
||||
// }
|
||||
//
|
||||
// //return buffer
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// /* Non Decimate - filter each of the values and return the buffer */
|
||||
// float[] samples = buffer.getSamples();
|
||||
//
|
||||
// for(int x = 0; x < samples.length; x += 2)
|
||||
// {
|
||||
// samples[x] = mIFilter.filter(samples[x]);
|
||||
// samples[x + 1] = mQFilter.filter(samples[x + 1]);
|
||||
// }
|
||||
//
|
||||
// //return buffer
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.filter.halfband.real;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
@Deprecated
|
||||
public class HalfBandFilter
|
||||
{
|
||||
private float[] mBuffer;
|
||||
@@ -84,10 +85,10 @@ public class HalfBandFilter
|
||||
mBufferPointer = mBufferPointer % mBufferSize;
|
||||
}
|
||||
|
||||
public ReusableBuffer filter(ReusableBuffer input)
|
||||
public ReusableFloatBuffer filter(ReusableFloatBuffer input)
|
||||
{
|
||||
float[] inputSamples = input.getSamples();
|
||||
ReusableBuffer output = mReusableBufferQueue.getBuffer(inputSamples.length);
|
||||
ReusableFloatBuffer output = mReusableBufferQueue.getBuffer(inputSamples.length);
|
||||
float[] outputSamples = output.getSamples();
|
||||
|
||||
for(int x = 0; x < inputSamples.length; x++)
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.dsp.filter.halfband.real;
|
||||
|
||||
/**
|
||||
* Implements a half-band filter that produces one filtered output for every two input samples.
|
||||
*
|
||||
* This filter is structured for Java 8+ compiler JIT optimization for SIMD instructions when
|
||||
* supported by the host CPU.
|
||||
*/
|
||||
public class HalfBandFilter2
|
||||
{
|
||||
private static final float CENTER_COEFFICIENT = 0.5f;
|
||||
private float[] mCoefficients;
|
||||
private float[] mEvenSamples;
|
||||
private float[] mOddSamples;
|
||||
private int mCenterSampleIndex;
|
||||
private float mAccumulator;
|
||||
private int mPointer;
|
||||
private float mGain;
|
||||
|
||||
/**
|
||||
* Creates a half band filter with inherent decimation by two.
|
||||
*
|
||||
* @param coefficients of the half-band filter that is odd-length where all odd index coefficients are
|
||||
* zero valued except for the middle odd index coefficient which should be valued 0.5
|
||||
* @param gain value to apply to the output
|
||||
*/
|
||||
public HalfBandFilter2(float[] coefficients, float gain)
|
||||
{
|
||||
if(coefficients.length % 2 == 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Half-band filter coefficients must be odd-length");
|
||||
}
|
||||
|
||||
mGain = gain;
|
||||
|
||||
int half = coefficients.length / 2 + 1;
|
||||
|
||||
mCoefficients = new float[half];
|
||||
mEvenSamples = new float[half];
|
||||
|
||||
//Use only the even coefficients since the odd coefficients are all zero-valued
|
||||
for(int x = 0; x < coefficients.length; x += 2)
|
||||
{
|
||||
mCoefficients[x / 2] = coefficients[x];
|
||||
}
|
||||
|
||||
//Setup the odd samples array as a simple delay line, half the size of the even samples array
|
||||
mOddSamples = new float[half / 2];
|
||||
mCenterSampleIndex = mOddSamples.length - 1;
|
||||
}
|
||||
|
||||
public float filter(float sample1, float sample2)
|
||||
{
|
||||
//Structured for SIMD array copy optimization when supported by host CPU
|
||||
System.arraycopy(mEvenSamples, 0, mEvenSamples, 1, mEvenSamples.length - 1);
|
||||
System.arraycopy(mOddSamples, 0, mOddSamples, 1, mOddSamples.length - 1);
|
||||
|
||||
mEvenSamples[0] = sample1;
|
||||
mOddSamples[0] = sample2;
|
||||
|
||||
mAccumulator = 0.0f;
|
||||
|
||||
//Structured for SIMD dot.product optimization when supported by host CPU
|
||||
for(mPointer = 0; mPointer < mEvenSamples.length; mPointer++)
|
||||
{
|
||||
mAccumulator += (mEvenSamples[mPointer] * mCoefficients[mPointer]);
|
||||
}
|
||||
|
||||
mAccumulator += (mOddSamples[mCenterSampleIndex] * CENTER_COEFFICIENT);
|
||||
mAccumulator *= mGain;
|
||||
|
||||
return mAccumulator;
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ package io.github.dsheirer.dsp.filter.resample;
|
||||
import com.laszlosystems.libresample4j.Resampler;
|
||||
import com.laszlosystems.libresample4j.SampleBuffers;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -32,7 +32,7 @@ public class RealResampler
|
||||
|
||||
private ReusableBufferQueue mReusableBufferQueue = new ReusableBufferQueue("RealResampler");
|
||||
private Resampler mResampler;
|
||||
private Listener<ReusableBuffer> mResampledListener;
|
||||
private Listener<ReusableFloatBuffer> mResampledListener;
|
||||
private BufferManager mBufferManager;
|
||||
private double mResampleFactor;
|
||||
|
||||
@@ -50,11 +50,11 @@ public class RealResampler
|
||||
|
||||
/**
|
||||
* Primary input method to the resampler
|
||||
* @param reusableBuffer to resample
|
||||
* @param reusableFloatBuffer to resample
|
||||
*/
|
||||
public void resample(ReusableBuffer reusableBuffer)
|
||||
public void resample(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
mBufferManager.load(reusableBuffer);
|
||||
mBufferManager.load(reusableFloatBuffer);
|
||||
mResampler.process(mResampleFactor, mBufferManager, false);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class RealResampler
|
||||
* Registers the listener to receive the resampled buffer output
|
||||
* @param resampledBufferListener to receive buffers
|
||||
*/
|
||||
public void setListener(Listener<ReusableBuffer> resampledBufferListener)
|
||||
public void setListener(Listener<ReusableFloatBuffer> resampledBufferListener)
|
||||
{
|
||||
mResampledListener = resampledBufferListener;
|
||||
}
|
||||
@@ -89,10 +89,10 @@ public class RealResampler
|
||||
/**
|
||||
* Queues the buffer sample for resampling
|
||||
*/
|
||||
public void load(ReusableBuffer reusableBuffer)
|
||||
public void load(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
mInputBuffer.put(reusableBuffer.getSamples());
|
||||
reusableBuffer.decrementUserCount();
|
||||
mInputBuffer.put(reusableFloatBuffer.getSamples());
|
||||
reusableFloatBuffer.decrementUserCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,7 +128,7 @@ public class RealResampler
|
||||
|
||||
while(mOutputBuffer.position() > mOutputBufferSize)
|
||||
{
|
||||
ReusableBuffer outputBuffer = mReusableBufferQueue.getBuffer(mOutputBufferSize);
|
||||
ReusableFloatBuffer outputBuffer = mReusableBufferQueue.getBuffer(mOutputBufferSize);
|
||||
|
||||
mOutputBuffer.flip();
|
||||
mOutputBuffer.get(outputBuffer.getSamples());
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.fm;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
|
||||
/**
|
||||
@@ -129,9 +129,9 @@ public class FMDemodulator
|
||||
* @param basebandSampleBuffer containing samples to demodulate
|
||||
* @return demodulated sample buffer.
|
||||
*/
|
||||
public ReusableBuffer demodulate(ReusableComplexBuffer basebandSampleBuffer)
|
||||
public ReusableFloatBuffer demodulate(ReusableComplexBuffer basebandSampleBuffer)
|
||||
{
|
||||
ReusableBuffer demodulatedBuffer = mReusableBufferQueue.getBuffer(basebandSampleBuffer.getSampleCount());
|
||||
ReusableFloatBuffer demodulatedBuffer = mReusableBufferQueue.getBuffer(basebandSampleBuffer.getSampleCount());
|
||||
|
||||
float[] basebandSamples = basebandSampleBuffer.getSamples();
|
||||
float[] demodulatedSamples = demodulatedBuffer.getSamples();
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter2;
|
||||
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
import io.github.dsheirer.dsp.symbol.ISyncDetectListener;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.slf4j.LoggerFactory;
|
||||
* adjustment may not fully compensate for mis-tuned signals and may result in missed decodes of idle bursts. However,
|
||||
* the DC filter will track a mis-tuned signal to baseband quickly upon a continuous transmission.
|
||||
*/
|
||||
public class LTRDecoder implements Listener<ReusableBuffer>, ISyncDetectListener, ISyncStateListener
|
||||
public class LTRDecoder implements Listener<ReusableFloatBuffer>, ISyncDetectListener, ISyncStateListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(LTRDecoder.class);
|
||||
|
||||
@@ -148,10 +148,10 @@ public class LTRDecoder implements Listener<ReusableBuffer>, ISyncDetectListener
|
||||
* @param buffer containing 8.0 kHz unfiltered FM demodulated audio samples with sub-audible LTR signalling.
|
||||
*/
|
||||
@Override
|
||||
public void receive(ReusableBuffer buffer)
|
||||
public void receive(ReusableFloatBuffer buffer)
|
||||
{
|
||||
ReusableBuffer dcFiltered = mDCFilter.filter(buffer);
|
||||
ReusableBuffer lowPassFiltered = mLowPassFilter.filter(dcFiltered);
|
||||
ReusableFloatBuffer dcFiltered = mDCFilter.filter(buffer);
|
||||
ReusableFloatBuffer lowPassFiltered = mLowPassFilter.filter(dcFiltered);
|
||||
|
||||
for(float sample : lowPassFiltered.getSamples())
|
||||
{
|
||||
|
||||
@@ -2,8 +2,8 @@ package io.github.dsheirer.dsp.gain;
|
||||
|
||||
import io.github.dsheirer.buffer.DoubleCircularBuffer;
|
||||
import io.github.dsheirer.buffer.RealCircularBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
@@ -147,9 +147,9 @@ public class AutomaticGainControl
|
||||
* @param input samples to process
|
||||
* @return output samples with gain applied
|
||||
*/
|
||||
public ReusableBuffer process(ReusableBuffer input)
|
||||
public ReusableFloatBuffer process(ReusableFloatBuffer input)
|
||||
{
|
||||
ReusableBuffer output = mReusableBufferQueue.getBuffer(input.getSampleCount());
|
||||
ReusableFloatBuffer output = mReusableBufferQueue.getBuffer(input.getSampleCount());
|
||||
|
||||
float[] inputSamples = input.getSamples();
|
||||
float[] outputSamples = output.getSamples();
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.mixer;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -127,8 +127,8 @@ public abstract class AbstractOscillator implements IOscillator
|
||||
|
||||
for(int x = 0; x < sampleCount; x++)
|
||||
{
|
||||
samples[x] = inphase();
|
||||
rotate();
|
||||
samples[x] = quadrature();
|
||||
}
|
||||
|
||||
return samples;
|
||||
@@ -182,11 +182,11 @@ public abstract class AbstractOscillator implements IOscillator
|
||||
|
||||
/**
|
||||
* Generates enough real samples to fill the reusable buffer
|
||||
* @param reusableBuffer to fill with real samples
|
||||
* @param reusableFloatBuffer to fill with real samples
|
||||
*/
|
||||
@Override
|
||||
public void generateReal(ReusableBuffer reusableBuffer)
|
||||
public void generateReal(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
reusableBuffer.reloadFrom(generateReal(reusableBuffer.getSampleCount()), System.currentTimeMillis());
|
||||
reusableFloatBuffer.reloadFrom(generateReal(reusableFloatBuffer.getSampleCount()), System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.mixer;
|
||||
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
/**
|
||||
* Oscillator interface.
|
||||
@@ -102,9 +102,9 @@ public interface IOscillator
|
||||
* Generates real samples and fills the reusable buffer using the current system time in millis for
|
||||
* the update timestamp.
|
||||
*
|
||||
* @param reusableBuffer to fill with complex samples
|
||||
* @param reusableFloatBuffer to fill with complex samples
|
||||
*/
|
||||
void generateReal(ReusableBuffer reusableBuffer);
|
||||
void generateReal(ReusableFloatBuffer reusableFloatBuffer);
|
||||
|
||||
/**
|
||||
* Mixes (heterodynes) the complex sample array using the current settings of this oscillator.
|
||||
|
||||
@@ -18,6 +18,10 @@ package io.github.dsheirer.dsp.mixer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LowPhaseNoiseOscillator extends AbstractOscillator
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(LowPhaseNoiseOscillator.class);
|
||||
@@ -82,4 +86,54 @@ public class LowPhaseNoiseOscillator extends AbstractOscillator
|
||||
//Update the gain value for the next rotation
|
||||
mGain = THREE_HALVES - ((mPreviousInphase * mPreviousInphase) + (mPreviousQuadrature * mPreviousQuadrature));
|
||||
}
|
||||
|
||||
public static long process(AbstractOscillator oscillator, int iterations, int sampleCount)
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
|
||||
for(int x = 0; x < iterations; x++)
|
||||
{
|
||||
float[] samples = oscillator.generateComplex(sampleCount);
|
||||
}
|
||||
|
||||
long duration = System.nanoTime() - start;
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
mLog.debug("Starting ...");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss.SSS");
|
||||
|
||||
AbstractOscillator lpno = new LowPhaseNoiseOscillator(100, 800);
|
||||
AbstractOscillator regular = new Oscillator(100, 800);
|
||||
|
||||
int iterations = 10000;
|
||||
int sampleCount = 1000000;
|
||||
|
||||
mLog.debug("Warm up - lpno");
|
||||
process(lpno, iterations, sampleCount);
|
||||
process(lpno, iterations, sampleCount);
|
||||
process(lpno, iterations, sampleCount);
|
||||
|
||||
mLog.debug("Warm up - regular");
|
||||
process(regular, iterations, sampleCount);
|
||||
process(regular, iterations, sampleCount);
|
||||
process(regular, iterations, sampleCount);
|
||||
|
||||
mLog.debug("Test - lpno");
|
||||
process(lpno, iterations, sampleCount);
|
||||
process(lpno, iterations, sampleCount);
|
||||
long duration = process(lpno, iterations, sampleCount);
|
||||
mLog.debug("Duration Low Phase Noise: " + sdf.format(new Date(TimeUnit.NANOSECONDS.toMillis(duration))));
|
||||
|
||||
mLog.debug("Test - regular");
|
||||
process(regular, iterations, sampleCount);
|
||||
process(regular, iterations, sampleCount);
|
||||
duration = process(regular, iterations, sampleCount);
|
||||
mLog.debug("Duration Regular: " + sdf.format(new Date(TimeUnit.NANOSECONDS.toMillis(duration))));
|
||||
|
||||
// mLog.debug(" Delta: " + sdf.format(new Date(TimeUnit.NANOSECONDS.toMillis(duration2 - duration))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +95,45 @@ public class DQPSKDecisionDirectedSymbolEvaluator implements IPSKSymbolEvaluator
|
||||
|
||||
//Since we've rotated the error symbol back to 0 radians, the quadrature value closely approximates the
|
||||
//arctan of the error angle relative to 0 radians and this provides our error value
|
||||
mPhaseError = -mEvaluationSymbol.quadrature();
|
||||
float errorNormalized = normalize(mEvaluationSymbol.quadrature(), 0.3f);
|
||||
|
||||
mPhaseError = -errorNormalized;
|
||||
|
||||
//Timing error is the same as phase error with the sign corrected according to the vector's angular rotation
|
||||
mTimingError = mEvaluationSymbol.quadrature() * mTimingErrorPolarity;
|
||||
mTimingError = errorNormalized * mTimingErrorPolarity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrains value to the range of ( -maximum <> maximum )
|
||||
*/
|
||||
private static float clip(float value, float maximum)
|
||||
{
|
||||
if(value > maximum)
|
||||
{
|
||||
return maximum;
|
||||
}
|
||||
else if(value < -maximum)
|
||||
{
|
||||
return -maximum;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrains timing error to +/- the maximum value and corrects any
|
||||
* floating point invalid numbers
|
||||
*/
|
||||
private float normalize(float error, float maximum)
|
||||
{
|
||||
if(Float.isNaN(error))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return clip(error, maximum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,7 +100,7 @@ public class DQPSKGardnerSymbolEvaluator implements IPSKSymbolEvaluator<Dibit>
|
||||
|
||||
//Since we've rotated the error symbol back to 0 radians, the quadrature value closely approximates the
|
||||
//arctan of the error angle relative to 0 radians and this provides our error value
|
||||
mPhaseError = -mEvaluationSymbol.quadrature();
|
||||
mPhaseError = normalize(-mEvaluationSymbol.quadrature(), 0.3f);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,11 +20,13 @@ package io.github.dsheirer.dsp.psk.pll;
|
||||
*/
|
||||
public enum PLLGain
|
||||
{
|
||||
LEVEL_1(50.0, 0, 1),
|
||||
LEVEL_2(75.0, 2, 4),
|
||||
LEVEL_3(100.0, 5, 6),
|
||||
LEVEL_4(125.0, 7, 8),
|
||||
LEVEL_5(150.0, 9, 10);
|
||||
//NOTE: static gain level of 200 produced the best results for releases prior to 0.3.4b2
|
||||
|
||||
LEVEL_1(150.0, 0, 1),
|
||||
LEVEL_2(170.0, 2, 4),
|
||||
LEVEL_3(190.0, 5, 6),
|
||||
LEVEL_4(200.0, 7, 8),
|
||||
LEVEL_5(200.0, 9, 10);
|
||||
|
||||
private double mLoopBandwidth;
|
||||
private int mRangeStart;
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.dsp.symbol;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBufferQueue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Assembles reusable byte buffers from an incoming stream of Dibits.
|
||||
*/
|
||||
public class DibitToByteBufferAssembler implements Listener<Dibit>, IReusableByteBufferProvider
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DibitToByteBufferAssembler.class);
|
||||
|
||||
private ReusableByteBufferQueue mBufferQueue = new ReusableByteBufferQueue("DibitToByteBufferAssembler");
|
||||
private ReusableByteBuffer mCurrentBuffer;
|
||||
private int mBufferPointer;
|
||||
private int mBufferSize;
|
||||
private byte mCurrentByte;
|
||||
private int mDibitCount;
|
||||
private Listener<ReusableByteBuffer> mBufferListener;
|
||||
|
||||
/**
|
||||
* Constructs an assembler to produce reusable byte buffers of the specified size
|
||||
*
|
||||
* @param bufferSize
|
||||
*/
|
||||
public DibitToByteBufferAssembler(int bufferSize)
|
||||
{
|
||||
mBufferSize = bufferSize;
|
||||
getNextBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the current buffer to the registered listener and creates a new buffer, resetting
|
||||
* the buffer pointer to zero so that new dibits can be loaded.
|
||||
*/
|
||||
private void getNextBuffer()
|
||||
{
|
||||
if(mCurrentBuffer != null && mBufferListener != null)
|
||||
{
|
||||
mBufferListener.receive(mCurrentBuffer);
|
||||
}
|
||||
|
||||
mCurrentBuffer = mBufferQueue.getBuffer(mBufferSize);
|
||||
mBufferPointer = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Dibit dibit)
|
||||
{
|
||||
mCurrentByte <<= 2;
|
||||
|
||||
switch(dibit)
|
||||
{
|
||||
case D01_PLUS_3:
|
||||
mCurrentByte |= 0x01;
|
||||
break;
|
||||
case D00_PLUS_1:
|
||||
//no-op - value is already 00
|
||||
break;
|
||||
case D10_MINUS_1:
|
||||
mCurrentByte |= 0x02;
|
||||
break;
|
||||
case D11_MINUS_3:
|
||||
mCurrentByte |= 0x03;
|
||||
break;
|
||||
}
|
||||
|
||||
mDibitCount++;
|
||||
|
||||
if(mDibitCount >= 4)
|
||||
{
|
||||
mCurrentBuffer.getBytes()[mBufferPointer++] = mCurrentByte;
|
||||
mCurrentByte = 0x00;
|
||||
mDibitCount = 0;
|
||||
|
||||
if(mBufferPointer >= mBufferSize)
|
||||
{
|
||||
getNextBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive fully assembled byte buffers from this assembler.
|
||||
*/
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
{
|
||||
mBufferListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving buffers from this assembler
|
||||
*/
|
||||
@Override
|
||||
public void removeBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
{
|
||||
mBufferListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBufferListeners()
|
||||
{
|
||||
return mBufferListener != null;
|
||||
}
|
||||
}
|
||||
@@ -259,7 +259,8 @@ public class ChannelizerViewer extends JFrame
|
||||
|
||||
for(int x = 0; x < mChannelCount; x++)
|
||||
{
|
||||
TunerChannelSource source = mTestTuner.getChannelSourceManager().getTestingSource(x);
|
||||
TunerChannel tunerChannel = new TunerChannel(100000000, 12500);
|
||||
TunerChannelSource source = mTestTuner.getChannelSourceManager().getSource(tunerChannel, null);
|
||||
DiscreteChannelPanel channelPanel = new DiscreteChannelPanel(mSettingsManager, source, x);
|
||||
channelPanel.setDFTSize(mChannelPanelDFTSize);
|
||||
|
||||
@@ -361,7 +362,7 @@ public class ChannelizerViewer extends JFrame
|
||||
mComplexDecibelConverter.addListener(mSpectrumPanel);
|
||||
|
||||
TunerChannel tunerChannel = new TunerChannel(frequency, bandwidth);
|
||||
mSource = mTestTuner.getChannelSourceManager().getSource(tunerChannel);
|
||||
mSource = mTestTuner.getChannelSourceManager().getSource(tunerChannel, null);
|
||||
|
||||
if(mSource != null)
|
||||
{
|
||||
@@ -555,7 +556,7 @@ public class ChannelizerViewer extends JFrame
|
||||
{
|
||||
if(sourceCount < maxSourceCount)
|
||||
{
|
||||
TunerChannelSource source = tuner.getChannelSourceManager().getSource(tunerChannel);
|
||||
TunerChannelSource source = tuner.getChannelSourceManager().getSource(tunerChannel, null);
|
||||
|
||||
if(source != null)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ import io.github.dsheirer.source.ISourceEventProcessor;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.Tuner;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel;
|
||||
@@ -376,10 +377,11 @@ public class ChannelizerViewer2 extends JFrame
|
||||
mComplexDecibelConverter.addListener(mSpectrumPanel);
|
||||
|
||||
TunerChannel tunerChannel = new TunerChannel(frequency, bandwidth);
|
||||
mSource = mTestTuner.getChannelSourceManager().getSource(tunerChannel);
|
||||
mSource = mTestTuner.getChannelSourceManager().getSource(tunerChannel, new ChannelSpecification(50000, 12500, 6000, 7000));
|
||||
|
||||
if(mSource != null)
|
||||
{
|
||||
mLog.debug("Channel: " + mSource.getTunerChannel() + " Rate:" + mSource.getSampleRate());
|
||||
mSource.setListener(new Listener<ReusableComplexBuffer>()
|
||||
{
|
||||
@Override
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* Application for viewing and working with demodulators and recording files.
|
||||
@@ -42,6 +43,9 @@ public class DemodulatorViewerFX extends Application
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DemodulatorViewerFX.class);
|
||||
|
||||
private static final String PREFERENCE_FILE_OPEN_DEFAULT_FOLDER = "default.folder";
|
||||
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(DemodulatorViewerFX.class);
|
||||
private Stage mStage;
|
||||
private MenuBar mMenuBar;
|
||||
private RecentFilesMenu mRecentFilesMenu;
|
||||
@@ -117,9 +121,22 @@ public class DemodulatorViewerFX extends Application
|
||||
{
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("Open an I/Q recording file");
|
||||
|
||||
String directory = mPreferences.get(PREFERENCE_FILE_OPEN_DEFAULT_FOLDER, null);
|
||||
|
||||
if(directory != null)
|
||||
{
|
||||
fileChooser.setInitialDirectory(new File(directory));
|
||||
}
|
||||
|
||||
File file = fileChooser.showOpenDialog(mStage);
|
||||
getViewerDesktop().load(file);
|
||||
getRecentFilesMenu().add(file);
|
||||
|
||||
if(file != null)
|
||||
{
|
||||
getViewerDesktop().load(file);
|
||||
getRecentFilesMenu().add(file);
|
||||
mPreferences.put(PREFERENCE_FILE_OPEN_DEFAULT_FOLDER, file.getParent());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ package io.github.dsheirer.gui.instrument;
|
||||
|
||||
import io.github.dsheirer.gui.instrument.decoder.AbstractDecoderPane;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.source.IControllableFileSource;
|
||||
import io.github.dsheirer.source.IFrameLocationListener;
|
||||
import io.github.dsheirer.source.wave.ComplexWaveSource;
|
||||
@@ -177,7 +177,7 @@ public class PlaybackController extends HBox implements IFrameLocationListener
|
||||
/**
|
||||
* Adds listener to receive complex buffers from this playback
|
||||
*/
|
||||
public void addListener(Listener<ReusableBuffer> listener)
|
||||
public void addListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mReusableBufferBroadcaster.addListener(listener);
|
||||
}
|
||||
@@ -185,7 +185,7 @@ public class PlaybackController extends HBox implements IFrameLocationListener
|
||||
/**
|
||||
* Removes the listener from receiving complex buffers from this playback
|
||||
*/
|
||||
public void removeListener(Listener<ReusableBuffer> listener)
|
||||
public void removeListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mReusableBufferBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.prefs.Preferences;
|
||||
|
||||
public class RecentFilesMenu extends Menu implements EventHandler<ActionEvent>
|
||||
{
|
||||
private static final String PREFERENCE_RECENT_FILE = ".recent.file.";
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(RecentFilesMenu.class);
|
||||
private IFileSelectionListener mFileSelectionListener;
|
||||
private String mIdentifier;
|
||||
@@ -111,7 +112,7 @@ public class RecentFilesMenu extends Menu implements EventHandler<ActionEvent>
|
||||
*/
|
||||
private String getKey(int index)
|
||||
{
|
||||
return mIdentifier + ".recent.file." + index;
|
||||
return mIdentifier + PREFERENCE_RECENT_FILE + index;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,7 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.chart.LineChart;
|
||||
import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.ValueAxis;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -42,6 +43,24 @@ public class DoubleLineChart extends LineChart implements Listener<Double>
|
||||
init(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum displayable value for the y axis
|
||||
* @param min
|
||||
*/
|
||||
public void setMin(double min)
|
||||
{
|
||||
((ValueAxis)getYAxis()).setLowerBound(min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum displayable value for the y axis
|
||||
* @param max
|
||||
*/
|
||||
public void setMax(double max)
|
||||
{
|
||||
((ValueAxis)getYAxis()).setUpperBound(max);
|
||||
}
|
||||
|
||||
private void init(int length)
|
||||
{
|
||||
mDoubleCircularBuffer = new DoubleCircularBuffer(length);
|
||||
|
||||
@@ -17,7 +17,7 @@ package io.github.dsheirer.gui.instrument.chart;
|
||||
|
||||
import io.github.dsheirer.buffer.RealCircularBuffer;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.chart.LineChart;
|
||||
@@ -27,7 +27,7 @@ import javafx.scene.layout.StackPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RealSampleLineChart extends LineChart implements Listener<ReusableBuffer>
|
||||
public class RealSampleLineChart extends LineChart implements Listener<ReusableFloatBuffer>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RealSampleLineChart.class);
|
||||
private ObservableList<Data<Integer,Float>> mSamples = FXCollections.observableArrayList();
|
||||
@@ -65,7 +65,7 @@ public class RealSampleLineChart extends LineChart implements Listener<ReusableB
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer buffer)
|
||||
public void receive(ReusableFloatBuffer buffer)
|
||||
{
|
||||
for(float sample: buffer.getSamples())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.gui.instrument.chart;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
public class SamplesPerSymbolChart extends DoubleLineChart
|
||||
{
|
||||
private double mFractionalSamplesPerSymbol;
|
||||
private double mPreviousSamplesPerSymbol;
|
||||
|
||||
public SamplesPerSymbolChart(int length, double samplesPerSymbol)
|
||||
{
|
||||
super("Samples Per Symbol", -1.0, 1.0, 0.1, length);
|
||||
setSamplesPerSymbol(samplesPerSymbol);
|
||||
}
|
||||
|
||||
public void setSamplesPerSymbol(double samplesPerSymbol)
|
||||
{
|
||||
mFractionalSamplesPerSymbol = samplesPerSymbol - ((int)samplesPerSymbol);
|
||||
mPreviousSamplesPerSymbol = samplesPerSymbol;
|
||||
|
||||
ObservableList<Series> seriesList = getData();
|
||||
seriesList.get(0).setName("Samples Per Symbol:" + samplesPerSymbol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Double samplesPerSymbol)
|
||||
{
|
||||
double delta = samplesPerSymbol - mPreviousSamplesPerSymbol - mFractionalSamplesPerSymbol;
|
||||
|
||||
while(delta < (-1.0 + mFractionalSamplesPerSymbol))
|
||||
{
|
||||
delta++;
|
||||
}
|
||||
|
||||
while(delta > 1.0)
|
||||
{
|
||||
delta--;
|
||||
}
|
||||
|
||||
super.receive(delta);
|
||||
|
||||
mPreviousSamplesPerSymbol = samplesPerSymbol;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import io.github.dsheirer.gui.instrument.chart.RealSampleLineChart;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
@@ -52,7 +52,7 @@ public abstract class AbstractAFSK1200Pane extends RealDecoderPane
|
||||
private void init()
|
||||
{
|
||||
//This is force-cast without type checking ....
|
||||
addListener((Listener<ReusableBuffer>)getDecoder());
|
||||
addListener((Listener<ReusableFloatBuffer>)getDecoder());
|
||||
|
||||
addListener(getSampleLineChart());
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ package io.github.dsheirer.gui.instrument.decoder;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.SampleType;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
public abstract class AbstractDecoderPane<T extends ReusableBuffer> extends VBox implements Listener<T>
|
||||
public abstract class AbstractDecoderPane<T extends ReusableFloatBuffer> extends VBox implements Listener<T>
|
||||
{
|
||||
abstract SampleType getSampleType();
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import io.github.dsheirer.gui.instrument.chart.ComplexSampleLineChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.DoubleLineChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.EyeDiagramChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.PhaseLineChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.SamplesPerSymbolChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.SymbolChart;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
@@ -34,14 +35,14 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(P25Phase1LSMPane.class);
|
||||
|
||||
private HBox mSampleChartBox;
|
||||
private ComplexSampleLineChart mSampleLineChart;
|
||||
private HBox mTopChartBox;
|
||||
private ComplexSampleLineChart mDifferentialDemodulatedSamplesChartBox;
|
||||
private EyeDiagramChart mEyeDiagramChart;
|
||||
private HBox mDecoderChartBox;
|
||||
private SymbolChart mSymbolChart;
|
||||
private HBox mBottomChartBox;
|
||||
private SymbolChart mSymbolConstellationChart;
|
||||
private PhaseLineChart mPLLPhaseErrorLineChart;
|
||||
private DoubleLineChart mPLLFrequencyLineChart;
|
||||
private DoubleLineChart mSamplesPerSymbolLineChart;
|
||||
private SamplesPerSymbolChart mSamplesPerSymbolLineChart;
|
||||
private ReusableBufferBroadcaster mFilteredBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private P25DecoderLSMInstrumented mDecoder = new P25DecoderLSMInstrumented(null);
|
||||
|
||||
@@ -56,16 +57,16 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
addListener(getDecoder());
|
||||
|
||||
getDecoder().setFilteredBufferListener(mFilteredBufferBroadcaster);
|
||||
getDecoder().setComplexSymbolListener(getSymbolChart());
|
||||
getDecoder().setComplexSymbolListener(getSymbolConstellationChart());
|
||||
getDecoder().setPLLPhaseErrorListener(getPLLPhaseErrorLineChart());
|
||||
getDecoder().setPLLFrequencyListener(getPLLFrequencyLineChart());
|
||||
getDecoder().setSymbolDecisionDataListener(getEyeDiagramChart());
|
||||
getDecoder().setSamplesPerSymbolListener(getSamplesPerSymbolLineChart());
|
||||
mFilteredBufferBroadcaster.addListener(getSampleLineChart());
|
||||
mFilteredBufferBroadcaster.addListener(getDifferentialDemodulatedSamplesChartBox());
|
||||
|
||||
HBox.setHgrow(getSampleChartBox(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getDecoderChartBox(), Priority.ALWAYS);
|
||||
getChildren().addAll(getSampleChartBox(), getDecoderChartBox());
|
||||
HBox.setHgrow(getTopChartBox(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getBottomChartBox(), Priority.ALWAYS);
|
||||
getChildren().addAll(getTopChartBox(), getBottomChartBox());
|
||||
|
||||
mDecoder.setMessageListener(new Listener<Message>()
|
||||
{
|
||||
@@ -86,7 +87,8 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
mDecoder.setSampleRate(sampleRate);
|
||||
double samplesPerSymbol = sampleRate / 4800.0;
|
||||
|
||||
getSampleLineChart().setSamplesPerSymbol((int)samplesPerSymbol);
|
||||
getDifferentialDemodulatedSamplesChartBox().setSamplesPerSymbol((int)samplesPerSymbol);
|
||||
getSamplesPerSymbolLineChart().setSamplesPerSymbol(samplesPerSymbol);
|
||||
}
|
||||
|
||||
private P25DecoderLSMInstrumented getDecoder()
|
||||
@@ -94,69 +96,69 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
return mDecoder;
|
||||
}
|
||||
|
||||
private SymbolChart getSymbolChart()
|
||||
private SymbolChart getSymbolConstellationChart()
|
||||
{
|
||||
if(mSymbolChart == null)
|
||||
if(mSymbolConstellationChart == null)
|
||||
{
|
||||
mSymbolChart = new SymbolChart(10);
|
||||
mSymbolConstellationChart = new SymbolChart(10);
|
||||
}
|
||||
|
||||
return mSymbolChart;
|
||||
return mSymbolConstellationChart;
|
||||
}
|
||||
|
||||
private HBox getDecoderChartBox()
|
||||
private HBox getBottomChartBox()
|
||||
{
|
||||
if(mDecoderChartBox == null)
|
||||
if(mBottomChartBox == null)
|
||||
{
|
||||
mDecoderChartBox = new HBox();
|
||||
mDecoderChartBox.setMaxHeight(Double.MAX_VALUE);
|
||||
getSymbolChart().setMaxWidth(Double.MAX_VALUE);
|
||||
mBottomChartBox = new HBox();
|
||||
mBottomChartBox.setMaxHeight(Double.MAX_VALUE);
|
||||
getSymbolConstellationChart().setMaxWidth(Double.MAX_VALUE);
|
||||
getPLLPhaseErrorLineChart().setMaxWidth(Double.MAX_VALUE);
|
||||
getPLLFrequencyLineChart().setMaxWidth(Double.MAX_VALUE);
|
||||
HBox.setHgrow(getSymbolChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getSymbolConstellationChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getPLLPhaseErrorLineChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getPLLFrequencyLineChart(), Priority.ALWAYS);
|
||||
mDecoderChartBox.getChildren().addAll(getSymbolChart(), getPLLPhaseErrorLineChart(),
|
||||
mBottomChartBox.getChildren().addAll(getSymbolConstellationChart(), getPLLPhaseErrorLineChart(),
|
||||
getPLLFrequencyLineChart());
|
||||
}
|
||||
|
||||
return mDecoderChartBox;
|
||||
return mBottomChartBox;
|
||||
}
|
||||
|
||||
private HBox getSampleChartBox()
|
||||
private HBox getTopChartBox()
|
||||
{
|
||||
if(mSampleChartBox == null)
|
||||
if(mTopChartBox == null)
|
||||
{
|
||||
mSampleChartBox = new HBox();
|
||||
mSampleChartBox.setMaxHeight(Double.MAX_VALUE);
|
||||
getSampleLineChart().setMaxWidth(Double.MAX_VALUE);
|
||||
mTopChartBox = new HBox();
|
||||
mTopChartBox.setMaxHeight(Double.MAX_VALUE);
|
||||
getDifferentialDemodulatedSamplesChartBox().setMaxWidth(Double.MAX_VALUE);
|
||||
getEyeDiagramChart().setMaxWidth(Double.MAX_VALUE);
|
||||
getSamplesPerSymbolLineChart().setMaxWidth(Double.MAX_VALUE);
|
||||
HBox.setHgrow(getSampleLineChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getDifferentialDemodulatedSamplesChartBox(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getEyeDiagramChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getSamplesPerSymbolLineChart(), Priority.ALWAYS);
|
||||
mSampleChartBox.getChildren().addAll(getSampleLineChart(), getEyeDiagramChart(),
|
||||
mTopChartBox.getChildren().addAll(getDifferentialDemodulatedSamplesChartBox(), getEyeDiagramChart(),
|
||||
getSamplesPerSymbolLineChart());
|
||||
}
|
||||
|
||||
return mSampleChartBox;
|
||||
return mTopChartBox;
|
||||
}
|
||||
|
||||
private ComplexSampleLineChart getSampleLineChart()
|
||||
private ComplexSampleLineChart getDifferentialDemodulatedSamplesChartBox()
|
||||
{
|
||||
if(mSampleLineChart == null)
|
||||
if(mDifferentialDemodulatedSamplesChartBox == null)
|
||||
{
|
||||
mSampleLineChart = new ComplexSampleLineChart(100, 10);
|
||||
mDifferentialDemodulatedSamplesChartBox = new ComplexSampleLineChart(100, 10);
|
||||
}
|
||||
|
||||
return mSampleLineChart;
|
||||
return mDifferentialDemodulatedSamplesChartBox;
|
||||
}
|
||||
|
||||
private EyeDiagramChart getEyeDiagramChart()
|
||||
{
|
||||
if(mEyeDiagramChart == null)
|
||||
{
|
||||
mEyeDiagramChart = new EyeDiagramChart(10, "Middle: 3-4 / Symbol: 8-9");
|
||||
mEyeDiagramChart = new EyeDiagramChart(10, "Symbol/Eye Diagram");
|
||||
}
|
||||
|
||||
return mEyeDiagramChart;
|
||||
@@ -182,11 +184,11 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
return mPLLFrequencyLineChart;
|
||||
}
|
||||
|
||||
private DoubleLineChart getSamplesPerSymbolLineChart()
|
||||
private SamplesPerSymbolChart getSamplesPerSymbolLineChart()
|
||||
{
|
||||
if(mSamplesPerSymbolLineChart == null)
|
||||
{
|
||||
mSamplesPerSymbolLineChart = new DoubleLineChart( "Sample Point", 4.5, 7.0, 0.1, 40);
|
||||
mSamplesPerSymbolLineChart = new SamplesPerSymbolChart(40, 6.25);
|
||||
}
|
||||
|
||||
return mSamplesPerSymbolLineChart;
|
||||
|
||||
@@ -18,11 +18,11 @@ package io.github.dsheirer.gui.instrument.decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.SampleType;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
public class RealDecoderPane extends AbstractDecoderPane<ReusableBuffer>
|
||||
public class RealDecoderPane extends AbstractDecoderPane<ReusableFloatBuffer>
|
||||
{
|
||||
private ReusableBufferBroadcaster mBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private DecoderType mDecoderType;
|
||||
@@ -47,17 +47,17 @@ public class RealDecoderPane extends AbstractDecoderPane<ReusableBuffer>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
mBufferBroadcaster.broadcast(reusableBuffer);
|
||||
mBufferBroadcaster.broadcast(reusableFloatBuffer);
|
||||
}
|
||||
|
||||
public void addListener(Listener<ReusableBuffer> listener)
|
||||
public void addListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mBufferBroadcaster.addListener(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener<ReusableBuffer> listener)
|
||||
public void removeListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mBufferBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
@@ -49,11 +49,14 @@ import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.source.ComplexSource;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
@@ -93,8 +96,9 @@ public class ProcessingChain implements IChannelEventListener
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class);
|
||||
|
||||
private ReusableBufferBroadcaster<ReusableAudioPacket> mAudioPacketBroadcaster = new ReusableBufferBroadcaster<>();
|
||||
private ReusableBufferBroadcaster<ReusableBuffer> mDemodulatedAudioBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private ReusableBufferBroadcaster<ReusableFloatBuffer> mDemodulatedAudioBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private ReusableBufferBroadcaster<ReusableComplexBuffer> mBasebandComplexBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private ReusableBufferBroadcaster<ReusableByteBuffer> mDemodulatedBitstreamBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private Broadcaster<AttributeChangeRequest> mAttributeChangeRequestBroadcaster = new Broadcaster<>();
|
||||
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
|
||||
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster<>();
|
||||
@@ -163,6 +167,7 @@ public class ProcessingChain implements IChannelEventListener
|
||||
mCallEventBroadcaster.dispose();
|
||||
mChannelEventBroadcaster.dispose();
|
||||
mBasebandComplexBufferBroadcaster.dispose();
|
||||
mDemodulatedBitstreamBufferBroadcaster.dispose();
|
||||
mMessageBroadcaster.dispose();
|
||||
mSquelchStateBroadcaster.dispose();
|
||||
}
|
||||
@@ -323,6 +328,11 @@ public class ProcessingChain implements IChannelEventListener
|
||||
mDemodulatedAudioBufferBroadcaster.addListener(((IReusableBufferListener)module).getReusableBufferListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableByteBufferListener)
|
||||
{
|
||||
mDemodulatedBitstreamBufferBroadcaster.addListener(((IReusableByteBufferListener)module).getReusableByteBufferListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableComplexBufferListener)
|
||||
{
|
||||
mBasebandComplexBufferBroadcaster.addListener(((IReusableComplexBufferListener)module).getReusableComplexBufferListener());
|
||||
@@ -385,6 +395,11 @@ public class ProcessingChain implements IChannelEventListener
|
||||
mDemodulatedAudioBufferBroadcaster.removeListener(((IReusableBufferListener)module).getReusableBufferListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableByteBufferListener)
|
||||
{
|
||||
mDemodulatedBitstreamBufferBroadcaster.removeListener(((IReusableByteBufferListener) module).getReusableByteBufferListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableComplexBufferListener)
|
||||
{
|
||||
mBasebandComplexBufferBroadcaster.removeListener(((IReusableComplexBufferListener) module).getReusableComplexBufferListener());
|
||||
@@ -452,6 +467,11 @@ public class ProcessingChain implements IChannelEventListener
|
||||
((IMessageProvider) module).setMessageListener(mMessageBroadcaster);
|
||||
}
|
||||
|
||||
if(module instanceof IReusableByteBufferProvider)
|
||||
{
|
||||
((IReusableByteBufferProvider)module).setBufferListener(mDemodulatedBitstreamBufferBroadcaster);
|
||||
}
|
||||
|
||||
if(module instanceof IReusableBufferProvider)
|
||||
{
|
||||
((IReusableBufferProvider)module).setBufferListener(mDemodulatedAudioBufferBroadcaster);
|
||||
@@ -484,6 +504,11 @@ public class ProcessingChain implements IChannelEventListener
|
||||
((IAudioPacketProvider) module).setAudioPacketListener(null);
|
||||
}
|
||||
|
||||
if(module instanceof IReusableByteBufferProvider)
|
||||
{
|
||||
((IReusableByteBufferProvider)module).removeBufferListener(mDemodulatedBitstreamBufferBroadcaster);
|
||||
}
|
||||
|
||||
if(module instanceof ICallEventProvider)
|
||||
{
|
||||
((ICallEventProvider) module).removeCallEventListener(mCallEventBroadcaster);
|
||||
@@ -540,16 +565,10 @@ public class ProcessingChain implements IChannelEventListener
|
||||
{
|
||||
if(mSource != null)
|
||||
{
|
||||
//Reset each of the modules
|
||||
for(Module module : mModules)
|
||||
{
|
||||
module.reset();
|
||||
}
|
||||
|
||||
//Broadcast the source sample rate so that each of the modules can self-configure
|
||||
mSourceEventBroadcaster.broadcast(SourceEvent.sampleRateChange(mSource.getSampleRate(), "Processing Chain Startup"));
|
||||
|
||||
//Broadcast the source center frequencysample rate so that each of the modules can self-configure
|
||||
//Broadcast the source center frequency / sample rate so that each of the modules can self-configure
|
||||
mSourceEventBroadcaster.broadcast(SourceEvent.frequencyChange(mSource.getFrequency(), "Processing Chain Startup"));
|
||||
|
||||
//Setup the channel state to monitor source overflow conditions
|
||||
@@ -627,6 +646,12 @@ public class ProcessingChain implements IChannelEventListener
|
||||
{
|
||||
module.stop();
|
||||
}
|
||||
|
||||
//Reset each of the modules
|
||||
for(Module module : mModules)
|
||||
{
|
||||
module.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,7 +815,7 @@ public class ProcessingChain implements IChannelEventListener
|
||||
/**
|
||||
* Adds listener to receive demodulated audio buffers from an modules that produce demodulated audio.
|
||||
*/
|
||||
public void addDemodulatedAudioListener(Listener<ReusableBuffer> listener)
|
||||
public void addDemodulatedAudioListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mDemodulatedAudioBufferBroadcaster.addListener(listener);
|
||||
}
|
||||
@@ -798,7 +823,7 @@ public class ProcessingChain implements IChannelEventListener
|
||||
/**
|
||||
* Removes the listener from receiving demodulated audio buffers.
|
||||
*/
|
||||
public void removeDemodulatedAudioListener(Listener<ReusableBuffer> listener)
|
||||
public void removeDemodulatedAudioListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mDemodulatedAudioBufferBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class DecoderFactory
|
||||
@@ -346,7 +347,6 @@ public class DecoderFactory
|
||||
filters.add(new MPT1327MessageFilter());
|
||||
break;
|
||||
case P25_PHASE1:
|
||||
case P25_PHASE2:
|
||||
filters.add(new P25MessageFilterSet());
|
||||
break;
|
||||
case PASSPORT:
|
||||
@@ -467,4 +467,14 @@ public class DecoderFactory
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoder(s) that support bitstreams.
|
||||
*
|
||||
* @return set of decoders that support bitstreams.
|
||||
*/
|
||||
public static EnumSet<DecoderType> getBitstreamDecoders()
|
||||
{
|
||||
return EnumSet.of(DecoderType.P25_PHASE1);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,17 @@
|
||||
/*******************************************************************************
|
||||
* 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/>
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.module.decode;
|
||||
|
||||
@@ -25,37 +23,28 @@ import java.util.EnumSet;
|
||||
*/
|
||||
public enum DecoderType
|
||||
{
|
||||
ACARS( "ACARS", "ACARS", "images/acars.png", 12500 ),
|
||||
AIS( "AIS", "AIS", "images/ais.png", 12500 ),
|
||||
AM( "AM", "AM", "images/am.png", 10000 ),
|
||||
APRS( "APRS", "APRS", "images/aprs.png", 12500 ),
|
||||
DMR( "DMR", "DMR", "images/dmr.png", 12500 ),
|
||||
FLEETSYNC2( "Fleetsync II", "Fleetsync2", "images/fm.png", 12500 ),
|
||||
LJ_1200( "LJ1200 173.075", "LJ1200", "images/lj1200.png", 12500 ),
|
||||
LTR_STANDARD( "LTR-Standard", "LTR", "images/ltr.png", 10000 ),
|
||||
LTR_NET( "LTR-Net", "LTR-Net", "images/ltr_net.png", 12500 ),
|
||||
MDC1200( "MDC1200", "MDC1200", "images/fm.png", 12500 ),
|
||||
MOTOROLA_TYPE_2( "MOTO T2", "MOTO T2", "images/motorola_type_2.png", 15000 ),
|
||||
MPT1327( "MPT1327", "MPT1327", "images/mpt1327.png", 12500 ),
|
||||
NXDN( "NXDN", "NXDN", "images/nxdn.png", 8000 ),
|
||||
NBFM( "NBFM", "NBFM", "images/fm.png", 12500 ),
|
||||
PASSPORT( "Passport", "Passport", "images/passport.png", 12500 ),
|
||||
P25_PHASE1( "P25 Phase I", "P25-1", "images/p25_1.png", 12500 ),
|
||||
P25_PHASE2( "P25 Phase II", "P25-2", "images/p25_2.png", 15000 ),
|
||||
TAIT_1200( "Tait 1200", "Tait 1200", "images/tait.png", 12500 );
|
||||
|
||||
//Primary Decoders
|
||||
AM("AM", "AM"),
|
||||
LTR_STANDARD("LTR-Standard", "LTR"),
|
||||
LTR_NET("LTR-Net", "LTR-Net"),
|
||||
MPT1327("MPT1327", "MPT1327"),
|
||||
NBFM("NBFM", "NBFM"),
|
||||
PASSPORT("Passport", "Passport"),
|
||||
P25_PHASE1("P25 Phase I", "P25-1"),
|
||||
|
||||
//Auxiliary Decoders
|
||||
FLEETSYNC2("Fleetsync II", "Fleetsync2"),
|
||||
LJ_1200("LJ1200 173.075", "LJ1200"),
|
||||
MDC1200("MDC1200", "MDC1200"),
|
||||
TAIT_1200("Tait 1200", "Tait 1200");
|
||||
|
||||
private String mDisplayString;
|
||||
private String mShortDisplayString;
|
||||
private String mIconFilename;
|
||||
private int mChannelBandwidth;
|
||||
|
||||
DecoderType( String displayString, String shortDisplayString,
|
||||
String iconFilename, int bandwidth )
|
||||
|
||||
DecoderType(String displayString, String shortDisplayString)
|
||||
{
|
||||
mDisplayString = displayString;
|
||||
mShortDisplayString = shortDisplayString;
|
||||
mIconFilename = iconFilename;
|
||||
mChannelBandwidth = bandwidth;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,68 +52,43 @@ public enum DecoderType
|
||||
*/
|
||||
public static EnumSet<DecoderType> getPrimaryDecoders()
|
||||
{
|
||||
return EnumSet.of( DecoderType.AM,
|
||||
DecoderType.LTR_NET,
|
||||
DecoderType.LTR_STANDARD,
|
||||
DecoderType.MPT1327,
|
||||
DecoderType.NBFM,
|
||||
DecoderType.P25_PHASE1,
|
||||
DecoderType.PASSPORT );
|
||||
return EnumSet.of(DecoderType.AM,
|
||||
DecoderType.LTR_NET,
|
||||
DecoderType.LTR_STANDARD,
|
||||
DecoderType.MPT1327,
|
||||
DecoderType.NBFM,
|
||||
DecoderType.P25_PHASE1,
|
||||
DecoderType.PASSPORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoders that can be used in the viewer application
|
||||
*/
|
||||
public static EnumSet<DecoderType> getInstrumentableDecoders()
|
||||
{
|
||||
return EnumSet.of( DecoderType.FLEETSYNC2,
|
||||
DecoderType.LJ_1200,
|
||||
DecoderType.LTR_NET,
|
||||
DecoderType.MDC1200,
|
||||
DecoderType.MPT1327,
|
||||
DecoderType.PASSPORT,
|
||||
DecoderType.P25_PHASE1,
|
||||
DecoderType.TAIT_1200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Available auxiliary decoders.
|
||||
*/
|
||||
public static ArrayList<DecoderType> getAuxDecoders()
|
||||
{
|
||||
ArrayList<DecoderType> decoders = new ArrayList<DecoderType>();
|
||||
|
||||
decoders.add( DecoderType.FLEETSYNC2 );
|
||||
decoders.add( DecoderType.LJ_1200 );
|
||||
decoders.add( DecoderType.MDC1200 );
|
||||
decoders.add( DecoderType.TAIT_1200 );
|
||||
|
||||
return decoders;
|
||||
ArrayList<DecoderType> decoders = new ArrayList<DecoderType>();
|
||||
|
||||
decoders.add(DecoderType.FLEETSYNC2);
|
||||
decoders.add(DecoderType.LJ_1200);
|
||||
decoders.add(DecoderType.MDC1200);
|
||||
decoders.add(DecoderType.TAIT_1200);
|
||||
|
||||
return decoders;
|
||||
}
|
||||
|
||||
|
||||
public String getDisplayString()
|
||||
{
|
||||
return mDisplayString;
|
||||
}
|
||||
|
||||
|
||||
public String getShortDisplayString()
|
||||
{
|
||||
return mShortDisplayString;
|
||||
return mShortDisplayString;
|
||||
}
|
||||
|
||||
public String getIconFilename()
|
||||
{
|
||||
return mIconFilename;
|
||||
}
|
||||
|
||||
public int getChannelBandwidth()
|
||||
{
|
||||
return mChannelBandwidth;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mDisplayString;
|
||||
return mDisplayString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import io.github.dsheirer.dsp.afsk.AFSK1200Decoder;
|
||||
import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
/**
|
||||
* Abstract class for Audio Frequency Shift Keying (AFSK) 1200-baud decoder based decoder modules.
|
||||
@@ -28,7 +28,7 @@ import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
*
|
||||
* Sub-class implementations should invoke getDecoder().setListener() to receive decoded symbol stream.
|
||||
*/
|
||||
public abstract class AbstractAFSKDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableBuffer>
|
||||
public abstract class AbstractAFSKDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableFloatBuffer>
|
||||
{
|
||||
private AFSK1200Decoder mAFSK1200Decoder;
|
||||
|
||||
@@ -48,13 +48,13 @@ public abstract class AbstractAFSKDecoder extends Decoder implements IReusableBu
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
mAFSK1200Decoder.receive(reusableBuffer);
|
||||
mAFSK1200Decoder.receive(reusableFloatBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableBuffer> getReusableBufferListener()
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.module.decode.am;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigAM extends DecodeConfiguration
|
||||
{
|
||||
@@ -27,6 +29,17 @@ public class DecodeConfigAM extends DecodeConfiguration
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Source channel specification for this decoder
|
||||
*/
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public ChannelSpecification getChannelSpecification()
|
||||
{
|
||||
return new ChannelSpecification(25000.0,
|
||||
3000, 3000.0, 5000.0);
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
public DecoderType getDecoderType()
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ import io.github.dsheirer.module.decode.mpt1327.DecodeConfigMPT1327;
|
||||
import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM;
|
||||
import io.github.dsheirer.module.decode.p25.DecodeConfigP25Phase1;
|
||||
import io.github.dsheirer.module.decode.passport.DecodeConfigPassport;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||
@@ -58,4 +59,7 @@ public abstract class DecodeConfiguration extends Configuration
|
||||
|
||||
@JsonIgnore
|
||||
public abstract DecoderType getDecoderType();
|
||||
|
||||
@JsonIgnore
|
||||
public abstract ChannelSpecification getChannelSpecification();
|
||||
}
|
||||
|
||||
@@ -30,10 +30,17 @@ import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
@@ -86,64 +93,6 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
|
||||
});
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void channelChanged(ChannelEvent event)
|
||||
// {
|
||||
// boolean changed = false;
|
||||
//
|
||||
// if(event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE &&
|
||||
// event.getChannel().isSelected())
|
||||
// {
|
||||
// if(mDisplayedChannel == null || (mDisplayedChannel != null && mDisplayedChannel != event.getChannel()))
|
||||
// {
|
||||
// mDisplayedChannel = event.getChannel();
|
||||
//
|
||||
// ProcessingChain chain = mChannelProcessingManager.getProcessingChain(mDisplayedChannel);
|
||||
//
|
||||
// if(chain != null)
|
||||
// {
|
||||
// mDisplayedModel = chain.getMessageActivityModel();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// mDisplayedModel = EMPTY_MODEL;
|
||||
// }
|
||||
//
|
||||
// mMessageTable.setModel(mDisplayedModel);
|
||||
//
|
||||
// if(mDisplayedChannel != null)
|
||||
// {
|
||||
// mManagementPanel.enableButtons();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// mManagementPanel.disableButtons();
|
||||
// }
|
||||
//
|
||||
// changed = true;
|
||||
// }
|
||||
// }
|
||||
// else if(event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || event.getEvent() == Event.REQUEST_DISABLE)
|
||||
// {
|
||||
// if(mDisplayedChannel != null && mDisplayedChannel == event.getChannel())
|
||||
// {
|
||||
// mDisplayedChannel = null;
|
||||
//
|
||||
// mMessageTable.setModel(EMPTY_MODEL);
|
||||
//
|
||||
// mManagementPanel.disableButtons();
|
||||
//
|
||||
// changed = true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(changed)
|
||||
// {
|
||||
// revalidate();
|
||||
// repaint();
|
||||
// }
|
||||
// }
|
||||
|
||||
public class MessageManagementPanel extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -17,7 +17,7 @@ package io.github.dsheirer.module.decode.fleetsync2;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -44,9 +44,9 @@ public class Fleetsync2DecoderInstrumented extends Fleetsync2Decoder implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
super.receive(reusableBuffer);
|
||||
super.receive(reusableFloatBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package io.github.dsheirer.module.decode.lj1200;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -44,9 +44,9 @@ public class LJ1200DecoderInstrumented extends LJ1200Decoder implements IInstrum
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
super.receive(reusableBuffer);
|
||||
super.receive(reusableFloatBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.module.decode.ltrnet;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.message.MessageDirection;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigLTRNet extends DecodeConfiguration
|
||||
{
|
||||
@@ -46,4 +48,15 @@ public class DecodeConfigLTRNet extends DecodeConfiguration
|
||||
{
|
||||
mMessageDirection = direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source channel specification for this decoder
|
||||
*/
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public ChannelSpecification getChannelSpecification()
|
||||
{
|
||||
return new ChannelSpecification(25000.0,
|
||||
12500, 6000.0, 7000.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
public class LTRNetDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableBuffer>
|
||||
public class LTRNetDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableFloatBuffer>
|
||||
{
|
||||
public static final int LTR_NET_MESSAGE_LENGTH = 40;
|
||||
protected LTRDecoder mLTRDecoder;
|
||||
@@ -73,15 +73,15 @@ public class LTRNetDecoder extends Decoder implements IReusableBufferListener, L
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableBuffer> getReusableBufferListener()
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
{
|
||||
return mLTRDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
mLTRDecoder.receive(reusableBuffer);
|
||||
mLTRDecoder.receive(reusableFloatBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,7 +17,7 @@ package io.github.dsheirer.module.decode.ltrnet;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.dsp.fsk.LTRDecoderInstrumented;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
public class LTRNetDecoderInstrumented extends LTRNetDecoder
|
||||
@@ -35,9 +35,9 @@ public class LTRNetDecoderInstrumented extends LTRNetDecoder
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
super.receive(reusableBuffer);
|
||||
super.receive(reusableFloatBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.module.decode.ltrstandard;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.message.MessageDirection;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigLTRStandard extends DecodeConfiguration
|
||||
{
|
||||
@@ -47,4 +49,15 @@ public class DecodeConfigLTRStandard extends DecodeConfiguration
|
||||
{
|
||||
mMessageDirection = direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source channel specification for this decoder
|
||||
*/
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public ChannelSpecification getChannelSpecification()
|
||||
{
|
||||
return new ChannelSpecification(25000.0,
|
||||
12500, 6000.0, 7000.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
public class LTRStandardDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableBuffer>
|
||||
public class LTRStandardDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableFloatBuffer>
|
||||
{
|
||||
public static final int LTR_STANDARD_MESSAGE_LENGTH = 40;
|
||||
|
||||
@@ -65,15 +65,15 @@ public class LTRStandardDecoder extends Decoder implements IReusableBufferListen
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableBuffer> getReusableBufferListener()
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
{
|
||||
return mLTRDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
mLTRDecoder.receive(reusableBuffer);
|
||||
mLTRDecoder.receive(reusableFloatBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,7 +17,7 @@ package io.github.dsheirer.module.decode.mdc1200;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -44,9 +44,9 @@ public class MDCDecoderInstrumented extends MDCDecoder implements IInstrumentedA
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
super.receive(reusableBuffer);
|
||||
super.receive(reusableFloatBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.module.decode.mpt1327;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigMPT1327 extends DecodeConfiguration
|
||||
{
|
||||
@@ -102,4 +104,15 @@ public class DecodeConfigMPT1327 extends DecodeConfiguration
|
||||
{
|
||||
mTrafficChannelPoolSize = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source channel specification for this decoder
|
||||
*/
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public ChannelSpecification getChannelSpecification()
|
||||
{
|
||||
return new ChannelSpecification(25000.0,
|
||||
12500, 6000.0, 7000.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package io.github.dsheirer.module.decode.mpt1327;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -44,9 +44,9 @@ public class MPT1327DecoderInstrumented extends MPT1327Decoder implements IInstr
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
super.receive(reusableBuffer);
|
||||
super.receive(reusableFloatBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -17,12 +17,16 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.module.decode.nbfm;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigNBFM extends DecodeConfiguration
|
||||
{
|
||||
private Bandwidth mBandwidth = Bandwidth.BW_12_5;
|
||||
|
||||
public DecodeConfigNBFM()
|
||||
{
|
||||
}
|
||||
@@ -33,4 +37,53 @@ public class DecodeConfigNBFM extends DecodeConfiguration
|
||||
return DecoderType.NBFM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel bandwidth
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "bandwidth")
|
||||
public Bandwidth getBandwidth()
|
||||
{
|
||||
return mBandwidth;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public ChannelSpecification getChannelSpecification()
|
||||
{
|
||||
if(mBandwidth == Bandwidth.BW_12_5)
|
||||
{
|
||||
return new ChannelSpecification(25000.0, 12500, 6000.0, 7000.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ChannelSpecification(50000.0, 25000, 12500.0, 13500.0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel bandwidth
|
||||
*/
|
||||
public void setBandwidth(Bandwidth bandwidth)
|
||||
{
|
||||
mBandwidth = bandwidth;
|
||||
}
|
||||
|
||||
public enum Bandwidth
|
||||
{
|
||||
BW_12_5("12.5 kHz"),
|
||||
BW_25_0("25.0 kHz");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
Bandwidth(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 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/>
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.module.decode.nbfm;
|
||||
|
||||
@@ -24,70 +22,94 @@ import io.github.dsheirer.gui.editor.ValidatingEditor;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class NBFMDecoderEditor extends ValidatingEditor<Channel>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NBFMDecoderEditor()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "insets 0 0 0 0,wrap 2", "[right][grow,fill]", "" ) );
|
||||
add( new JLabel( "Narrow Band FM Decoder" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate( Editor<Channel> editor ) throws EditorValidationException
|
||||
{
|
||||
//No validation
|
||||
}
|
||||
private JComboBox<DecodeConfigNBFM.Bandwidth> mComboBandwidth;
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if( hasItem() && isModified() )
|
||||
{
|
||||
DecodeConfigNBFM nbfm = new DecodeConfigNBFM();
|
||||
getItem().setDecodeConfiguration( nbfm );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
public NBFMDecoderEditor()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
private void setControlsEnabled( boolean enabled )
|
||||
{
|
||||
}
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0,wrap 2", "[right][grow,fill]", ""));
|
||||
|
||||
@Override
|
||||
public void setItem( Channel item )
|
||||
{
|
||||
super.setItem( item );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
setControlsEnabled( true );
|
||||
|
||||
DecodeConfiguration config = getItem().getDecodeConfiguration();
|
||||
|
||||
if( config instanceof DecodeConfigNBFM )
|
||||
{
|
||||
DecodeConfigNBFM nbfm = (DecodeConfigNBFM)config;
|
||||
setModified( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
setModified( true );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setControlsEnabled( false );
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
mComboBandwidth = new JComboBox<>();
|
||||
mComboBandwidth.setModel(new DefaultComboBoxModel<DecodeConfigNBFM.Bandwidth>(
|
||||
DecodeConfigNBFM.Bandwidth.values()));
|
||||
mComboBandwidth.setEnabled(false);
|
||||
mComboBandwidth.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
add(new JLabel("Bandwidth:"));
|
||||
add(mComboBandwidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Editor<Channel> editor) throws EditorValidationException
|
||||
{
|
||||
//No validation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if(hasItem() && isModified())
|
||||
{
|
||||
DecodeConfigNBFM nbfm = new DecodeConfigNBFM();
|
||||
nbfm.setBandwidth((DecodeConfigNBFM.Bandwidth)mComboBandwidth.getSelectedItem());
|
||||
getItem().setDecodeConfiguration(nbfm);
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
private void setControlsEnabled(boolean enabled)
|
||||
{
|
||||
mComboBandwidth.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(Channel item)
|
||||
{
|
||||
super.setItem(item);
|
||||
|
||||
if(hasItem())
|
||||
{
|
||||
setControlsEnabled(true);
|
||||
|
||||
DecodeConfiguration config = getItem().getDecodeConfiguration();
|
||||
|
||||
if(config instanceof DecodeConfigNBFM)
|
||||
{
|
||||
DecodeConfigNBFM nbfm = (DecodeConfigNBFM)config;
|
||||
mComboBandwidth.setSelectedItem(nbfm.getBandwidth());
|
||||
setModified(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setControlsEnabled(false);
|
||||
setModified(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package io.github.dsheirer.module.decode.p25;
|
||||
|
||||
import io.github.dsheirer.dsp.gain.DirectGainControl;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.real.RealSampleListener;
|
||||
import io.github.dsheirer.sample.real.RealSampleProvider;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
|
||||
public class C4FMSymbolFilter implements Listener<ReusableBuffer>, ISourceEventListener, RealSampleProvider
|
||||
public class C4FMSymbolFilter implements Listener<ReusableFloatBuffer>, ISourceEventListener, RealSampleProvider
|
||||
{
|
||||
private static final float TAPS[][] =
|
||||
{
|
||||
@@ -215,7 +215,7 @@ public class C4FMSymbolFilter implements Listener<ReusableBuffer>, ISourceEventL
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer buffer)
|
||||
public void receive(ReusableFloatBuffer buffer)
|
||||
{
|
||||
for(float sample : buffer.getSamples())
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigP25Phase1 extends DecodeConfiguration
|
||||
{
|
||||
@@ -104,4 +105,14 @@ public class DecodeConfigP25Phase1 extends DecodeConfiguration
|
||||
{
|
||||
mTrafficChannelPoolSize = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source channel specification for this decoder
|
||||
*/
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public ChannelSpecification getChannelSpecification()
|
||||
{
|
||||
return new ChannelSpecification(50000.0, 12500, 5750.0, 6500.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,26 @@
|
||||
package io.github.dsheirer.module.decode.p25;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.dsp.symbol.Dibit;
|
||||
import io.github.dsheirer.dsp.symbol.DibitToByteBufferAssembler;
|
||||
import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
|
||||
public abstract class P25Decoder extends Decoder implements ISourceEventListener, ISourceEventProvider,
|
||||
IReusableComplexBufferListener, Listener<ReusableComplexBuffer>
|
||||
IReusableComplexBufferListener, Listener<ReusableComplexBuffer>, IReusableByteBufferProvider
|
||||
{
|
||||
private double mSampleRate;
|
||||
private Broadcaster<Dibit> mDibitBroadcaster = new Broadcaster<>();
|
||||
private DibitToByteBufferAssembler mByteBufferAssembler = new DibitToByteBufferAssembler(300);
|
||||
private P25MessageProcessor mMessageProcessor;
|
||||
private AliasList mAliasList;
|
||||
private Listener<SourceEvent> mSourceEventListener;
|
||||
@@ -40,6 +47,42 @@ public abstract class P25Decoder extends Decoder implements ISourceEventListener
|
||||
mAliasList = aliasList;
|
||||
mMessageProcessor = new P25MessageProcessor(mAliasList);
|
||||
mMessageProcessor.setMessageListener(getMessageListener());
|
||||
mDibitBroadcaster.addListener(mByteBufferAssembler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembler for packaging Dibit stream into reusable byte buffers.
|
||||
*/
|
||||
protected Broadcaster<Dibit> getDibitBroadcaster()
|
||||
{
|
||||
return mDibitBroadcaster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
{
|
||||
mByteBufferAssembler.setBufferListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public void removeBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
{
|
||||
mByteBufferAssembler.removeBufferListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public boolean hasBufferListeners()
|
||||
{
|
||||
return mByteBufferAssembler.hasBufferListeners();
|
||||
}
|
||||
|
||||
protected double getSymbolRate()
|
||||
|
||||
@@ -71,12 +71,20 @@ public class P25DecoderC4FM extends P25Decoder
|
||||
|
||||
mQPSKDemodulator = new DQPSKDecisionDirectedDemodulator(mCostasLoop, mInterpolatingSampleBuffer);
|
||||
|
||||
if(mMessageFramer != null)
|
||||
{
|
||||
getDibitBroadcaster().removeListener(mMessageFramer);
|
||||
mMessageFramer.dispose();
|
||||
}
|
||||
|
||||
//The Costas Loop receives symbol-inversion correction requests when detected.
|
||||
//The PLL gain monitor receives sync detect/loss signals from the message framer
|
||||
mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop, mPLLGainMonitor);
|
||||
mMessageFramer.setListener(getMessageProcessor());
|
||||
mMessageFramer.setSampleRate(sampleRate);
|
||||
mQPSKDemodulator.setSymbolListener(mMessageFramer);
|
||||
|
||||
mQPSKDemodulator.setSymbolListener(getDibitBroadcaster());
|
||||
getDibitBroadcaster().addListener(mMessageFramer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -78,7 +78,8 @@ public class P25DecoderC4FMInstrumented extends P25DecoderC4FM
|
||||
instrumented.setPLLFrequencyListener(mPLLFrequencyListener);
|
||||
instrumented.setSymbolDecisionDataListener(mSymbolDecisionDataListener);
|
||||
instrumented.setSamplesPerSymbolListener(mSamplesPerSymbolListener);
|
||||
instrumented.setSymbolListener(mMessageFramer);
|
||||
instrumented.setSymbolListener(getDibitBroadcaster());
|
||||
getDibitBroadcaster().addListener(mMessageFramer);
|
||||
}
|
||||
|
||||
public void setComplexSymbolListener(Listener<Complex> listener)
|
||||
|
||||
@@ -75,10 +75,17 @@ public class P25DecoderLSM extends P25Decoder
|
||||
|
||||
//The Costas Loop receives symbol-inversion correction requests when detected.
|
||||
//The PLL gain monitor receives sync detect/loss signals from the message framer
|
||||
if(mMessageFramer != null)
|
||||
{
|
||||
getDibitBroadcaster().removeListener(mMessageFramer);
|
||||
mMessageFramer.dispose();
|
||||
}
|
||||
|
||||
mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop, mPLLGainMonitor);
|
||||
mMessageFramer.setListener(getMessageProcessor());
|
||||
mMessageFramer.setSampleRate(sampleRate);
|
||||
mQPSKDemodulator.setSymbolListener(mMessageFramer);
|
||||
mQPSKDemodulator.setSymbolListener(getDibitBroadcaster());
|
||||
getDibitBroadcaster().addListener(mMessageFramer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,7 +79,8 @@ public class P25DecoderLSMInstrumented extends P25DecoderLSM
|
||||
instrumented.setPLLFrequencyListener(mPLLFrequencyListener);
|
||||
instrumented.setSymbolDecisionDataListener(mSymbolDecisionDataListener);
|
||||
instrumented.setSamplesPerSymbolListener(mSamplesPerSymbolListener);
|
||||
instrumented.setSymbolListener(mMessageFramer);
|
||||
instrumented.setSymbolListener(getDibitBroadcaster());
|
||||
getDibitBroadcaster().addListener(mMessageFramer);
|
||||
}
|
||||
|
||||
public void setComplexSymbolListener(Listener<Complex> listener)
|
||||
|
||||
@@ -254,6 +254,7 @@ public class P25DecoderState extends DecoderState
|
||||
if(mCurrentCallEvent != null)
|
||||
{
|
||||
mCurrentCallEvent.end();
|
||||
broadcast(mCurrentCallEvent);
|
||||
}
|
||||
|
||||
mCurrentCallEvent = null;
|
||||
@@ -395,6 +396,10 @@ public class P25DecoderState extends DecoderState
|
||||
*/
|
||||
private void processTDU(TDUMessage tdu)
|
||||
{
|
||||
if(mChannelType == ChannelType.STANDARD)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -432,19 +437,21 @@ public class P25DecoderState extends DecoderState
|
||||
*/
|
||||
private void processTDULC(TDULinkControlMessage tdulc)
|
||||
{
|
||||
if(mCurrentCallEvent != null)
|
||||
{
|
||||
mCurrentCallEvent.end();
|
||||
broadcast(mCurrentCallEvent);
|
||||
mCurrentCallEvent = null;
|
||||
}
|
||||
|
||||
if(tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.END, State.FADE));
|
||||
|
||||
if(mCurrentCallEvent != null)
|
||||
{
|
||||
mCurrentCallEvent.end();
|
||||
mCurrentCallEvent = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL));
|
||||
broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE));
|
||||
}
|
||||
|
||||
switch(tdulc.getOpcode())
|
||||
@@ -495,22 +502,17 @@ public class P25DecoderState extends DecoderState
|
||||
/* This opcode as handled at the beginning of the method */
|
||||
break;
|
||||
case CHANNEL_IDENTIFIER_UPDATE:
|
||||
//TODO: does the activity summary need this message?
|
||||
|
||||
/* This message is handled by the P25MessageProcessor and
|
||||
* inserted into any channels needing frequency band info */
|
||||
break;
|
||||
case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT:
|
||||
//TODO: does the activity summary need this message?
|
||||
|
||||
/* This message is handled by the P25MessageProcessor and
|
||||
* inserted into any channels needing frequency band info */
|
||||
break;
|
||||
case EXTENDED_FUNCTION_COMMAND:
|
||||
if(tdulc instanceof ExtendedFunctionCommand)
|
||||
{
|
||||
ExtendedFunctionCommand efc =
|
||||
(ExtendedFunctionCommand)tdulc;
|
||||
ExtendedFunctionCommand efc = (ExtendedFunctionCommand)tdulc;
|
||||
|
||||
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND)
|
||||
.aliasList(getAliasList())
|
||||
@@ -606,42 +608,25 @@ public class P25DecoderState extends DecoderState
|
||||
}
|
||||
break;
|
||||
case GROUP_VOICE_CHANNEL_USER:
|
||||
/* Used on a traffic channel to reflect current call entities */
|
||||
//Used on a traffic channel to reflect that the channel is currently being used by the
|
||||
//TO talkgroup, but that channel is currently quiet (ie no voice) */
|
||||
if(tdulc instanceof GroupVoiceChannelUser)
|
||||
{
|
||||
GroupVoiceChannelUser gvcuser =
|
||||
(GroupVoiceChannelUser)tdulc;
|
||||
GroupVoiceChannelUser gvcuser = (GroupVoiceChannelUser)tdulc;
|
||||
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL));
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE));
|
||||
|
||||
//The from value should always be "000000" indicating that nobody is currently talking
|
||||
mFromTalkgroupMonitor.reset();
|
||||
|
||||
String from = gvcuser.getSourceAddress();
|
||||
String to = gvcuser.getGroupAddress();
|
||||
|
||||
mFromTalkgroupMonitor.process(from);
|
||||
mToTalkgroupMonitor.process(to);
|
||||
|
||||
if(mCurrentCallEvent == null)
|
||||
{
|
||||
mCurrentCallEvent = new P25CallEvent.Builder(CallEvent.CallEventType.GROUP_CALL)
|
||||
.aliasList(getAliasList())
|
||||
.channel(mCurrentChannel)
|
||||
.details("TDULC GROUP VOICE CHANNEL USER" +
|
||||
(gvcuser.isEncrypted() ? "ENCRYPTED " : "") +
|
||||
(gvcuser.isEmergency() ? "EMERGENCY " : ""))
|
||||
.frequency(mCurrentChannelFrequency)
|
||||
.from(from)
|
||||
.to(to)
|
||||
.build();
|
||||
|
||||
broadcast(mCurrentCallEvent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MESSAGE_UPDATE:
|
||||
if(tdulc instanceof MessageUpdate)
|
||||
{
|
||||
MessageUpdate mu =
|
||||
(MessageUpdate)tdulc;
|
||||
MessageUpdate mu = (MessageUpdate)tdulc;
|
||||
|
||||
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.SDM)
|
||||
.aliasList(getAliasList())
|
||||
@@ -748,8 +733,7 @@ public class P25DecoderState extends DecoderState
|
||||
io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast sccb =
|
||||
(io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast)tdulc;
|
||||
|
||||
String site = sccb.getRFSubsystemID() + "-" +
|
||||
sccb.getSiteID();
|
||||
String site = sccb.getRFSubsystemID() + "-" + sccb.getSiteID();
|
||||
|
||||
mSiteAttributeMonitor.process(site);
|
||||
}
|
||||
@@ -761,11 +745,9 @@ public class P25DecoderState extends DecoderState
|
||||
case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT:
|
||||
if(tdulc instanceof SecondaryControlChannelBroadcastExplicit)
|
||||
{
|
||||
SecondaryControlChannelBroadcastExplicit sccb =
|
||||
(SecondaryControlChannelBroadcastExplicit)tdulc;
|
||||
SecondaryControlChannelBroadcastExplicit sccb = (SecondaryControlChannelBroadcastExplicit)tdulc;
|
||||
|
||||
String site = sccb.getRFSubsystemID() + "-" +
|
||||
sccb.getSiteID();
|
||||
String site = sccb.getRFSubsystemID() + "-" + sccb.getSiteID();
|
||||
|
||||
mSiteAttributeMonitor.process(site);
|
||||
}
|
||||
@@ -777,8 +759,7 @@ public class P25DecoderState extends DecoderState
|
||||
case STATUS_QUERY:
|
||||
if(tdulc instanceof StatusQuery)
|
||||
{
|
||||
StatusQuery sq =
|
||||
(StatusQuery)tdulc;
|
||||
StatusQuery sq = (StatusQuery)tdulc;
|
||||
|
||||
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY)
|
||||
.aliasList(getAliasList())
|
||||
@@ -816,8 +797,7 @@ public class P25DecoderState extends DecoderState
|
||||
case TELEPHONE_INTERCONNECT_ANSWER_REQUEST:
|
||||
if(tdulc instanceof TelephoneInterconnectAnswerRequest)
|
||||
{
|
||||
TelephoneInterconnectAnswerRequest tiar =
|
||||
(TelephoneInterconnectAnswerRequest)tdulc;
|
||||
TelephoneInterconnectAnswerRequest tiar = (TelephoneInterconnectAnswerRequest)tdulc;
|
||||
|
||||
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE)
|
||||
.aliasList(getAliasList())
|
||||
@@ -835,8 +815,7 @@ public class P25DecoderState extends DecoderState
|
||||
if(mChannelType == ChannelType.STANDARD &&
|
||||
tdulc instanceof TelephoneInterconnectVoiceChannelUser)
|
||||
{
|
||||
TelephoneInterconnectVoiceChannelUser tivcu =
|
||||
(TelephoneInterconnectVoiceChannelUser)tdulc;
|
||||
TelephoneInterconnectVoiceChannelUser tivcu = (TelephoneInterconnectVoiceChannelUser)tdulc;
|
||||
|
||||
String to = tivcu.getAddress();
|
||||
|
||||
@@ -860,8 +839,7 @@ public class P25DecoderState extends DecoderState
|
||||
case UNIT_AUTHENTICATION_COMMAND:
|
||||
if(tdulc instanceof UnitAuthenticationCommand)
|
||||
{
|
||||
UnitAuthenticationCommand uac =
|
||||
(UnitAuthenticationCommand)tdulc;
|
||||
UnitAuthenticationCommand uac = (UnitAuthenticationCommand)tdulc;
|
||||
|
||||
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND)
|
||||
.aliasList(getAliasList())
|
||||
@@ -877,8 +855,7 @@ public class P25DecoderState extends DecoderState
|
||||
case UNIT_REGISTRATION_COMMAND:
|
||||
if(tdulc instanceof UnitRegistrationCommand)
|
||||
{
|
||||
UnitRegistrationCommand urc =
|
||||
(UnitRegistrationCommand)tdulc;
|
||||
UnitRegistrationCommand urc = (UnitRegistrationCommand)tdulc;
|
||||
|
||||
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND)
|
||||
.aliasList(getAliasList())
|
||||
@@ -894,8 +871,7 @@ public class P25DecoderState extends DecoderState
|
||||
case UNIT_TO_UNIT_ANSWER_REQUEST:
|
||||
if(tdulc instanceof UnitToUnitAnswerRequest)
|
||||
{
|
||||
UnitToUnitAnswerRequest uuar =
|
||||
(UnitToUnitAnswerRequest)tdulc;
|
||||
UnitToUnitAnswerRequest uuar = (UnitToUnitAnswerRequest)tdulc;
|
||||
|
||||
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE)
|
||||
.aliasList(getAliasList())
|
||||
@@ -913,27 +889,12 @@ public class P25DecoderState extends DecoderState
|
||||
/* Used on traffic channels to indicate the current call entities */
|
||||
if(tdulc instanceof UnitToUnitVoiceChannelUser)
|
||||
{
|
||||
UnitToUnitVoiceChannelUser uuvcu =
|
||||
(UnitToUnitVoiceChannelUser)tdulc;
|
||||
UnitToUnitVoiceChannelUser uuvcu = (UnitToUnitVoiceChannelUser)tdulc;
|
||||
|
||||
String from = uuvcu.getSourceAddress();
|
||||
mFromTalkgroupMonitor.process(from);
|
||||
mFromTalkgroupMonitor.reset();
|
||||
|
||||
String to = uuvcu.getTargetAddress();
|
||||
mToTalkgroupMonitor.process(to);
|
||||
|
||||
if(mCurrentCallEvent != null)
|
||||
{
|
||||
mCurrentCallEvent = new P25CallEvent.Builder(CallEvent.CallEventType.UNIT_TO_UNIT_CALL)
|
||||
.aliasList(getAliasList())
|
||||
.channel(mCurrentChannel)
|
||||
.details((uuvcu.isEncrypted() ? "ENCRYPTED " : "") +
|
||||
(uuvcu.isEmergency() ? "EMERGENCY " : ""))
|
||||
.frequency(mCurrentChannelFrequency)
|
||||
.from(from)
|
||||
.to(to)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -1166,9 +1127,17 @@ public class P25DecoderState extends DecoderState
|
||||
(io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu;
|
||||
|
||||
mFromTalkgroupMonitor.process(gvcuser.getSourceAddress());
|
||||
mCurrentCallEvent.setFromID(mFromTalkgroupMonitor.getValue());
|
||||
|
||||
mToTalkgroupMonitor.process(gvcuser.getGroupAddress());
|
||||
|
||||
String to = mToTalkgroupMonitor.getValue();
|
||||
|
||||
if(mCurrentCallEvent.getToID() == null || !mCurrentCallEvent.getToID().contentEquals(to))
|
||||
{
|
||||
mCurrentCallEvent.setToID(to);
|
||||
}
|
||||
|
||||
if(mChannelType == ChannelType.STANDARD)
|
||||
{
|
||||
if(mCurrentCallEvent.getCallEventType() != CallEvent.CallEventType.GROUP_CALL)
|
||||
@@ -1631,14 +1600,13 @@ public class P25DecoderState extends DecoderState
|
||||
*/
|
||||
private void processPDU(PDUMessage pdu)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
|
||||
|
||||
if(pdu instanceof PacketData || pdu instanceof PDUTypeUnknown)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
|
||||
}
|
||||
else if(pdu instanceof PDUConfirmedMessage)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
|
||||
|
||||
PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu;
|
||||
|
||||
switch(pduc.getPDUType())
|
||||
@@ -1723,6 +1691,11 @@ public class P25DecoderState extends DecoderState
|
||||
sbTo.append(sud.getUDPDestinationPort());
|
||||
}
|
||||
|
||||
mFromTalkgroupMonitor.reset();
|
||||
mFromTalkgroupMonitor.process(sbFrom.toString());
|
||||
mToTalkgroupMonitor.reset();
|
||||
mToTalkgroupMonitor.process(pduc.getLogicalLinkID());
|
||||
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
|
||||
|
||||
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL)
|
||||
@@ -1750,9 +1723,6 @@ public class P25DecoderState extends DecoderState
|
||||
}
|
||||
else
|
||||
{
|
||||
/* These are alternate trunking control blocks in PDU format */
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
|
||||
|
||||
switch(pdu.getOpcode())
|
||||
{
|
||||
case GROUP_DATA_CHANNEL_GRANT:
|
||||
|
||||
@@ -35,9 +35,6 @@ import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class P25MessageFramer implements Listener<Dibit>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(P25MessageFramer.class);
|
||||
@@ -77,7 +74,7 @@ public class P25MessageFramer implements Listener<Dibit>
|
||||
private PLLPhaseInversionDetector mInversionDetector180;
|
||||
|
||||
private MultiSyncPatternMatcher mMatcher;
|
||||
private List<P25MessageAssembler> mAssemblers = new ArrayList<P25MessageAssembler>();
|
||||
private P25MessageAssembler mMessageAssembler = new P25MessageAssembler();
|
||||
|
||||
private Listener<Message> mListener;
|
||||
private AliasList mAliasList;
|
||||
@@ -104,13 +101,9 @@ public class P25MessageFramer implements Listener<Dibit>
|
||||
@Override
|
||||
public void syncDetected()
|
||||
{
|
||||
for(P25MessageAssembler assembler : mAssemblers)
|
||||
if(!mMessageAssembler.isActive())
|
||||
{
|
||||
if(!assembler.isActive())
|
||||
{
|
||||
assembler.setActive(true);
|
||||
break;
|
||||
}
|
||||
mMessageAssembler.setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,17 +115,6 @@ public class P25MessageFramer implements Listener<Dibit>
|
||||
});
|
||||
|
||||
mMatcher.add(mPrimarySyncDetector);
|
||||
|
||||
/**
|
||||
* We use two message assemblers to catch any sync detections, so that
|
||||
* if we have a false trigger then we still have a chance to catch the
|
||||
* valid message. If we have two false triggers before the arrival of
|
||||
* the actual message sync, then the third or subsequent real or false
|
||||
* sync pattern will produce a debug message indicating no assemblers
|
||||
* are available..
|
||||
*/
|
||||
mAssemblers.add(new P25MessageAssembler());
|
||||
mAssemblers.add(new P25MessageAssembler());
|
||||
}
|
||||
|
||||
public P25MessageFramer(AliasList aliasList, IPhaseLockedLoop phaseLockedLoop, ISyncDetectListener syncDetectListener)
|
||||
@@ -172,12 +154,7 @@ public class P25MessageFramer implements Listener<Dibit>
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
for(P25MessageAssembler a : mAssemblers)
|
||||
{
|
||||
a.dispose();
|
||||
}
|
||||
|
||||
mAssemblers.clear();
|
||||
mMessageAssembler.dispose();
|
||||
|
||||
mListener = null;
|
||||
mAliasList = null;
|
||||
@@ -201,16 +178,13 @@ public class P25MessageFramer implements Listener<Dibit>
|
||||
@Override
|
||||
public void receive(Dibit symbol)
|
||||
{
|
||||
for(P25MessageAssembler assembler : mAssemblers)
|
||||
if(mMessageAssembler.isActive())
|
||||
{
|
||||
if(assembler.isActive())
|
||||
{
|
||||
assembler.receive(symbol);
|
||||
mMessageAssembler.receive(symbol);
|
||||
|
||||
if(assembler.complete())
|
||||
{
|
||||
assembler.reset();
|
||||
}
|
||||
if(mMessageAssembler.complete())
|
||||
{
|
||||
mMessageAssembler.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,13 +716,9 @@ public class P25MessageFramer implements Listener<Dibit>
|
||||
mPhaseLockedLoop.correctInversion(mPllCorrection);
|
||||
|
||||
/* Since we detected a sync pattern, start a message assembler */
|
||||
for(P25MessageAssembler assembler : mAssemblers)
|
||||
if(!mMessageAssembler.isActive())
|
||||
{
|
||||
if(!assembler.isActive())
|
||||
{
|
||||
assembler.setActive(true);
|
||||
break;
|
||||
}
|
||||
mMessageAssembler.setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.module.decode.p25;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
@@ -13,8 +28,7 @@ import java.util.HashMap;
|
||||
|
||||
public class P25MessageProcessor implements Listener<Message>
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( P25MessageProcessor.class );
|
||||
private final static Logger mLog = LoggerFactory.getLogger( P25MessageProcessor.class );
|
||||
|
||||
private Listener<Message> mMessageListener;
|
||||
|
||||
|
||||
@@ -350,9 +350,13 @@ public class Trellis_3_4_Rate
|
||||
public void add( Con con )
|
||||
{
|
||||
Node current = getLastNode();
|
||||
|
||||
Tribit input = INPUT_FROM_CONSTELLATION_MAP
|
||||
.get( con )[ current.getState().getValue() ];
|
||||
|
||||
Tribit input = null;
|
||||
|
||||
if(current != null)
|
||||
{
|
||||
input = INPUT_FROM_CONSTELLATION_MAP.get( con )[ current.getState().getValue() ];
|
||||
}
|
||||
|
||||
/* A non-null input tribit from the lookup table is valid, otherwise
|
||||
* create 8 alternate branches from the original path to explore
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* *********************************************************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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
|
||||
@@ -12,13 +11,15 @@
|
||||
*
|
||||
* 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.module.decode.passport;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigPassport extends DecodeConfiguration
|
||||
{
|
||||
@@ -31,4 +32,14 @@ public class DecodeConfigPassport extends DecodeConfiguration
|
||||
{
|
||||
return DecoderType.PASSPORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source channel specification for this decoder
|
||||
*/
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public ChannelSpecification getChannelSpecification()
|
||||
{
|
||||
return new ChannelSpecification(25000.0, 12500, 6000.0, 7000.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
|
||||
public class PassportDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableBuffer>
|
||||
public class PassportDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableFloatBuffer>
|
||||
{
|
||||
public static final int PASSPORT_MESSAGE_LENGTH = 68;
|
||||
|
||||
@@ -52,15 +52,15 @@ public class PassportDecoder extends Decoder implements IReusableBufferListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableBuffer> getReusableBufferListener()
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
{
|
||||
return mLTRDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
mLTRDecoder.receive(reusableBuffer);
|
||||
mLTRDecoder.receive(reusableFloatBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,7 +17,7 @@ package io.github.dsheirer.module.decode.tait;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -44,9 +44,9 @@ public class Tait1200DecoderInstrumented extends Tait1200Decoder implements IIns
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
super.receive(reusableBuffer);
|
||||
super.receive(reusableFloatBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
@@ -55,7 +55,7 @@ public class AMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
private double mChannelBandwidth;
|
||||
private double mOutputSampleRate;
|
||||
private RealResampler mResampler;
|
||||
private Listener<ReusableBuffer> mResampledReusableBufferListener;
|
||||
private Listener<ReusableFloatBuffer> mResampledReusableBufferListener;
|
||||
private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
|
||||
|
||||
/**
|
||||
@@ -109,7 +109,7 @@ public class AMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
public void receive(ReusableComplexBuffer basebandBuffer)
|
||||
{
|
||||
ReusableComplexBuffer filteredBuffer = mIQFilter.filter(basebandBuffer);
|
||||
ReusableBuffer demodulated = mDemodulator.demodulate(filteredBuffer);
|
||||
ReusableFloatBuffer demodulated = mDemodulator.demodulate(filteredBuffer);
|
||||
mResampler.resample(demodulated);
|
||||
}
|
||||
|
||||
@@ -117,12 +117,12 @@ public class AMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
* Processes resampled audio buffers produced by the resampler.
|
||||
* @param resampledAudio buffer to process.
|
||||
*/
|
||||
private void processResampledAudio(ReusableBuffer resampledAudio)
|
||||
private void processResampledAudio(ReusableFloatBuffer resampledAudio)
|
||||
{
|
||||
if(mResampledReusableBufferListener != null)
|
||||
{
|
||||
ReusableBuffer filteredAudio = mLowPassFilter.filter(resampledAudio);
|
||||
ReusableBuffer gainApplied = mAGC.process(filteredAudio);
|
||||
ReusableFloatBuffer filteredAudio = mLowPassFilter.filter(resampledAudio);
|
||||
ReusableFloatBuffer gainApplied = mAGC.process(filteredAudio);
|
||||
mResampledReusableBufferListener.receive(gainApplied);
|
||||
}
|
||||
else
|
||||
@@ -172,7 +172,7 @@ public class AMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableBuffer> listener)
|
||||
public void setBufferListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mResampledReusableBufferListener = listener;
|
||||
}
|
||||
@@ -253,10 +253,10 @@ public class AMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
|
||||
mResampler = new RealResampler(sampleRate, mOutputSampleRate, 2000, 1000);
|
||||
|
||||
mResampler.setListener(new Listener<ReusableBuffer>()
|
||||
mResampler.setListener(new Listener<ReusableFloatBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(ReusableBuffer resampledAudioBuffer)
|
||||
public void receive(ReusableFloatBuffer resampledAudioBuffer)
|
||||
{
|
||||
processResampledAudio(resampledAudioBuffer);
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
@@ -47,7 +47,7 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
private FMDemodulator mDemodulator = new FMDemodulator();
|
||||
private RealResampler mResampler;
|
||||
private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
|
||||
private Listener<ReusableBuffer> mResampledReusableBufferListener;
|
||||
private Listener<ReusableFloatBuffer> mResampledReusableBufferListener;
|
||||
private double mChannelBandwidth;
|
||||
private double mOutputSampleRate;
|
||||
|
||||
@@ -106,7 +106,7 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableBuffer> listener)
|
||||
public void setBufferListener(Listener<ReusableFloatBuffer> listener)
|
||||
{
|
||||
mResampledReusableBufferListener = listener;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
}
|
||||
|
||||
ReusableComplexBuffer basebandFilteredBuffer = mIQFilter.filter(reusableComplexBuffer);
|
||||
ReusableBuffer demodulatedBuffer = mDemodulator.demodulate(basebandFilteredBuffer);
|
||||
ReusableFloatBuffer demodulatedBuffer = mDemodulator.demodulate(basebandFilteredBuffer);
|
||||
|
||||
if(mResampler != null)
|
||||
{
|
||||
@@ -204,18 +204,18 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
|
||||
mResampler = new RealResampler(sampleRate, mOutputSampleRate, 2000, 1000);
|
||||
|
||||
mResampler.setListener(new Listener<ReusableBuffer>()
|
||||
mResampler.setListener(new Listener<ReusableFloatBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(ReusableBuffer reusableBuffer)
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
{
|
||||
if(mResampledReusableBufferListener != null)
|
||||
{
|
||||
mResampledReusableBufferListener.receive(reusableBuffer);
|
||||
mResampledReusableBufferListener.receive(reusableFloatBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
reusableBuffer.decrementUserCount();
|
||||
reusableFloatBuffer.decrementUserCount();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 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/>
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.record;
|
||||
|
||||
@@ -22,7 +20,7 @@ import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.record.config.RecordConfiguration;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.JCheckBox;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
@@ -30,121 +28,168 @@ import java.util.List;
|
||||
public class RecordConfigurationEditor extends Editor<Channel>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
private JCheckBox mAudioRecorder;
|
||||
private JCheckBox mBasebandRecorder;
|
||||
private JCheckBox mTrafficBasebandRecorder;
|
||||
private JCheckBox mBitstreamRecorder;
|
||||
private JCheckBox mTrafficBitstreamRecorder;
|
||||
|
||||
public RecordConfigurationEditor()
|
||||
{
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) );
|
||||
|
||||
mAudioRecorder = new JCheckBox( "Audio" );
|
||||
mAudioRecorder.setEnabled( false );
|
||||
mAudioRecorder.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
add( mAudioRecorder );
|
||||
setLayout(new MigLayout("fill,wrap 2", "", "[][][][grow]"));
|
||||
|
||||
mBasebandRecorder = new JCheckBox( "Baseband I/Q" );
|
||||
mBasebandRecorder.setEnabled( false );
|
||||
mBasebandRecorder.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
add( mBasebandRecorder );
|
||||
mAudioRecorder = new JCheckBox("Audio");
|
||||
mAudioRecorder.setEnabled(false);
|
||||
mAudioRecorder.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
add(mAudioRecorder, "wrap");
|
||||
|
||||
mTrafficBasebandRecorder = new JCheckBox( "Traffic Channel Baseband I/Q" );
|
||||
mTrafficBasebandRecorder.setEnabled( false );
|
||||
mTrafficBasebandRecorder.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
add( mTrafficBasebandRecorder );
|
||||
mBasebandRecorder = new JCheckBox("Baseband I/Q");
|
||||
mBasebandRecorder.setEnabled(false);
|
||||
mBasebandRecorder.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
add(mBasebandRecorder);
|
||||
|
||||
mTrafficBasebandRecorder = new JCheckBox("Traffic Channel Baseband I/Q");
|
||||
mTrafficBasebandRecorder.setEnabled(false);
|
||||
mTrafficBasebandRecorder.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
add(mTrafficBasebandRecorder);
|
||||
|
||||
mBitstreamRecorder = new JCheckBox("Demodulated Bitstream");
|
||||
mBitstreamRecorder.setEnabled(false);
|
||||
mBitstreamRecorder.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
add(mBitstreamRecorder);
|
||||
|
||||
mTrafficBitstreamRecorder = new JCheckBox("Traffic Channel Bitstream");
|
||||
mTrafficBitstreamRecorder.setEnabled(false);
|
||||
mTrafficBitstreamRecorder.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
add(mTrafficBitstreamRecorder);
|
||||
}
|
||||
|
||||
private void setControlsEnabled( boolean enabled )
|
||||
|
||||
private void setControlsEnabled(boolean enabled)
|
||||
{
|
||||
if( mAudioRecorder.isEnabled() != enabled )
|
||||
{
|
||||
mAudioRecorder.setEnabled( enabled );
|
||||
}
|
||||
|
||||
if( mBasebandRecorder.isEnabled() != enabled )
|
||||
{
|
||||
mBasebandRecorder.setEnabled( enabled );
|
||||
}
|
||||
|
||||
if( mTrafficBasebandRecorder.isEnabled() != enabled )
|
||||
{
|
||||
mTrafficBasebandRecorder.setEnabled( enabled );
|
||||
}
|
||||
if(mAudioRecorder.isEnabled() != enabled)
|
||||
{
|
||||
mAudioRecorder.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mBasebandRecorder.isEnabled() != enabled)
|
||||
{
|
||||
mBasebandRecorder.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mTrafficBasebandRecorder.isEnabled() != enabled)
|
||||
{
|
||||
mTrafficBasebandRecorder.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mBitstreamRecorder.isEnabled() != enabled)
|
||||
{
|
||||
mBitstreamRecorder.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mTrafficBitstreamRecorder.isEnabled() != enabled)
|
||||
{
|
||||
mTrafficBitstreamRecorder.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if( hasItem() && isModified() )
|
||||
{
|
||||
RecordConfiguration config = getItem().getRecordConfiguration();
|
||||
|
||||
config.clearRecorders();
|
||||
|
||||
if( mAudioRecorder.isSelected() )
|
||||
{
|
||||
config.addRecorder( RecorderType.AUDIO );
|
||||
}
|
||||
|
||||
if( mBasebandRecorder.isSelected() )
|
||||
{
|
||||
config.addRecorder( RecorderType.BASEBAND );
|
||||
}
|
||||
|
||||
if( mTrafficBasebandRecorder.isSelected() )
|
||||
{
|
||||
config.addRecorder( RecorderType.TRAFFIC_BASEBAND );
|
||||
}
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
if(hasItem() && isModified())
|
||||
{
|
||||
RecordConfiguration config = getItem().getRecordConfiguration();
|
||||
|
||||
config.clearRecorders();
|
||||
|
||||
if(mAudioRecorder.isSelected())
|
||||
{
|
||||
config.addRecorder(RecorderType.AUDIO);
|
||||
}
|
||||
|
||||
if(mBasebandRecorder.isSelected())
|
||||
{
|
||||
config.addRecorder(RecorderType.BASEBAND);
|
||||
}
|
||||
|
||||
if(mTrafficBasebandRecorder.isSelected())
|
||||
{
|
||||
config.addRecorder(RecorderType.TRAFFIC_BASEBAND);
|
||||
}
|
||||
|
||||
if(mBitstreamRecorder.isSelected())
|
||||
{
|
||||
config.addRecorder(RecorderType.DEMODULATED_BIT_STREAM);
|
||||
}
|
||||
|
||||
if(mTrafficBitstreamRecorder.isSelected())
|
||||
{
|
||||
config.addRecorder(RecorderType.TRAFFIC_DEMODULATED_BIT_STREAM);
|
||||
}
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(Channel channel)
|
||||
{
|
||||
super.setItem(channel);
|
||||
|
||||
if(hasItem())
|
||||
{
|
||||
setControlsEnabled(true);
|
||||
|
||||
List<RecorderType> recorders = getItem().getRecordConfiguration().getRecorders();
|
||||
mAudioRecorder.setSelected(recorders.contains(RecorderType.AUDIO));
|
||||
mBasebandRecorder.setSelected(recorders.contains(RecorderType.BASEBAND));
|
||||
mTrafficBasebandRecorder.setSelected(recorders.contains(RecorderType.TRAFFIC_BASEBAND));
|
||||
mBitstreamRecorder.setSelected(recorders.contains(RecorderType.DEMODULATED_BIT_STREAM));
|
||||
mTrafficBitstreamRecorder.setSelected(recorders.contains(RecorderType.TRAFFIC_DEMODULATED_BIT_STREAM));
|
||||
}
|
||||
else
|
||||
{
|
||||
setControlsEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( Channel channel )
|
||||
{
|
||||
super.setItem( channel );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
setControlsEnabled( true );
|
||||
|
||||
List<RecorderType> recorders = getItem().getRecordConfiguration().getRecorders();
|
||||
mAudioRecorder.setSelected( recorders.contains( RecorderType.AUDIO ) );
|
||||
mBasebandRecorder.setSelected( recorders.contains( RecorderType.BASEBAND ) );
|
||||
mTrafficBasebandRecorder.setSelected( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
setControlsEnabled( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +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.record;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Recorder
|
||||
{
|
||||
RecorderType mRecorderType;
|
||||
|
||||
public Recorder()
|
||||
{
|
||||
}
|
||||
|
||||
public String getFileName()
|
||||
{
|
||||
return "No Recording";
|
||||
}
|
||||
|
||||
public void start() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
public void stop() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
public void pause()
|
||||
{
|
||||
}
|
||||
|
||||
public void resume()
|
||||
{
|
||||
}
|
||||
|
||||
public Recorder( RecorderType type )
|
||||
{
|
||||
mRecorderType = type;
|
||||
}
|
||||
|
||||
public RecorderType getRecorderType()
|
||||
{
|
||||
return mRecorderType;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* *********************************************************************************************************************
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
* Copyright (C) 2014-2018 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
|
||||
@@ -12,8 +11,8 @@
|
||||
*
|
||||
* 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.record;
|
||||
|
||||
import io.github.dsheirer.channel.metadata.Metadata;
|
||||
@@ -29,6 +28,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -228,13 +228,18 @@ public class RecorderManager implements Listener<ReusableAudioPacket>
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public Path getRecordingBasePath()
|
||||
{
|
||||
return SystemProperties.getInstance().getApplicationFolder("recordings");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a baseband recorder for use in a processing chain.
|
||||
*/
|
||||
public ComplexBufferWaveRecorder getBasebandRecorder(String channelName)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(SystemProperties.getInstance().getApplicationFolder("recordings"));
|
||||
sb.append(getRecordingBasePath());
|
||||
sb.append(File.separator).append(channelName).append("_baseband");
|
||||
|
||||
return new ComplexBufferWaveRecorder(BASEBAND_SAMPLE_RATE, sb.toString());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user