mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-10 07:37:25 -05:00
Compare commits
15 Commits
v0.3.0-bet
...
v0.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
108691dc3c | ||
|
|
455aa97286 | ||
|
|
d608834214 | ||
|
|
41660e1881 | ||
|
|
110ccb158c | ||
|
|
64ccacf04a | ||
|
|
f00b9555fe | ||
|
|
85442a9648 | ||
|
|
c67a0b329d | ||
|
|
8035d64fcd | ||
|
|
df2a061a08 | ||
|
|
e430319937 | ||
|
|
724f769771 | ||
|
|
c196cfebed | ||
|
|
e80b5866d4 |
@@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
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
|
||||
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
@@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
@@ -2,10 +2,9 @@
|
||||
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
||||
|
||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
||||
* [User's Manual Version 0.2.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
|
||||
* [User's Manual Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_Version0.3.0)
|
||||
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
||||
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||
|
||||

|
||||
**Figure 1:** sdrtrunk **Version 0.3.0** Application Screenshot
|
||||
**Figure 1:** sdrtrunk **Version 0.3.0** Application Screenshot
|
||||
|
||||
233
build/build.xml
233
build/build.xml
@@ -1,121 +1,142 @@
|
||||
<project name="SDRTrunk" default="SDRTrunk" basedir=".">
|
||||
<description>
|
||||
Ant build script for SDRTrunk Decoder Application
|
||||
</description>
|
||||
<description>
|
||||
Ant build script for SDRTrunk Decoder Application
|
||||
</description>
|
||||
|
||||
<!-- version properties -->
|
||||
<property name="major" value="0"/>
|
||||
<property name="minor" value="3"/>
|
||||
<property name="patch" value="0"/>
|
||||
<property name="release" value="-beta13"/>
|
||||
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
|
||||
|
||||
<!-- Build properties -->
|
||||
<property name="repo" location=".."/>
|
||||
<property name="source.code" location="${repo}/src"/>
|
||||
<property name="source.configs" location="${repo}/config"/>
|
||||
<property name="source.images" location="${repo}/images"/>
|
||||
<property name="source.libs" location="${repo}/imports"/>
|
||||
<property name="source.license" location="${repo}/license"/>
|
||||
<property name="source.scripts" location="${repo}/scripts"/>
|
||||
<!-- version properties -->
|
||||
<property name="major" value="0"/>
|
||||
<property name="minor" value="3"/>
|
||||
<property name="patch" value="1"/>
|
||||
<property name="release" value="final"/>
|
||||
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
|
||||
|
||||
<property name="output" location="${repo}/product"/>
|
||||
<property name="output.classes" location="${repo}/classes"/>
|
||||
<property name="output.folder" location="${output}/sdrtrunk"/>
|
||||
<property name="output.config" location="${output.folder}/config"/>
|
||||
<property name="output.images" location="${output.folder}/images"/>
|
||||
<property name="output.libs" location="${output.folder}/libs"/>
|
||||
<!-- Build properties -->
|
||||
<property name="repo" location=".."/>
|
||||
<property name="source.code" location="${repo}/src"/>
|
||||
<property name="source.configs" location="${repo}/config"/>
|
||||
<property name="source.images" location="${repo}/images"/>
|
||||
<property name="source.libs" location="${repo}/imports"/>
|
||||
<property name="source.scripts" location="${repo}/scripts"/>
|
||||
|
||||
<path id="classpath">
|
||||
<fileset dir="${source.libs}" includes="*.jar" />
|
||||
</path>
|
||||
|
||||
<target name="init">
|
||||
<!-- Create the classes folder to contain the compiled java classes -->
|
||||
<mkdir dir="${output.classes}"/>
|
||||
|
||||
<!-- Create version file -->
|
||||
<echo message="${major}.${minor}.${patch}-${release}" file="${output.classes}/sdrtrunk-version"/>
|
||||
</target>
|
||||
<property name="output" location="${repo}/product"/>
|
||||
<property name="output.classes" location="${repo}/classes"/>
|
||||
<property name="output.folder" location="${output}/sdrtrunk"/>
|
||||
<property name="output.config" location="${output.folder}/config"/>
|
||||
<property name="output.images" location="${output.folder}/images"/>
|
||||
<property name="output.libs" location="${output.folder}/libs"/>
|
||||
|
||||
<target name="compile" depends="clean,init" description="Compile java classes" >
|
||||
<javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false" />
|
||||
</target>
|
||||
<path id="classpath">
|
||||
<fileset dir="${source.libs}" includes="*.jar"/>
|
||||
</path>
|
||||
|
||||
<target name="SDRTrunk" depends="compile" description="Create SDRTrunk application" >
|
||||
<!-- Create the distribution directory -->
|
||||
<mkdir dir="${output.folder}"/>
|
||||
<target name="init">
|
||||
<!-- Create the classes folder to contain the compiled java classes -->
|
||||
<mkdir dir="${output.classes}"/>
|
||||
|
||||
<!-- Create jar file -->
|
||||
<jar jarfile="${output.folder}/sdrtrunk.jar" basedir="${output.classes}">
|
||||
<manifest>
|
||||
<attribute name="Title" value="sdrtrunk"/>
|
||||
<attribute name="Main-Class" value="gui.SDRTrunk"/>
|
||||
<attribute name="Version" value="${major}.${minor}.${patch}-${release}"/>
|
||||
</manifest>
|
||||
<fileset dir="${source.configs}">
|
||||
<exclude name="*.rules"/>
|
||||
</fileset>
|
||||
|
||||
<fileset dir="${source.license}">
|
||||
<include name="license.txt"/>
|
||||
</fileset>
|
||||
</jar>
|
||||
|
||||
<!-- Copy third-party libraries -->
|
||||
<mkdir dir="${output.libs}"/>
|
||||
<copy todir="${output.libs}">
|
||||
<fileset dir="${source.libs}" includes="*.jar" />
|
||||
</copy>
|
||||
|
||||
<!-- Copy config files -->
|
||||
<mkdir dir="${output.config}"/>
|
||||
<!-- Create version file -->
|
||||
<echo message="${major}.${minor}.${patch}-${release}" file="${output.classes}/sdrtrunk-version"/>
|
||||
</target>
|
||||
|
||||
<copy todir="${output.config}">
|
||||
<fileset dir="${source.configs}">
|
||||
<include name="*.rules"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<!-- Copy image files -->
|
||||
<mkdir dir="${output.images}"/>
|
||||
<copy todir="${output.images}">
|
||||
<fileset dir="${source.images}" />
|
||||
</copy>
|
||||
|
||||
<!-- Copy license file -->
|
||||
<copy todir="${output.folder}">
|
||||
<fileset dir="${source.license}">
|
||||
<include name="license.txt"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<!-- Copy program scripts -->
|
||||
<copy todir="${output.folder}">
|
||||
<fileset dir="${source.scripts}" />
|
||||
</copy>
|
||||
<target name="compile" depends="clean,init,compile-java-8,compile-java-9"
|
||||
description="Compile java classes" />
|
||||
|
||||
<target name="compile-java-8" depends="check-java-version" if="java.8" description="Compiles code with Java 8 arguments">
|
||||
<echo message="Compiling with Java 8 Options"/>
|
||||
<javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false"/>
|
||||
</target>
|
||||
|
||||
<target name="compile-java-9" depends="check-java-version" if="java.9" description="Compiles code with Java 9 arguments">
|
||||
<echo message="Compiling with Java 9 Options"/>
|
||||
<javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false">
|
||||
<compilerarg value="--add-modules"/>
|
||||
<compilerarg value="java.xml.bind"/>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="check-java-version" description="Determines if the compiler is Java 8 or 9">
|
||||
<condition property="java.8" value="true">
|
||||
<contains string="${java.version}" substring="1.8"/>
|
||||
</condition>
|
||||
<condition property="java.9" value="true">
|
||||
<equals arg1="${java.version}" arg2="9"/>
|
||||
</condition>
|
||||
<echo message="Java Version: ${java.version}"/>
|
||||
</target>
|
||||
|
||||
<target name="SDRTrunk" depends="compile" description="Create SDRTrunk application">
|
||||
<!-- Create the distribution directory -->
|
||||
<mkdir dir="${output.folder}"/>
|
||||
|
||||
<!-- Create jar file -->
|
||||
<jar jarfile="${output.folder}/sdrtrunk.jar" basedir="${output.classes}">
|
||||
<manifest>
|
||||
<attribute name="Title" value="sdrtrunk"/>
|
||||
<attribute name="Main-Class" value="gui.SDRTrunk"/>
|
||||
<attribute name="Version" value="${major}.${minor}.${patch}-${release}"/>
|
||||
</manifest>
|
||||
<fileset dir="${source.configs}">
|
||||
<exclude name="*.rules"/>
|
||||
</fileset>
|
||||
|
||||
<fileset dir="${repo}">
|
||||
<include name="LICENSE"/>
|
||||
</fileset>
|
||||
</jar>
|
||||
|
||||
<!-- Copy third-party libraries -->
|
||||
<mkdir dir="${output.libs}"/>
|
||||
<copy todir="${output.libs}">
|
||||
<fileset dir="${source.libs}" includes="*.jar"/>
|
||||
</copy>
|
||||
|
||||
<!-- Copy config files -->
|
||||
<mkdir dir="${output.config}"/>
|
||||
|
||||
<copy todir="${output.config}">
|
||||
<fileset dir="${source.configs}">
|
||||
<include name="*.rules"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<!-- Copy image files -->
|
||||
<mkdir dir="${output.images}"/>
|
||||
<copy todir="${output.images}">
|
||||
<fileset dir="${source.images}"/>
|
||||
</copy>
|
||||
|
||||
<!-- Copy license file -->
|
||||
<copy todir="${output.folder}">
|
||||
<fileset dir="${repo}">
|
||||
<include name="LICENSE"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<!-- Copy program scripts -->
|
||||
<copy todir="${output.folder}">
|
||||
<fileset dir="${source.scripts}"/>
|
||||
</copy>
|
||||
|
||||
|
||||
<!-- Bundle the files into .zip -->
|
||||
<zip destfile="${output}/${filename}.zip" basedir="${output}" />
|
||||
<!-- Bundle the files into .zip -->
|
||||
<zip destfile="${output}/${filename}.zip" basedir="${output}"/>
|
||||
|
||||
<!-- Remove linux start script before tar task -->
|
||||
<delete file="${output.folder}/run_sdrtrunk_linux.sh"/>
|
||||
<!-- Remove linux start script before tar task -->
|
||||
<delete file="${output.folder}/run_sdrtrunk_linux.sh"/>
|
||||
|
||||
<!-- Bundle files into .tar.gz and copy/set permissions on linux start script -->
|
||||
<tar destfile="${output}/${filename}.tar.gz" compression="gzip">
|
||||
<tarfileset dir="${output.folder}" prefix="sdrtrunk" />
|
||||
<tarfileset dir="${source.scripts}" prefix="sdrtrunk" filemode="777">
|
||||
<include name="run_sdrtrunk_linux.sh"/>
|
||||
</tarfileset>
|
||||
</tar>
|
||||
|
||||
<delete dir="${output.folder}"/>
|
||||
</target>
|
||||
<!-- Bundle files into .tar.gz and copy/set permissions on linux start script -->
|
||||
<tar destfile="${output}/${filename}.tar.gz" compression="gzip">
|
||||
<tarfileset dir="${output.folder}" prefix="sdrtrunk"/>
|
||||
<tarfileset dir="${source.scripts}" prefix="sdrtrunk" filemode="777">
|
||||
<include name="run_sdrtrunk_linux.sh"/>
|
||||
</tarfileset>
|
||||
</tar>
|
||||
|
||||
<target name="clean" description="Clean previous build artifacts" >
|
||||
<delete dir="${output.classes}"/>
|
||||
<delete dir="${output}"/>
|
||||
</target>
|
||||
<delete dir="${output.folder}"/>
|
||||
</target>
|
||||
|
||||
<target name="clean" description="Clean previous build artifacts">
|
||||
<delete dir="${output.classes}"/>
|
||||
<delete dir="${output}"/>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
@@ -1,2 +1,16 @@
|
||||
#!/bin/bash
|
||||
java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk
|
||||
|
||||
java -version
|
||||
|
||||
# Test for java version 9
|
||||
JAVA_9=$(java -version 2>&1 | grep -i version | sed 's/.* "\([9]\).*/\1/')
|
||||
|
||||
if [ -z ${JAVA_9+x} ]; then
|
||||
echo Using Java 9 program options
|
||||
# Java 9 uses the G1 garbage collector by default and requires that we specify the
|
||||
# java.xml.bind package since it's been removed from the java se core library
|
||||
java -cp "*:libs/*" --add-modules java.xml.bind gui.SDRTrunk;
|
||||
else
|
||||
echo Using Java 8 program options
|
||||
java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk;
|
||||
fi
|
||||
|
||||
@@ -1 +1,25 @@
|
||||
java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk
|
||||
@echo off
|
||||
|
||||
java -version
|
||||
|
||||
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||
|
||||
for /f "usebackq tokens=3" %%a in (`java -version 2^>^&1 ^| find "version"`) do (
|
||||
rem set b to the unquoted version string parsed from the java version command
|
||||
set b=%%~a
|
||||
|
||||
rem set c to the first character of the unquoted version string
|
||||
set c=!b:~0,1!
|
||||
|
||||
if !c! equ 9 (
|
||||
echo Using Java 9 options
|
||||
java -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" --add-modules java.xml.bind gui.SDRTrunk
|
||||
) else (
|
||||
echo Using Java 8 options
|
||||
java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk
|
||||
)
|
||||
)
|
||||
|
||||
ENDLOCAL
|
||||
|
||||
@echo on
|
||||
|
||||
@@ -36,6 +36,7 @@ import util.ThreadPool;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.UnresolvedAddressException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -44,7 +45,8 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
private final static Logger mLog = LoggerFactory.getLogger(IcecastTCPAudioBroadcaster.class);
|
||||
private final static String TERMINATOR = "\r\n";
|
||||
private final static String SEPARATOR = ":";
|
||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 3000; //3 seconds
|
||||
private static final long CONNECTION_ATTEMPT_TIMEOUT = 5000; //5 seconds
|
||||
|
||||
private NioSocketConnector mSocketConnector;
|
||||
private IoSession mStreamingSession = null;
|
||||
@@ -99,10 +101,11 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
if(mSocketConnector == null)
|
||||
{
|
||||
mSocketConnector = new NioSocketConnector();
|
||||
mSocketConnector.setConnectTimeoutCheckInterval(10000);
|
||||
mSocketConnector.setConnectTimeoutCheckInterval(CONNECTION_ATTEMPT_TIMEOUT);
|
||||
|
||||
// mSocketConnector.getFilterChain().addLast("logger",
|
||||
// new LoggingFilter(IcecastTCPAudioBroadcaster.class));
|
||||
// LoggingFilter loggingFilter = new LoggingFilter(IcecastTCPAudioBroadcaster.class);
|
||||
// loggingFilter.setMessageSentLogLevel(LogLevel.NONE);
|
||||
// mSocketConnector.getFilterChain().addLast("logger", loggingFilter);
|
||||
|
||||
mSocketConnector.getFilterChain().addLast("codec",
|
||||
new ProtocolCodecFilter(new IcecastCodecFactory()));
|
||||
@@ -123,9 +126,20 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
ConnectFuture future = mSocketConnector
|
||||
.connect(new InetSocketAddress(getBroadcastConfiguration().getHost(),
|
||||
getBroadcastConfiguration().getPort()));
|
||||
future.awaitUninterruptibly();
|
||||
|
||||
boolean connected = future.await(CONNECTION_ATTEMPT_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
|
||||
mStreamingSession = future.getSession();
|
||||
}
|
||||
catch(UnresolvedAddressException uae)
|
||||
{
|
||||
//What do you do with this? Nothing.
|
||||
}
|
||||
catch(InterruptedException ie)
|
||||
{
|
||||
//We couldn't connect within the timeout period -- disconnect so that we can try again later
|
||||
disconnect();
|
||||
}
|
||||
catch(RuntimeIoException rie)
|
||||
{
|
||||
Throwable throwableCause = rie.getCause();
|
||||
@@ -137,16 +151,20 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
else if(throwableCause != null)
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
mLog.debug("Failed to connect", rie);
|
||||
mLog.debug("[" + getStreamName() + "] failed to connect", rie);
|
||||
}
|
||||
else
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
mLog.debug("Failed to connect - no exception is available");
|
||||
mLog.debug("[" + getStreamName() + "] failed to connect - no exception is available");
|
||||
}
|
||||
|
||||
disconnect();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
mConnecting.set(false);
|
||||
}
|
||||
@@ -164,12 +182,13 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
*/
|
||||
public void disconnect()
|
||||
{
|
||||
if(connected())
|
||||
if(connected() && mStreamingSession != null)
|
||||
{
|
||||
if(mStreamingSession != null)
|
||||
{
|
||||
mStreamingSession.closeNow();
|
||||
}
|
||||
mStreamingSession.closeNow();
|
||||
}
|
||||
else
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,17 +245,18 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
@Override
|
||||
public void sessionClosed(IoSession session) throws Exception
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
|
||||
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
||||
if(!getBroadcastState().isErrorState())
|
||||
{
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
}
|
||||
|
||||
mSocketConnector.dispose();
|
||||
mStreamingSession = null;
|
||||
mSocketConnector = null;
|
||||
|
||||
mConnecting.set(false);
|
||||
|
||||
super.sessionClosed(session);
|
||||
}
|
||||
|
||||
@@ -253,45 +273,36 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
|
||||
if(connected())
|
||||
{
|
||||
mLog.info("Streaming connection error detected - resetting connection - " + reason);
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
else if(reason.startsWith("Connection reset"))
|
||||
{
|
||||
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
else if(reason.startsWith("Operation timed out"))
|
||||
{
|
||||
mLog.info("Streaming connection timed out - resetting connection");
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
disconnect();
|
||||
mLog.error("Unrecognized IO error: " + reason + ". Streaming halted.", cause);
|
||||
}
|
||||
}
|
||||
else if(ioe instanceof WriteTimeoutException)
|
||||
{
|
||||
mLog.info("Network write timeout error - resetting connection");
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
disconnect();
|
||||
mLog.error("Unspecified IO error - streaming halted.", cause);
|
||||
mLog.error("[" + getStreamName() + "] Unspecified IO error - streaming halted.", cause);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Broadcast error", cause);
|
||||
mLog.error("[" + getStreamName() + "] Broadcast error", cause);
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
disconnect();
|
||||
}
|
||||
@@ -319,6 +330,10 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
setBroadcastState(BroadcastState.INVALID_CREDENTIALS);
|
||||
}
|
||||
else if(message.contains("HTTP/1.1 501"))
|
||||
{
|
||||
disconnect(); //So that we can reconnect later
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Unrecognized server response:" + message);
|
||||
@@ -328,7 +343,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Icecast TCP broadcaster - unrecognized message [ " + object.getClass() +
|
||||
mLog.error("[" + getStreamName() + "]Icecast TCP broadcaster - unrecognized message [ " + object.getClass() +
|
||||
"] received:" + object.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +168,10 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
|
||||
disconnect();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
mConnecting.set(false);
|
||||
}
|
||||
@@ -185,12 +189,13 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
*/
|
||||
public void disconnect()
|
||||
{
|
||||
if(connected())
|
||||
if(connected() && mStreamingSession != null)
|
||||
{
|
||||
if(mStreamingSession != null)
|
||||
{
|
||||
mStreamingSession.closeNow();
|
||||
}
|
||||
mStreamingSession.closeNow();
|
||||
}
|
||||
else
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,15 +231,15 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
@Override
|
||||
public void sessionClosed(IoSession session) throws Exception
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
|
||||
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
||||
if(!getBroadcastState().isErrorState())
|
||||
{
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
}
|
||||
|
||||
mSocketConnector.dispose();
|
||||
mStreamingSession = null;
|
||||
mSocketConnector = null;
|
||||
|
||||
mConnecting.set(false);
|
||||
super.sessionClosed(session);
|
||||
@@ -255,13 +260,11 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
{
|
||||
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
else if(reason.startsWith("Operation timed out"))
|
||||
{
|
||||
mLog.info("Streaming connection timed out - resetting connection");
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -204,6 +204,10 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
||||
|
||||
disconnect();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
mConnecting.set(false);
|
||||
}
|
||||
@@ -221,13 +225,13 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
||||
*/
|
||||
public void disconnect()
|
||||
{
|
||||
if(connected())
|
||||
if(connected() && mStreamingSession != null)
|
||||
{
|
||||
if(mStreamingSession != null)
|
||||
{
|
||||
mStreamingSession.write(new TerminateBroadcast());
|
||||
mStreamingSession.closeNow();
|
||||
}
|
||||
mStreamingSession.closeNow();
|
||||
}
|
||||
else
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,15 +353,15 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
||||
@Override
|
||||
public void sessionClosed(IoSession session) throws Exception
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
|
||||
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
||||
if(!getBroadcastState().isErrorState())
|
||||
{
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
}
|
||||
|
||||
mSocketConnector.dispose();
|
||||
mStreamingSession = null;
|
||||
mSocketConnector = null;
|
||||
|
||||
mConnecting.set(false);
|
||||
super.sessionClosed(session);
|
||||
@@ -378,13 +382,11 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
||||
{
|
||||
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
else if(reason.startsWith("Operation timed out"))
|
||||
{
|
||||
mLog.info("Streaming connection timed out - resetting connection");
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -136,6 +136,17 @@ public class BinaryMessage extends BitSet
|
||||
{
|
||||
mCRC = crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the number of bit errors that were corrected in this message.
|
||||
*
|
||||
* @see CorrectedBinaryMessage subclass that supports setting this value
|
||||
* @return number of corrected bits in this message
|
||||
*/
|
||||
public int getCorrectedBitCount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current pointer index
|
||||
|
||||
65
src/bits/CorrectedBinaryMessage.java
Normal file
65
src/bits/CorrectedBinaryMessage.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 bits;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public class CorrectedBinaryMessage extends BinaryMessage
|
||||
{
|
||||
private int mCorrectedBitCount;
|
||||
|
||||
/**
|
||||
* Subclass of binary message class to allow capturing a corrected bits metric.
|
||||
* @param size of message to create.
|
||||
*/
|
||||
public CorrectedBinaryMessage(int size)
|
||||
{
|
||||
super(size);
|
||||
}
|
||||
|
||||
public CorrectedBinaryMessage(int size, boolean[] bitsToPreload)
|
||||
{
|
||||
super(size, bitsToPreload);
|
||||
}
|
||||
|
||||
public CorrectedBinaryMessage(BitSet bitset, int size)
|
||||
{
|
||||
super(bitset, size);
|
||||
}
|
||||
|
||||
public CorrectedBinaryMessage(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCorrectedBitCount()
|
||||
{
|
||||
return mCorrectedBitCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of bits that were corrected in this message
|
||||
* @param count of corrected bits
|
||||
*/
|
||||
public void setCorrectedBitCount(int count)
|
||||
{
|
||||
mCorrectedBitCount = count;
|
||||
}
|
||||
}
|
||||
86
src/edac/trellis/DMRNode.java
Normal file
86
src/edac/trellis/DMRNode.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 edac.trellis;
|
||||
|
||||
public class DMRNode extends Node
|
||||
{
|
||||
//Hamming error values indicating the number of bits set in each indexed value, 0-15
|
||||
public static final int[] HAMMING_ERROR_COUNT = new int[]{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
|
||||
|
||||
/**
|
||||
* Transmitted quad-bit value, derived from ETSI TS 102-361-1, Table B.7 Trellis encoder state transition table
|
||||
*
|
||||
* Note: the B.7 table contains constellation point ID numbers that have to be converted into actual bit values
|
||||
* where the bits represent the dibit pair for the constellation point.
|
||||
*/
|
||||
public static final int[][] DMR_TRANSITION_MATRIX = new int[][]
|
||||
{
|
||||
{2,13,14,1,7,8,11,4},
|
||||
{14,1,7,8,11,4,2,13},
|
||||
{10,5,6,9,15,0,3,12},
|
||||
{6,9,15,0,3,12,10,5},
|
||||
{15,0,3,12,10,5,6,9},
|
||||
{3,12,10,5,6,9,15,0},
|
||||
{7,8,11,4,2,13,14,1},
|
||||
{11,4,2,13,14,1,7,8}
|
||||
};
|
||||
|
||||
/**
|
||||
* Trellis Coded Modulation (TCM) 3/4-rate trellis node for decoding DMR
|
||||
*
|
||||
* @param inputValue for the trellis time instant
|
||||
* @param transmittedOutputValue the actual four-bit output value that was transmitted.
|
||||
*/
|
||||
public DMRNode(int inputValue, int transmittedOutputValue)
|
||||
{
|
||||
super(inputValue, transmittedOutputValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the error between the preceeding node and this node as the Hamming distance between the expected
|
||||
* four-bit output value versus the actual transmitted output value.
|
||||
*
|
||||
* @param precedingNode that precedes this node
|
||||
* @return error value for this node relative to the preceeding node argument
|
||||
*/
|
||||
@Override
|
||||
public int getError(Node precedingNode)
|
||||
{
|
||||
if(precedingNode != null)
|
||||
{
|
||||
int expectedOutputValue = getOutputValue(precedingNode);
|
||||
int errorMask = expectedOutputValue ^ getTransmittedOutputValue();
|
||||
return HAMMING_ERROR_COUNT[errorMask];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expected output four-bit value when this node is preceeded by the argument node.
|
||||
*
|
||||
* @param precedingNode that precedes this node
|
||||
* @return expected output value
|
||||
*/
|
||||
@Override
|
||||
public int getOutputValue(Node precedingNode)
|
||||
{
|
||||
return DMR_TRANSITION_MATRIX[precedingNode.getInputValue()][getInputValue()];
|
||||
}
|
||||
}
|
||||
77
src/edac/trellis/Node.java
Normal file
77
src/edac/trellis/Node.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 edac.trellis;
|
||||
|
||||
public abstract class Node
|
||||
{
|
||||
private int mInputValue;
|
||||
private int mTransmittedOutputValue;
|
||||
|
||||
/**
|
||||
* Trellis Coded Modulation (TCM) node. A node represents a time instant on the trellis and contains an input
|
||||
* value and the corresponding transmitted output value for the time instant. A node does not maintain the
|
||||
* state at the time instant -- the state is determined by the input value of a preceeding node.
|
||||
*
|
||||
* @param inputValue for this node
|
||||
* @param transmittedOutputValue for this node
|
||||
*/
|
||||
public Node(int inputValue, int transmittedOutputValue)
|
||||
{
|
||||
mInputValue = inputValue;
|
||||
mTransmittedOutputValue = transmittedOutputValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input value for this node.
|
||||
*
|
||||
* @return input value
|
||||
*/
|
||||
public int getInputValue()
|
||||
{
|
||||
return mInputValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmitted output value for this node
|
||||
* @return transmitted output value
|
||||
*/
|
||||
public int getTransmittedOutputValue()
|
||||
{
|
||||
return mTransmittedOutputValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the error value for this node (time instant) by determining the expected output value that would have
|
||||
* been transmitted using the state from the preceding node argument and this node's input value and comparing that
|
||||
* expected value to the actual transmitted output value. This error is normally represented as the Hamming
|
||||
* distance between the two values.
|
||||
*
|
||||
* @param precedingNode that precedes this node
|
||||
* @return error value for this node relative to the preceeding node
|
||||
*/
|
||||
public abstract int getError(Node precedingNode);
|
||||
|
||||
/**
|
||||
* Expected output value when this node is preceeded by the node specified in the argument.
|
||||
*
|
||||
* @param precedingNode that precedes this node
|
||||
* @return expected output value
|
||||
*/
|
||||
public abstract int getOutputValue(Node precedingNode);
|
||||
}
|
||||
131
src/edac/trellis/Path.java
Normal file
131
src/edac/trellis/Path.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 edac.trellis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Path
|
||||
{
|
||||
private List<Node> mNodes = new ArrayList<>();
|
||||
private int mError = 0;
|
||||
|
||||
/**
|
||||
* Trellis Coded Modulation (TCM) path. Represents a sequence of nodes that traverse various time instants and
|
||||
* states across a trellis.
|
||||
*
|
||||
* Note: each node can be shared across multiple paths. This class does not assume that any nodes are unique.
|
||||
* @param startingNode to start the path. This node normally contains an input value of zero, but is implementation
|
||||
* specific.
|
||||
*/
|
||||
public Path(Node startingNode)
|
||||
{
|
||||
add(startingNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trellis Coded Modulation (TCM) path. Represents a sequence of nodes that traverse various time instants and
|
||||
* states across a trellis.
|
||||
*
|
||||
* Note: each node can be shared across multiple paths. This class does not assume that any nodes are unique.
|
||||
*
|
||||
* @param original path to create a copy from. Each of the nodes in the original will be added to this path and
|
||||
* after construction both this path and the original will contain the exact same set of nodes.
|
||||
*/
|
||||
private Path(Path original)
|
||||
{
|
||||
for(Node node: original.getNodes())
|
||||
{
|
||||
add(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cumulative error across all nodes contained within this path. Error value is updated as each new node is added
|
||||
* to this path.
|
||||
*
|
||||
* @return cumulative error value
|
||||
*/
|
||||
public int getError()
|
||||
{
|
||||
return mError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Length or number of nodes contained within this path. Note: this value includes the starting node and may
|
||||
* also include a flushing node. The values contained in the starting and flushing nodes are normally not used
|
||||
* when decoding a path.
|
||||
*
|
||||
* @return number of nodes in this path
|
||||
*/
|
||||
public int getLength()
|
||||
{
|
||||
return mNodes.size() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of nodes contained in this path.
|
||||
* @return nodes
|
||||
*/
|
||||
public List<Node> getNodes()
|
||||
{
|
||||
return mNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the node to this path and updates the cumulative error value for this path by adding the node's error
|
||||
* value to the running error total.
|
||||
*
|
||||
* @param node to add to this path
|
||||
*/
|
||||
public void add(Node node)
|
||||
{
|
||||
if(!mNodes.isEmpty())
|
||||
{
|
||||
mError += node.getError(getLastNode());
|
||||
}
|
||||
|
||||
mNodes.add(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Last node in this path.
|
||||
*
|
||||
* @return last node
|
||||
*/
|
||||
public Node getLastNode()
|
||||
{
|
||||
if(!mNodes.isEmpty())
|
||||
{
|
||||
return mNodes.get(mNodes.size() - 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create an exact copy of this path.
|
||||
*
|
||||
* @return a new copy of this path.
|
||||
*/
|
||||
public Path copyOf()
|
||||
{
|
||||
return new Path(this);
|
||||
}
|
||||
}
|
||||
226
src/edac/trellis/ViterbiDecoder.java
Normal file
226
src/edac/trellis/ViterbiDecoder.java
Normal file
@@ -0,0 +1,226 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 edac.trellis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class ViterbiDecoder
|
||||
{
|
||||
private int mInputBitLength;
|
||||
private int mInputValueCount;
|
||||
private int mOutputBitLength;
|
||||
private int mOutputValueCount;
|
||||
|
||||
/**
|
||||
* Viterbi decoder for trellis coded modulation (TCM) encoded binary sequences.
|
||||
*
|
||||
* @param inputBitLength for coding (e.g. 3/4 rate input bit length is 3)
|
||||
* @param outputBitLength for coding (e.g. 3/4 rate output bit length is 4)
|
||||
*/
|
||||
public ViterbiDecoder(int inputBitLength, int outputBitLength)
|
||||
{
|
||||
mInputBitLength = inputBitLength;
|
||||
mOutputBitLength = outputBitLength;
|
||||
|
||||
mInputValueCount = (int)Math.pow(2.0, mInputBitLength);
|
||||
mOutputValueCount = (int)Math.pow(2.0, mOutputBitLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a node that represents an input value and the actual/transmitted output value.
|
||||
*
|
||||
* Note: this method must be implemented by a sub-class since the node requires a specific state transition
|
||||
* table/matrix implementation.
|
||||
*
|
||||
* @param inputValue for the specific trellis node
|
||||
* @param transmittedOutputValue the actual transmitted output value
|
||||
* @return node for decoding
|
||||
*/
|
||||
protected abstract Node createNode(int inputValue, int transmittedOutputValue);
|
||||
|
||||
/**
|
||||
* Creates a flushing or final node that represents an actual/transmitted output value and a flushing input value.
|
||||
*
|
||||
* The final flushing input value is implementation-specific, therefore this will be implemented in any subclass
|
||||
* of this decoder. A normal flushing input value is zero.
|
||||
*
|
||||
* @param transmittedOutputValue the actual transmitted output value
|
||||
* @return flushing node
|
||||
*/
|
||||
protected abstract Node createFlushingNode(int transmittedOutputValue);
|
||||
|
||||
/**
|
||||
* Creates a starting node that represents a starting state and notional transmitted output value.
|
||||
*
|
||||
* The starting input value is implementation-specific, therefore this will be implemented in any subclass
|
||||
* of this decoder. A normal starting input value is zero.
|
||||
*
|
||||
* @return starting node
|
||||
*/
|
||||
protected abstract Node createStartingNode();
|
||||
|
||||
/**
|
||||
* Size in bits of the input and state values (e.g. 3/4 rate input size is 3)
|
||||
*
|
||||
* @return input bit size
|
||||
*/
|
||||
public int getInputBitLength()
|
||||
{
|
||||
return mInputBitLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of input or state values where the set of values is in the range of 0 <> count - 1
|
||||
*
|
||||
* @return input or state values count
|
||||
*/
|
||||
public int getInputValueCount()
|
||||
{
|
||||
return mInputValueCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size in bits of the output constellation or transmitted symbol value (e.g. 3/4 rate output size is 4)
|
||||
*
|
||||
* @return output size in bits
|
||||
*/
|
||||
public int getOutputBitLength()
|
||||
{
|
||||
return mOutputBitLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of input or state values where the set of values is in the range of 0 <> count - 1
|
||||
*
|
||||
* @return input or state values count
|
||||
*/
|
||||
public int getOutputValueCount()
|
||||
{
|
||||
return mOutputValueCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the TCM encoded transmitted output values and returns a path the represents the most likely transmitted
|
||||
* sequence of nodes.
|
||||
*
|
||||
* @param transmittedOutputValues from the encoded message
|
||||
* @return most likely path representing the transmitted values
|
||||
*/
|
||||
public Path decode(int[] transmittedOutputValues)
|
||||
{
|
||||
Collection<Path> survivingPaths = new ArrayList<>();
|
||||
|
||||
survivingPaths.add(new Path(createStartingNode()));
|
||||
|
||||
//Add all but the last transmitted value to the starting path, creating a set of survivors at each iteration.
|
||||
for(int x = 0; x < transmittedOutputValues.length - 1; x++)
|
||||
{
|
||||
survivingPaths = add(survivingPaths, transmittedOutputValues[x]);
|
||||
}
|
||||
|
||||
//Flush the survivors with the final transmitted value and return the lone surviving path
|
||||
return flush(survivingPaths, transmittedOutputValues[transmittedOutputValues.length - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set of new nodes representing all possible input values and the transmitted output value argument
|
||||
* and iteratively adds the set of nodes to a copy of each of the surviving paths. A set of surviving paths is
|
||||
* returned that represents each path that terminates at a distinct input/state value that has the least error
|
||||
* value.
|
||||
*
|
||||
* @param survivingPaths from the previous time instant to evaluate for the next time instant in the trellis
|
||||
* using the transmitted output value.
|
||||
*
|
||||
* @param transmittedOutputValue that will be decoded for this time instant.
|
||||
*
|
||||
* @return paths that have survived (ie have the lowest error value) after adding nodes for the transmitted
|
||||
* output value
|
||||
*
|
||||
*/
|
||||
public Collection<Path> add(Collection<Path> survivingPaths, int transmittedOutputValue)
|
||||
{
|
||||
Map<Integer,Path> survivingPathMap = new HashMap<>();
|
||||
|
||||
for(int x = 0; x < getInputValueCount(); x++)
|
||||
{
|
||||
//Create a single/unique node for each possible input value combined with the transmitted output value
|
||||
Node node = createNode(x, transmittedOutputValue);
|
||||
|
||||
//Evaluate each surviving path by creating a copy and adding the node to it and see if it survives
|
||||
for(Path path: survivingPaths)
|
||||
{
|
||||
Path pathToEvaluate = path.copyOf();
|
||||
|
||||
pathToEvaluate.add(node);
|
||||
|
||||
if(survivingPathMap.containsKey(node.getInputValue()))
|
||||
{
|
||||
Path survivingPath = survivingPathMap.get(node.getInputValue());
|
||||
|
||||
//Replace the current survivor path if this evaluate path has a lower error value
|
||||
if(pathToEvaluate.getError() < survivingPath.getError())
|
||||
{
|
||||
survivingPathMap.put(node.getInputValue(), pathToEvaluate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//No survivor yet -- set this evaluate path as the first survivor
|
||||
survivingPathMap.put(node.getInputValue(), pathToEvaluate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return survivingPathMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes each of the surviving paths with a final/flushing node using the transmitted symbol. A final flushing
|
||||
* input value or state is implementation-specific, but normally uses a value of zero. Since all paths will be
|
||||
* evaluated against each other as terminating at the same state, only the path with the lowest error value will
|
||||
* be returned from this method.
|
||||
*
|
||||
* @param survivingPaths to flush with the final flushing node
|
||||
* @param transmittedOutputValue to use for the final flushing node
|
||||
*
|
||||
* @return the single survivor path that remains after adding the flushing node to each of the candidate argument
|
||||
* survivor paths.
|
||||
*/
|
||||
public Path flush(Collection<Path> survivingPaths, int transmittedOutputValue)
|
||||
{
|
||||
Node flushingNode = createFlushingNode(transmittedOutputValue);
|
||||
|
||||
Path bestPath = null;
|
||||
|
||||
for(Path survivingPath: survivingPaths)
|
||||
{
|
||||
survivingPath.add(flushingNode);
|
||||
|
||||
if(bestPath == null || (survivingPath.getError() < bestPath.getError()))
|
||||
{
|
||||
bestPath = survivingPath;
|
||||
}
|
||||
}
|
||||
|
||||
return bestPath;
|
||||
}
|
||||
}
|
||||
159
src/edac/trellis/ViterbiDecoder_3_4_DMR.java
Normal file
159
src/edac/trellis/ViterbiDecoder_3_4_DMR.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 edac.trellis;
|
||||
|
||||
import bits.BinaryMessage;
|
||||
import bits.CorrectedBinaryMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
|
||||
{
|
||||
/**
|
||||
* Viterbi decoder for Digital Mobile Radio (DMR) 3/4 rate Trellis Coded Modulation (TCM) encoded messages.
|
||||
*/
|
||||
public ViterbiDecoder_3_4_DMR()
|
||||
{
|
||||
super(3,4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a 3/4 rate trellis coded modulation (TCM) encoded DMR binary message containing 196 bits that have
|
||||
* already been deinterleaved.
|
||||
*
|
||||
* @param encodedMessage to decode that has already been deinterleaved.
|
||||
* @return decoded message
|
||||
*/
|
||||
public BinaryMessage decode(BinaryMessage encodedMessage)
|
||||
{
|
||||
int[] symbols = getSymbols(encodedMessage);
|
||||
|
||||
Path mostLikelyPath = decode(symbols);
|
||||
|
||||
return getMessage(mostLikelyPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the decoded/corrected message from the surviving path argument.
|
||||
*
|
||||
* @param path to extract a message from
|
||||
* @return corrected binary message.
|
||||
*/
|
||||
private static CorrectedBinaryMessage getMessage(Path path)
|
||||
{
|
||||
List<Node> mNodes = path.getNodes();
|
||||
|
||||
//Each node contains an input value excluding the starting(0) and final flushing(0) nodes.
|
||||
CorrectedBinaryMessage message = new CorrectedBinaryMessage((mNodes.size() - 2) * 3);
|
||||
|
||||
if(mNodes.size() > 2)
|
||||
{
|
||||
for(int x = 1; x < mNodes.size() - 1; x++)
|
||||
{
|
||||
int inputValue = mNodes.get(x).getInputValue();
|
||||
|
||||
int messageOffset = 3 * (x - 1);
|
||||
|
||||
if((inputValue & 4) == 4)
|
||||
{
|
||||
message.set(messageOffset);
|
||||
}
|
||||
|
||||
if((inputValue & 2) == 2)
|
||||
{
|
||||
message.set(messageOffset + 1);
|
||||
}
|
||||
|
||||
if((inputValue & 1) == 1)
|
||||
{
|
||||
message.set(messageOffset + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Transfer the corrected error count to the message
|
||||
message.setCorrectedBitCount(path.getError());
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts (49) four-bit symbols (196 bits / 4) from the deinterleaved DMR binary message
|
||||
*
|
||||
* @param encodedMessage that deinterleaved and contains 196-bits of 3/4 TCM encoding
|
||||
* @return symbols
|
||||
*/
|
||||
public int[] getSymbols(BinaryMessage encodedMessage)
|
||||
{
|
||||
//Ensure we have an integral number of transmitted symbols (nibbles) in the message
|
||||
if(encodedMessage.size() % getOutputBitLength() != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Encoded message must contain an integral number of 4-bit symbols -" +
|
||||
" message size: " + encodedMessage.size());
|
||||
}
|
||||
|
||||
int[] symbols = new int[encodedMessage.size() / getOutputBitLength()];
|
||||
|
||||
int index = 0;
|
||||
int pointer = 0;
|
||||
|
||||
while(index < encodedMessage.size())
|
||||
{
|
||||
int symbol = encodedMessage.getInt(index, index + (getOutputBitLength() - 1));
|
||||
symbols[pointer++] = symbol;
|
||||
index += getOutputBitLength();
|
||||
}
|
||||
|
||||
return symbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DMR node
|
||||
* @param inputValue for the specific trellis node
|
||||
* @param transmittedOutputValue the actual transmitted output value
|
||||
* @return created node
|
||||
*/
|
||||
@Override
|
||||
protected Node createNode(int inputValue, int transmittedOutputValue)
|
||||
{
|
||||
return new DMRNode(inputValue, transmittedOutputValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DMR flushing node. DMR uses an input value of zero to flush the final output value out of the
|
||||
* encoder.
|
||||
*
|
||||
* @param transmittedOutputValue the actual transmitted output value
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected Node createFlushingNode(int transmittedOutputValue)
|
||||
{
|
||||
return new DMRNode(0, transmittedOutputValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DMR starting node. DMR uses a starting input value of zero for the initial node.
|
||||
*/
|
||||
@Override
|
||||
protected Node createStartingNode()
|
||||
{
|
||||
return new DMRNode(0,0);
|
||||
}
|
||||
}
|
||||
@@ -151,41 +151,4 @@ public class RadioReferenceService
|
||||
|
||||
return mClient.getUserFeedBroadcasts(mUserAuthorization);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
RadioReferenceService service = new RadioReferenceService("dsheirer", "1dodgeram");
|
||||
|
||||
Long expiration = service.getAccountExpirationDate();
|
||||
|
||||
mLog.debug("Expiration Date:" + (expiration != null ? new Date(expiration).toString() : "unknown"));
|
||||
|
||||
mLog.debug("Is Active: " + service.isAccountActive());
|
||||
|
||||
UserFeedBroadcast[] feeds = service.getUserFeedBroadcasts();
|
||||
|
||||
for(UserFeedBroadcast feed: feeds)
|
||||
{
|
||||
mLog.debug("Feed ID: " + feed.getFeedId() + " Desc:" + feed.getDescr());
|
||||
}
|
||||
}
|
||||
catch(AxisFault af)
|
||||
{
|
||||
mLog.debug("Hey, I caught an axis fault!");
|
||||
mLog.debug("Reason:" + af.getFaultReason());
|
||||
}
|
||||
catch(RemoteException re)
|
||||
{
|
||||
re.printStackTrace();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
mLog.debug("Finished!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user