mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-10 23:57:25 -05:00
Compare commits
10 Commits
v0.2.0-bet
...
v0.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8a20b5adb | ||
|
|
c670f1d855 | ||
|
|
1dc0125ef4 | ||
|
|
225e747d6e | ||
|
|
36f9953156 | ||
|
|
47c452e7e6 | ||
|
|
06c8bba97e | ||
|
|
a25bb83dea | ||
|
|
8e11cb4554 | ||
|
|
2892e4609e |
30
README.md
30
README.md
@@ -1,18 +1,22 @@
|
||||
# sdrtrunk
|
||||
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
||||
# sdrtrunk #
|
||||
A cross-platform java application for decoding, monitoring and recording trunked
|
||||
mobile and related radio protocols using Software Defined Radios (SDR).
|
||||
|
||||
# Documentation #
|
||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
||||
* [User's Manual](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
|
||||
* [User's Manual (Version 0.2.0)](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
|
||||
* [Wiki](https://github.com/DSheirer/sdrtrunk/wiki)
|
||||
|
||||
# Support Group and Error Reporting #
|
||||
You can get help with setting up the sdrtrunk application or report any errors
|
||||
that you encounter while running the application in the sdrtrunk Google Group at:
|
||||
|
||||
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||
|
||||
# Updates #
|
||||
Subscribe to this group to receive updates about new releases and the ongoing
|
||||
application development effort.
|
||||
|
||||
* [Subscribe for Updates](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||
|
||||

|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-67489234-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<property name="major" value="0"/>
|
||||
<property name="minor" value="2"/>
|
||||
<property name="patch" value="0"/>
|
||||
<property name="release" value="beta.4"/>
|
||||
<property name="release" value="beta.5"/>
|
||||
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}-${release}"/>
|
||||
|
||||
<!-- Build properties -->
|
||||
|
||||
@@ -27,7 +27,6 @@ import audio.inverted.AudioType;
|
||||
public class TalkgroupID extends AliasID
|
||||
{
|
||||
private String mTalkgroup;
|
||||
private AudioType mAudioType;
|
||||
|
||||
public TalkgroupID()
|
||||
{
|
||||
@@ -44,20 +43,9 @@ public class TalkgroupID extends AliasID
|
||||
mTalkgroup = talkgroup;
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public AudioType getAudioType()
|
||||
{
|
||||
return mAudioType;
|
||||
}
|
||||
|
||||
public void setAudioType( AudioType type )
|
||||
{
|
||||
mAudioType = type;
|
||||
}
|
||||
|
||||
public boolean hasAudioType()
|
||||
{
|
||||
return mAudioType != null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
@@ -66,13 +54,6 @@ public class TalkgroupID extends AliasID
|
||||
|
||||
sb.append( "Talkgroup: " + mTalkgroup );
|
||||
|
||||
if( mAudioType != null )
|
||||
{
|
||||
sb.append( " [Audio " );
|
||||
sb.append( mAudioType.getShortDisplayString() );
|
||||
sb.append( "]" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
* 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
|
||||
@@ -20,9 +20,7 @@ package alias.id.talkgroup;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
@@ -30,7 +28,6 @@ import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import audio.inverted.AudioType;
|
||||
import controller.ConfigurableNode;
|
||||
|
||||
public class TalkgroupIDEditor extends JPanel implements ActionListener
|
||||
@@ -38,19 +35,15 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener
|
||||
private static final long serialVersionUID = 1L;
|
||||
private TalkgroupIDNode mTalkgroupIDNode;
|
||||
|
||||
private JLabel mLabelName;
|
||||
private JTextField mTextTalkgroupID;
|
||||
private JLabel mLabelAudio;
|
||||
private JComboBox<AudioType> mComboAudioTypes;
|
||||
|
||||
private String mHelpText = "Enter a formatted talkgroup identifier.\n\n"
|
||||
+ "P25 Talkgroup Format: hexadecimal value, 4 characters for "
|
||||
+ "talkgroup or 6 characters for a mobile radio\n\n"
|
||||
+ "LTR Talkgroup Format: A-HH-TTT (A=Area, H=Home Repeater, "
|
||||
+ "T=Talkgroup)\n\n"
|
||||
+ "Passport Talkgroup Format: xxxxx (up to 5-digit number)\n\n"
|
||||
+ "Wildcard: use one or more asterisks (*) for any talkgroup digits.\n\n"
|
||||
+ "Audio: if the talkgroup uses simple frequency inversion, specify"
|
||||
+ " the inversion frequency from the dropdown list to automatically"
|
||||
+ " un-invert audio for this specific talkgroup";
|
||||
+ "Wildcard: use one or more asterisks (*) for any talkgroup digits.";
|
||||
|
||||
public TalkgroupIDEditor( TalkgroupIDNode talkgroupIDNode )
|
||||
{
|
||||
@@ -72,22 +65,6 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener
|
||||
|
||||
add( mTextTalkgroupID, "growx,push" );
|
||||
|
||||
add( new JLabel( "Audio:" ) );
|
||||
|
||||
mComboAudioTypes = new JComboBox<AudioType>();
|
||||
|
||||
mComboAudioTypes.setModel(
|
||||
new DefaultComboBoxModel<AudioType>( AudioType.values() ) );
|
||||
|
||||
AudioType audioType = mTalkgroupIDNode.getTalkgroupID().getAudioType();
|
||||
|
||||
if( audioType != null )
|
||||
{
|
||||
mComboAudioTypes.setSelectedItem( audioType );
|
||||
}
|
||||
|
||||
add( mComboAudioTypes, "growx,push" );
|
||||
|
||||
JButton btnSave = new JButton( "Save" );
|
||||
btnSave.addActionListener( TalkgroupIDEditor.this );
|
||||
add( btnSave, "growx,push" );
|
||||
@@ -117,14 +94,6 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener
|
||||
|
||||
((ConfigurableNode)mTalkgroupIDNode.getParent()).sort();
|
||||
|
||||
AudioType selected = mComboAudioTypes
|
||||
.getItemAt( mComboAudioTypes.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
mTalkgroupIDNode.getTalkgroupID().setAudioType( selected );
|
||||
}
|
||||
|
||||
mTalkgroupIDNode.save();
|
||||
|
||||
mTalkgroupIDNode.show();
|
||||
@@ -139,9 +108,6 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener
|
||||
{
|
||||
mTextTalkgroupID.setText(
|
||||
mTalkgroupIDNode.getTalkgroupID().getTalkgroup() );
|
||||
|
||||
mComboAudioTypes.setSelectedItem(
|
||||
mTalkgroupIDNode.getTalkgroupID().getAudioType() );
|
||||
}
|
||||
|
||||
mTalkgroupIDNode.refresh();
|
||||
|
||||
@@ -182,7 +182,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
|
||||
for( AudioOutputConnection connection: mAudioOutputConnections )
|
||||
{
|
||||
if( connection.isConnected() && connection.isInactive() )
|
||||
if( connection.isInactive() &&
|
||||
mChannelConnectionMap.containsKey( connection.getSource() ) )
|
||||
{
|
||||
mChannelConnectionMap.remove( connection.getSource() );
|
||||
connection.disconnect();
|
||||
@@ -424,7 +425,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio output connection manages a connection between a soure and an audio
|
||||
* Audio output connection manages a connection between a source and an audio
|
||||
* output and maintains current state information about the audio activity
|
||||
* received form the source.
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package audio.output;
|
||||
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.sound.sampled.FloatControl;
|
||||
import javax.sound.sampled.LineListener;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -11,18 +11,15 @@ import org.slf4j.LoggerFactory;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import audio.AudioEvent;
|
||||
import audio.AudioEvent.Type;
|
||||
import audio.AudioPacket;
|
||||
import audio.metadata.AudioMetadata;
|
||||
import controller.ThreadPoolManager;
|
||||
|
||||
public abstract class AudioOutput implements Listener<AudioPacket>
|
||||
public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class );
|
||||
|
||||
private AtomicBoolean mSquelched = new AtomicBoolean( false );
|
||||
private Listener<AudioMetadata> mAudioMetadataListener;
|
||||
|
||||
protected Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
|
||||
protected LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>();
|
||||
protected ThreadPoolManager mThreadPoolManager;
|
||||
@@ -74,19 +71,6 @@ public abstract class AudioOutput implements Listener<AudioPacket>
|
||||
mAudioEventBroadcaster.broadcast( audioEvent );
|
||||
}
|
||||
|
||||
public void setSquelched( boolean squelched )
|
||||
{
|
||||
mSquelched.set( squelched );
|
||||
|
||||
broadcast( new AudioEvent( squelched ? Type.AUDIO_SQUELCHED :
|
||||
Type.AUDIO_UNSQUELCHED, getChannelName() ) );
|
||||
}
|
||||
|
||||
public boolean isSquelched()
|
||||
{
|
||||
return mSquelched.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a single listener to receive audio start and audio stop events
|
||||
*/
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.sound.sampled.BooleanControl;
|
||||
import javax.sound.sampled.Control;
|
||||
import javax.sound.sampled.FloatControl;
|
||||
import javax.sound.sampled.LineEvent;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
@@ -50,11 +51,9 @@ public class MonoAudioOutput extends AudioOutput
|
||||
private SourceDataLine mOutput;
|
||||
private FloatControl mGainControl;
|
||||
private BooleanControl mMuteControl;
|
||||
|
||||
private AudioEvent mAudioStartEvent;
|
||||
private AudioEvent mAudioStopEvent;
|
||||
private AudioEvent mAudioContinuationEvent;
|
||||
|
||||
private ScheduledFuture<?> mProcessorTask;
|
||||
|
||||
public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer )
|
||||
@@ -76,6 +75,7 @@ public class MonoAudioOutput extends AudioOutput
|
||||
if( mOutput != null )
|
||||
{
|
||||
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE );
|
||||
mOutput.addLineListener( this );
|
||||
mCanProcessAudio = true;
|
||||
}
|
||||
}
|
||||
@@ -162,17 +162,13 @@ public class MonoAudioOutput extends AudioOutput
|
||||
|
||||
if( packets.size() > 0 )
|
||||
{
|
||||
broadcast( mAudioContinuationEvent );
|
||||
|
||||
for( AudioPacket packet: packets )
|
||||
{
|
||||
broadcast( mAudioContinuationEvent );
|
||||
|
||||
if( mCanProcessAudio )
|
||||
{
|
||||
if( isSquelched() )
|
||||
{
|
||||
checkStop();
|
||||
}
|
||||
else if( packet.getType() == Type.AUDIO )
|
||||
if( packet.getType() == Type.AUDIO )
|
||||
{
|
||||
float[] samples = packet.getAudioBuffer().getSamples();
|
||||
|
||||
@@ -216,24 +212,20 @@ public class MonoAudioOutput extends AudioOutput
|
||||
|
||||
private void checkStart()
|
||||
{
|
||||
if( mCanProcessAudio && !isSquelched() &&
|
||||
if( mCanProcessAudio &&
|
||||
!mOutput.isRunning() && mOutput.available() <= START_THRESHOLD )
|
||||
{
|
||||
mOutput.start();
|
||||
|
||||
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStop()
|
||||
{
|
||||
if( mCanProcessAudio && mOutput.isRunning() &&
|
||||
( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) )
|
||||
mOutput.available() >= STOP_THRESHOLD )
|
||||
{
|
||||
mOutput.drain();
|
||||
mOutput.stop();
|
||||
|
||||
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,4 +264,19 @@ public class MonoAudioOutput extends AudioOutput
|
||||
{
|
||||
return mGainControl != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( LineEvent event )
|
||||
{
|
||||
LineEvent.Type type = event.getType();
|
||||
|
||||
if( type == LineEvent.Type.START )
|
||||
{
|
||||
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
|
||||
}
|
||||
else if( type == LineEvent.Type.STOP )
|
||||
{
|
||||
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.sound.sampled.BooleanControl;
|
||||
import javax.sound.sampled.Control;
|
||||
import javax.sound.sampled.FloatControl;
|
||||
import javax.sound.sampled.LineEvent;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
@@ -40,9 +41,9 @@ public class StereoAudioOutput extends AudioOutput
|
||||
private final static int BUFFER_SIZE = 96000;
|
||||
|
||||
/* The queue processor will run every 20 milliseconds checking for inbound
|
||||
* audio and automatically starting or stopping audio playback. We set the
|
||||
* audio and automatically start or stop audio playback. We set the
|
||||
* data line available space threshold to start when we're 20 ms away from
|
||||
* being full, and the stop threshold when 20 ms away from being empty */
|
||||
* being full and the stop threshold when 20 ms away from being empty */
|
||||
private final static int SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25;
|
||||
private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS;
|
||||
private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS;
|
||||
@@ -50,13 +51,10 @@ public class StereoAudioOutput extends AudioOutput
|
||||
private SourceDataLine mOutput;
|
||||
private FloatControl mGainControl;
|
||||
private BooleanControl mMuteControl;
|
||||
|
||||
private AudioEvent mAudioStartEvent;
|
||||
private AudioEvent mAudioStopEvent;
|
||||
private AudioEvent mAudioContinuationEvent;
|
||||
|
||||
private MixerChannel mMixerChannel;
|
||||
|
||||
private ScheduledFuture<?> mProcessorTask;
|
||||
|
||||
public StereoAudioOutput( ThreadPoolManager threadPoolManager,
|
||||
@@ -83,6 +81,7 @@ public class StereoAudioOutput extends AudioOutput
|
||||
if( mOutput != null )
|
||||
{
|
||||
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE );
|
||||
mOutput.addLineListener( this );
|
||||
mCanProcessAudio = true;
|
||||
}
|
||||
}
|
||||
@@ -202,17 +201,14 @@ public class StereoAudioOutput extends AudioOutput
|
||||
|
||||
if( packets.size() > 0 )
|
||||
{
|
||||
broadcast( mAudioContinuationEvent );
|
||||
|
||||
for( AudioPacket packet: packets )
|
||||
{
|
||||
broadcast( mAudioContinuationEvent );
|
||||
|
||||
if( mCanProcessAudio )
|
||||
{
|
||||
if( isSquelched() )
|
||||
{
|
||||
checkStop();
|
||||
}
|
||||
else if( packet.getType() == Type.AUDIO )
|
||||
if( packet.getType() == Type.AUDIO )
|
||||
{
|
||||
ByteBuffer buffer = convert(
|
||||
packet.getAudioBuffer().getSamples() );
|
||||
@@ -245,24 +241,20 @@ public class StereoAudioOutput extends AudioOutput
|
||||
|
||||
private void checkStart()
|
||||
{
|
||||
if( mCanProcessAudio && !isSquelched() &&
|
||||
if( mCanProcessAudio &&
|
||||
!mOutput.isRunning() && mOutput.available() <= START_THRESHOLD )
|
||||
{
|
||||
mOutput.start();
|
||||
|
||||
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStop()
|
||||
{
|
||||
if( mCanProcessAudio && mOutput.isRunning() &&
|
||||
( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) )
|
||||
mOutput.available() >= STOP_THRESHOLD )
|
||||
{
|
||||
mOutput.drain();
|
||||
mOutput.stop();
|
||||
|
||||
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,4 +293,20 @@ public class StereoAudioOutput extends AudioOutput
|
||||
{
|
||||
return mGainControl != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update( LineEvent event )
|
||||
{
|
||||
LineEvent.Type type = event.getType();
|
||||
|
||||
if( type == LineEvent.Type.START )
|
||||
{
|
||||
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
|
||||
}
|
||||
else if( type == LineEvent.Type.STOP )
|
||||
{
|
||||
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,9 @@ public class LTRStandardDecoderState extends DecoderState
|
||||
case CA_ENDD:
|
||||
CallEndMessage end = (CallEndMessage)message;
|
||||
|
||||
int repeater = end.getHomeRepeater();
|
||||
//Home channel is 31 for call end -- use the free channel
|
||||
//as the call end channel
|
||||
int repeater = end.getFree();
|
||||
|
||||
mLCNTracker.processCallChannel( repeater );
|
||||
|
||||
|
||||
@@ -253,20 +253,6 @@ public class ChannelList extends JPanel implements ChannelEventListener
|
||||
idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) );
|
||||
|
||||
JMenu noTunerMenu = new JMenu( "No Tuner" );
|
||||
channelsMenu.add( noTunerMenu );
|
||||
|
||||
noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) );
|
||||
noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) );
|
||||
noTunerMenu.add( new JSeparator() );
|
||||
|
||||
noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) );
|
||||
noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) );
|
||||
|
||||
JMenu labelMenu = new JMenu( "Labels" );
|
||||
colorMenu.add( labelMenu );
|
||||
|
||||
|
||||
@@ -144,11 +144,6 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
mMonitoring.set( false );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException( "Channel state monitor's scheduled "
|
||||
+ "future pointer was not null" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user