mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
Compare commits
14 Commits
v0.3.0
...
v0.3.2-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b42e64f50 | ||
|
|
1ddc0a33a5 | ||
|
|
88580af436 | ||
|
|
64688a3c20 | ||
|
|
8be3399a6b | ||
|
|
28340809ca | ||
|
|
f3d3a8187a | ||
|
|
8484e837b8 | ||
|
|
01b188ea25 | ||
|
|
108691dc3c | ||
|
|
455aa97286 | ||
|
|
d608834214 | ||
|
|
41660e1881 | ||
|
|
110ccb158c |
10
CHANGELOG
10
CHANGELOG
@@ -1,5 +1,13 @@
|
||||
0.3.2 20171105
|
||||
- Resolves issue with tuner frequency adjustments that was causing channel processing
|
||||
to lockup and show persistent 'overflow' state in each channel status
|
||||
- Resolves issue with broadcastify 'mountpoint-in-use' error handling and updates
|
||||
memory usage logging
|
||||
|
||||
0.3.0-beta1 20170129
|
||||
0.3.1 20171009
|
||||
- Resolves Java 9 incompatibility issues
|
||||
|
||||
0.3.0 20170922
|
||||
- REQUIRES: Java 8 (or newer) for compatibility and eventual changeover to JavaFX
|
||||
- FEATURE: audio streaming - adds capability to stream to multiple remote broadcast
|
||||
servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each
|
||||
|
||||
@@ -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
|
||||
|
||||
232
build/build.xml
232
build/build.xml
@@ -1,120 +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=""/>
|
||||
<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.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=""/> <!-- Use dash (-) prefix (e.g. -beta1) -->
|
||||
<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="${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}"/>
|
||||
<!-- 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="${repo}">
|
||||
<include name="LICENSE"/>
|
||||
</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,4 +1,6 @@
|
||||
<configuration>
|
||||
<conversionRule conversionWord="memory_usage" converterClass="util.MemoryUsageLogger" />
|
||||
|
||||
<timestamp key="appstart" datePattern="HHmmss"/>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
@@ -9,7 +11,7 @@
|
||||
<maxHistory>10</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%-25(%d{yyyyMMdd HHmmss.SSS} [%thread]) %-5level %logger{30} - %msg%n</pattern>
|
||||
<pattern>%-25(%d{yyyyMMdd HHmmss.SSS} [%thread]) %-5level %logger{30} - %msg %memory_usage%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
@@ -19,7 +21,7 @@
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%-12(%d{HH:mm:ss.SSS}) %-5level %logger{25} - %msg%n</pattern>
|
||||
<pattern>%-12(%d{HH:mm:ss.SSS}) %-5level %logger{25} - %msg %memory_usage%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -68,7 +68,7 @@ public enum BroadcastState
|
||||
/**
|
||||
* Specified mount point is already in use
|
||||
*/
|
||||
MOUNT_POINT_IN_USE("Mount Point In Use", true),
|
||||
MOUNT_POINT_IN_USE("Mount Point In Use", false),
|
||||
|
||||
/**
|
||||
* Server is not known or reachable
|
||||
|
||||
@@ -325,6 +325,10 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
setBroadcastState(BroadcastState.MOUNT_POINT_IN_USE);
|
||||
}
|
||||
else if(message.contains("Content-Type: text/html"))
|
||||
{
|
||||
//Disregard ... this normally follows the mount point in use error
|
||||
}
|
||||
else if(message.contains("Invalid Password") ||
|
||||
message.contains("Authentication Required"))
|
||||
{
|
||||
|
||||
@@ -71,6 +71,40 @@ public class AuthenticateBroadcast extends UltravoxMessage
|
||||
setPayload(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the value using the XTEA block cipher algorithm. The byte array is sliced into 8-byte chunks and each
|
||||
* chunk is encrypted into a 16 hexadecimal character string. All encrypted chunks are concatenated and returned
|
||||
* as a single string. If the value byte array is not an even multiple of 8 characters, it will be padded with
|
||||
* zero valued bytes to make it an even multiple of eight.
|
||||
*
|
||||
* @param xtea instance of the XTEA algorithm with a loaded encryption key
|
||||
* @param value to encode
|
||||
* @return value encrypted into one or more multiples of 16 hexadecimal characters
|
||||
*/
|
||||
private static String encrypt(XTEA xtea, byte[] value)
|
||||
{
|
||||
if(value.length % 8 != 0 || value.length == 0)
|
||||
{
|
||||
int newLength = ((value.length / 8) + 1) * 8;
|
||||
value = Arrays.copyOf(value, newLength);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(int x = 0; x < value.length; x += 8)
|
||||
{
|
||||
byte[] plaintext = Arrays.copyOfRange(value, x, x + 8);
|
||||
byte[] encrypted = xtea.encrypt(plaintext);
|
||||
|
||||
for(byte b: encrypted)
|
||||
{
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the value using the XTEA block cipher algorithm and returns the encrypted value as a 16 hex value
|
||||
*
|
||||
@@ -80,23 +114,6 @@ public class AuthenticateBroadcast extends UltravoxMessage
|
||||
*/
|
||||
private static String encrypt(XTEA xtea, String value)
|
||||
{
|
||||
byte[] valueBytes = value.getBytes();
|
||||
|
||||
if(valueBytes.length % 8 != 0 || valueBytes.length == 0)
|
||||
{
|
||||
int newLength = ((valueBytes.length / 8) + 1) * 8;
|
||||
valueBytes = Arrays.copyOf(valueBytes, newLength);
|
||||
}
|
||||
|
||||
byte[] encrypted = xtea.encrypt(valueBytes);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(byte b: encrypted)
|
||||
{
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
return encrypt(xtea, value.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import icon.IconManager;
|
||||
import map.MapService;
|
||||
import module.log.EventLogManager;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import playlist.PlaylistManager;
|
||||
@@ -80,16 +81,16 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
|
||||
public SDRTrunk()
|
||||
{
|
||||
mLog.info("");
|
||||
mLog.info("*******************************************************************");
|
||||
mLog.info("**** sdrtrunk: a trunked radio and digital decoding application ***");
|
||||
mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***");
|
||||
mLog.info("*******************************************************************");
|
||||
mLog.info("");
|
||||
mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors());
|
||||
mLog.info("Host Memory Total: " + Runtime.getRuntime().totalMemory());
|
||||
mLog.info("Host Memory Max: " + Runtime.getRuntime().maxMemory());
|
||||
mLog.info("Host Memory Free: " + Runtime.getRuntime().freeMemory());
|
||||
mLog.info("Memory Logging Format: [Used/Allocated PercentUsed%]");
|
||||
mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors());
|
||||
mLog.info("Host OS Name: " + System.getProperty("os.name"));
|
||||
mLog.info("Host OS Arch: " + System.getProperty("os.arch"));
|
||||
mLog.info("Host OS Version: " + System.getProperty("os.version"));
|
||||
mLog.info("Host Max Java Memory: " + FileUtils.byteCountToDisplaySize(Runtime.getRuntime().maxMemory()));
|
||||
|
||||
//Setup the application home directory
|
||||
Path home = getHomePath();
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* 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 source.tuner;
|
||||
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import source.SourceException;
|
||||
import source.tuner.configuration.TunerConfiguration;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
@@ -32,365 +27,344 @@ import source.tuner.frequency.FrequencyController;
|
||||
import source.tuner.frequency.FrequencyController.Tunable;
|
||||
import source.tuner.frequency.IFrequencyChangeProcessor;
|
||||
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
public abstract class TunerController implements Tunable, IFrequencyChangeProcessor
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( TunerController.class );
|
||||
private final static Logger mLog = LoggerFactory.getLogger(TunerController.class);
|
||||
|
||||
/* List of currently tuned channels being served to demod channels */
|
||||
private SortedSet<TunerChannel> mTunedChannels = new ConcurrentSkipListSet<>();
|
||||
protected FrequencyController mFrequencyController;
|
||||
private int mMiddleUnusable;
|
||||
private double mUsableBandwidthPercentage;
|
||||
|
||||
/**
|
||||
* Abstract tuner controller class. The tuner controller manages frequency
|
||||
* bandwidth and currently tuned channels that are being fed samples from
|
||||
* the tuner.
|
||||
*
|
||||
* @param minimumFrequency minimum uncorrected tunable frequency
|
||||
* @param maximumFrequency maximum uncorrected tunable frequency
|
||||
* @param middleUnusable is the +/- value center DC spike to avoid for channels
|
||||
* @param usableBandwidthPercentage is the unusable space at the extreme ends of the spectrum
|
||||
*
|
||||
* @throws SourceException - for any issues related to constructing the
|
||||
* class, tuning a frequency, or setting the bandwidth
|
||||
*/
|
||||
public TunerController( long minimumFrequency,
|
||||
long maximumFrequency,
|
||||
int middleUnusable,
|
||||
double usableBandwidthPercentage )
|
||||
{
|
||||
mFrequencyController = new FrequencyController( this,
|
||||
minimumFrequency,
|
||||
maximumFrequency,
|
||||
0.0d );
|
||||
mMiddleUnusable = middleUnusable;
|
||||
mUsableBandwidthPercentage = usableBandwidthPercentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the settings in the tuner configuration
|
||||
*/
|
||||
public abstract void apply( TunerConfiguration config )
|
||||
throws SourceException;
|
||||
/* List of currently tuned channels being served to demod channels */
|
||||
private SortedSet<TunerChannel> mTunedChannels = new ConcurrentSkipListSet<>();
|
||||
protected FrequencyController mFrequencyController;
|
||||
private int mMiddleUnusable;
|
||||
private double mUsableBandwidthPercentage;
|
||||
|
||||
/**
|
||||
* Responds to requests to set the frequency
|
||||
*/
|
||||
@Override
|
||||
public void frequencyChanged( FrequencyChangeEvent event ) throws SourceException
|
||||
{
|
||||
if( event.getEvent() == Event.REQUEST_FREQUENCY_CHANGE )
|
||||
{
|
||||
setFrequency( event.getValue().longValue() );
|
||||
}
|
||||
}
|
||||
|
||||
public int getBandwidth()
|
||||
/**
|
||||
* Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels
|
||||
* that are being fed samples from the tuner.
|
||||
*
|
||||
* @param minimumFrequency minimum uncorrected tunable frequency
|
||||
* @param maximumFrequency maximum uncorrected tunable frequency
|
||||
* @param middleUnusable is the +/- value center DC spike to avoid for channels
|
||||
* @param usableBandwidth percentage of usable bandwidth relative to space at the extreme ends of the spectrum
|
||||
* @throws SourceException - for any issues related to constructing the
|
||||
* class, tuning a frequency, or setting the bandwidth
|
||||
*/
|
||||
public TunerController(long minimumFrequency, long maximumFrequency, int middleUnusable, double usableBandwidth)
|
||||
{
|
||||
return mFrequencyController.getBandwidth();
|
||||
mFrequencyController = new FrequencyController(this, minimumFrequency, maximumFrequency, 0.0d);
|
||||
mMiddleUnusable = middleUnusable;
|
||||
mUsableBandwidthPercentage = usableBandwidth;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies the settings in the tuner configuration
|
||||
*/
|
||||
public abstract void apply(TunerConfiguration config) throws SourceException;
|
||||
|
||||
/**
|
||||
* Responds to requests to set the frequency
|
||||
*/
|
||||
@Override
|
||||
public void frequencyChanged(FrequencyChangeEvent event) throws SourceException
|
||||
{
|
||||
if(event.getEvent() == Event.REQUEST_FREQUENCY_CHANGE)
|
||||
{
|
||||
setFrequency(event.getValue().longValue());
|
||||
}
|
||||
}
|
||||
|
||||
public int getBandwidth()
|
||||
{
|
||||
return mFrequencyController.getBandwidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the center frequency of the local oscillator.
|
||||
*
|
||||
*
|
||||
* @param frequency in hertz
|
||||
* @throws SourceException - if the tuner has any issues
|
||||
*/
|
||||
public void setFrequency( long frequency ) throws SourceException
|
||||
{
|
||||
mFrequencyController.setFrequency( frequency );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the center frequency of the local oscillator
|
||||
*
|
||||
* @return frequency in hertz
|
||||
*/
|
||||
public long getFrequency()
|
||||
{
|
||||
return mFrequencyController.getFrequency();
|
||||
}
|
||||
|
||||
public int getSampleRate()
|
||||
{
|
||||
return mFrequencyController.getBandwidth();
|
||||
}
|
||||
|
||||
public double getFrequencyCorrection()
|
||||
{
|
||||
return mFrequencyController.getFrequencyCorrection();
|
||||
}
|
||||
|
||||
public void setFrequencyCorrection( double correction ) throws SourceException
|
||||
{
|
||||
mFrequencyController.setFrequencyCorrection( correction );
|
||||
}
|
||||
|
||||
public long getMinFrequency()
|
||||
{
|
||||
return mFrequencyController.getMinimumFrequency();
|
||||
}
|
||||
|
||||
public long getMaxFrequency()
|
||||
{
|
||||
return mFrequencyController.getMaximumFrequency();
|
||||
}
|
||||
|
||||
private long getMinTunedFrequency() throws SourceException
|
||||
{
|
||||
return mFrequencyController.getFrequency() - ( getUsableBandwidth() / 2 );
|
||||
}
|
||||
|
||||
private long getMaxTunedFrequency() throws SourceException
|
||||
{
|
||||
return mFrequencyController.getFrequency() + ( getUsableBandwidth() / 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if channel along with all of the other currently sourced
|
||||
* channels can fit within the tunable bandwidth.
|
||||
*/
|
||||
private boolean canTune( TunerChannel channel )
|
||||
{
|
||||
//Make sure we're within the tunable frequency range of this tuner
|
||||
if( getMinFrequency() < channel.getMinFrequency() &&
|
||||
getMaxFrequency() > channel.getMaxFrequency() )
|
||||
{
|
||||
//If this is the first lock, then we're good
|
||||
if( mTunedChannels.isEmpty() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int usableBandwidth = getUsableBandwidth();
|
||||
long minLockedFrequency = mTunedChannels.first().getMinFrequency();
|
||||
long maxLockedFrequency = mTunedChannels.last().getMaxFrequency();
|
||||
|
||||
//Requested channel is within current locked channel frequency range
|
||||
if( minLockedFrequency <= channel.getMinFrequency() &&
|
||||
channel.getMaxFrequency() <= maxLockedFrequency )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//Requested channel is higher than min locked frequency
|
||||
if( channel.getMaxFrequency() > minLockedFrequency &&
|
||||
channel.getMaxFrequency() - minLockedFrequency <= usableBandwidth )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//Requested channel is lower than the max locked frequency
|
||||
if( channel.getMinFrequency() <= maxLockedFrequency &&
|
||||
maxLockedFrequency - channel.getMinFrequency() <= usableBandwidth )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a digital drop channel (DDC) as a tuner channel from from
|
||||
* the tuner, or returns null if the channel cannot be sourced from the
|
||||
* tuner.
|
||||
*
|
||||
* This controller can't provide a channel if the channel, along with the
|
||||
* current set of sourced channels, can't be accomodated within the current
|
||||
* tuner bandwidth, or if a center frequency cannot be calculated that will
|
||||
* ensure all of the channels can be accomodated and any defined central DC
|
||||
* spike avoided.
|
||||
*
|
||||
* @param tuner to source the channel from
|
||||
* @param channel with defined center frequency and bandwidth
|
||||
*
|
||||
* @return fully constructed tuner channel or null
|
||||
*
|
||||
* @throws RejectedExecutionException if the decimation processor has an error
|
||||
*/
|
||||
public TunerChannelSource getChannel( Tuner tuner, TunerChannel channel )
|
||||
throws RejectedExecutionException
|
||||
{
|
||||
TunerChannelSource source = null;
|
||||
|
||||
if( canTune( channel ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
mTunedChannels.add( channel );
|
||||
|
||||
if( requiresLOUpdate( channel ) )
|
||||
{
|
||||
updateLOFrequency();
|
||||
}
|
||||
|
||||
source = new TunerChannelSource( tuner, channel );
|
||||
}
|
||||
catch( SourceException se )
|
||||
{
|
||||
mTunedChannels.remove( channel );
|
||||
source = null;
|
||||
}
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public int getChannelCount()
|
||||
{
|
||||
return mTunedChannels.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the tuner's LO frequency must be updated in order to
|
||||
* accomodate the tuner channel
|
||||
*/
|
||||
private boolean requiresLOUpdate( TunerChannel channel ) throws SourceException
|
||||
{
|
||||
return !( getMinTunedFrequency() <= channel.getMinFrequency() &&
|
||||
channel.getMaxFrequency() <= getMaxTunedFrequency() &&
|
||||
( ( mMiddleUnusable == 0 ) ||
|
||||
( !channel.overlaps( getTunedFrequency() - mMiddleUnusable,
|
||||
getTunedFrequency() + mMiddleUnusable ) ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the currently sourced tuner channel from this tuner and shuts
|
||||
* down the tuner if no other sources exist.
|
||||
*/
|
||||
public void releaseChannel( TunerChannelSource tunerChannelSource )
|
||||
{
|
||||
if( tunerChannelSource != null )
|
||||
{
|
||||
mTunedChannels.remove( tunerChannelSource.getTunerChannel() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Local Oscillator frequency accomodate the current set of tuned
|
||||
* channels.
|
||||
*
|
||||
* If there is only a single tuned channel, it is placed immediately to the
|
||||
* right of the usable bandwidth right of the central DC spike.
|
||||
*
|
||||
* Otherwise, it places the highest channel frequency at the upper end of
|
||||
* the tuner bandwidth, and then iteratively moves the center frequency
|
||||
* higher until all channels fit within the bandwidth and none of the
|
||||
* channels overlap any defined central DC spike unusable region. If a
|
||||
* center tune frequency cannot be calculated, throw an exception so that
|
||||
* the most recently added channel can be removed.
|
||||
*
|
||||
* Note: the tuned frequency is not changed until a legitimate new frequency
|
||||
* can be calculated. If an exception is thrown, the current frequency is
|
||||
* retained, so that the recently added channel can be removed and all other
|
||||
* channels can continue as previously arranged.
|
||||
*
|
||||
* @throws SourceException if the set of tuner channels, including a recently
|
||||
* added channel, cannot be tuned within the current tuner bandwidth and any
|
||||
* central DC spike unusable region.
|
||||
*/
|
||||
private void updateLOFrequency() throws SourceException
|
||||
{
|
||||
long frequency = getFrequency();
|
||||
|
||||
boolean frequencyValid = true;
|
||||
|
||||
//If there is only 1 channel, position it to the right of center
|
||||
if( mTunedChannels.size() == 1 )
|
||||
{
|
||||
frequency = mTunedChannels.first().getMinFrequency() - mMiddleUnusable;
|
||||
}
|
||||
else
|
||||
{
|
||||
long minLockedFrequency = mTunedChannels.first().getMinFrequency();
|
||||
long maxLockedFrequency = mTunedChannels.last().getMaxFrequency();
|
||||
|
||||
//Start by placing the highest frequency channel at the high end of
|
||||
//the spectrum
|
||||
frequency = maxLockedFrequency - ( getUsableBandwidth() / 2 );
|
||||
|
||||
//Iterate the channels and make sure that none of them overlap the
|
||||
//center DC spike buffer, if one exists
|
||||
if( mMiddleUnusable > 0 )
|
||||
{
|
||||
boolean processingRequired = true;
|
||||
|
||||
while( frequencyValid && processingRequired )
|
||||
{
|
||||
processingRequired = false;
|
||||
|
||||
long minAvoid = frequency - mMiddleUnusable;
|
||||
long maxAvoid = frequency + mMiddleUnusable;
|
||||
|
||||
for( TunerChannel channel: mTunedChannels )
|
||||
{
|
||||
if( channel.overlaps( minAvoid, maxAvoid ) )
|
||||
{
|
||||
//Can we move the frequency lower so that the
|
||||
//channel sits to the right of center?
|
||||
long adjustment = channel.getMaxFrequency() - minAvoid;
|
||||
|
||||
if( frequency + adjustment -
|
||||
( getUsableBandwidth() / 2 ) <= minLockedFrequency )
|
||||
{
|
||||
frequency += adjustment;
|
||||
processingRequired = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
frequencyValid = false;
|
||||
}
|
||||
|
||||
//break out of the for/each loop, so that we can
|
||||
//start over again with all of the channels
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( frequencyValid )
|
||||
{
|
||||
mFrequencyController.setFrequency( frequency );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SourceException( "Couldn't calculate viable center "
|
||||
+ "frequency from set of tuner channels" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Usable bandwidth - total bandwidth minus the unusable space at either end
|
||||
* of the spectrum.
|
||||
*/
|
||||
private int getUsableBandwidth()
|
||||
{
|
||||
return (int)( getBandwidth() * mUsableBandwidthPercentage );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to be notified any time that the tuner changes frequency
|
||||
* or bandwidth/sample rate.
|
||||
*
|
||||
* Note: this is normally used by the Tuner. Any additional listeners can
|
||||
* be registered on the tuner.
|
||||
*/
|
||||
public void addListener( IFrequencyChangeProcessor processor )
|
||||
public void setFrequency(long frequency) throws SourceException
|
||||
{
|
||||
mFrequencyController.addListener( processor );
|
||||
mFrequencyController.setFrequency(frequency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the center frequency of the local oscillator
|
||||
*
|
||||
* @return frequency in hertz
|
||||
*/
|
||||
public long getFrequency()
|
||||
{
|
||||
return mFrequencyController.getFrequency();
|
||||
}
|
||||
|
||||
public int getSampleRate()
|
||||
{
|
||||
return mFrequencyController.getBandwidth();
|
||||
}
|
||||
|
||||
public double getFrequencyCorrection()
|
||||
{
|
||||
return mFrequencyController.getFrequencyCorrection();
|
||||
}
|
||||
|
||||
public void setFrequencyCorrection(double correction) throws SourceException
|
||||
{
|
||||
mFrequencyController.setFrequencyCorrection(correction);
|
||||
}
|
||||
|
||||
public long getMinFrequency()
|
||||
{
|
||||
return mFrequencyController.getMinimumFrequency();
|
||||
}
|
||||
|
||||
public long getMaxFrequency()
|
||||
{
|
||||
return mFrequencyController.getMaximumFrequency();
|
||||
}
|
||||
|
||||
private long getMinTunedFrequency() throws SourceException
|
||||
{
|
||||
return mFrequencyController.getFrequency() - (getUsableBandwidth() / 2);
|
||||
}
|
||||
|
||||
private long getMaxTunedFrequency() throws SourceException
|
||||
{
|
||||
return mFrequencyController.getFrequency() + (getUsableBandwidth() / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if channel along with all of the other currently sourced
|
||||
* channels can fit within the tunable bandwidth.
|
||||
*/
|
||||
private boolean canTune(TunerChannel channel)
|
||||
{
|
||||
//Make sure we're within the tunable frequency range of this tuner
|
||||
if(getMinFrequency() < channel.getMinFrequency() && getMaxFrequency() > channel.getMaxFrequency())
|
||||
{
|
||||
//If this is the first lock, then we're good
|
||||
if(mTunedChannels.isEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int usableBandwidth = getUsableBandwidth();
|
||||
long minLockedFrequency = mTunedChannels.first().getMinFrequency();
|
||||
long maxLockedFrequency = mTunedChannels.last().getMaxFrequency();
|
||||
|
||||
//Requested channel is within current locked channel frequency range
|
||||
if(minLockedFrequency <= channel.getMinFrequency() && channel.getMaxFrequency() <= maxLockedFrequency)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//Requested channel is higher than min locked frequency
|
||||
if(channel.getMaxFrequency() > minLockedFrequency &&
|
||||
channel.getMaxFrequency() - minLockedFrequency <= usableBandwidth)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//Requested channel is lower than the max locked frequency
|
||||
if(channel.getMinFrequency() <= maxLockedFrequency &&
|
||||
maxLockedFrequency - channel.getMinFrequency() <= usableBandwidth)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a digital drop channel (DDC) as a tuner channel from from the tuner, or returns null if the channel
|
||||
* cannot be sourced from the tuner.
|
||||
*
|
||||
* This controller can't provide a channel if the channel, along with the current set of sourced channels, can't be
|
||||
* accomodated within the current tuner bandwidth, or if a center frequency cannot be calculated that will ensure
|
||||
* all of the channels can be accomodated and any defined central DC spike avoided.
|
||||
*
|
||||
* @param tuner to source the channel from
|
||||
* @param channel with defined center frequency and bandwidth
|
||||
* @return fully constructed tuner channel or null
|
||||
* @throws RejectedExecutionException if the decimation processor has an error
|
||||
*/
|
||||
public TunerChannelSource getChannel(Tuner tuner, TunerChannel channel) throws RejectedExecutionException
|
||||
{
|
||||
TunerChannelSource source = null;
|
||||
|
||||
if(canTune(channel))
|
||||
{
|
||||
try
|
||||
{
|
||||
mTunedChannels.add(channel);
|
||||
|
||||
if(requiresLOUpdate(channel))
|
||||
{
|
||||
updateLOFrequency();
|
||||
}
|
||||
|
||||
source = new TunerChannelSource(tuner, channel);
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mTunedChannels.remove(channel);
|
||||
source = null;
|
||||
}
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public int getChannelCount()
|
||||
{
|
||||
return mTunedChannels.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the tuner's LO frequency must be updated in order to accommodate the tuner channel
|
||||
*/
|
||||
private boolean requiresLOUpdate(TunerChannel channel) throws SourceException
|
||||
{
|
||||
boolean isBelow = channel.getMinFrequency() < getMinTunedFrequency();
|
||||
boolean isAbove = channel.getMaxFrequency() > getMaxTunedFrequency();
|
||||
boolean overlapsCenter = mMiddleUnusable > 0 &&
|
||||
channel.overlaps(getFrequency() - mMiddleUnusable, getFrequency() + mMiddleUnusable);
|
||||
|
||||
return isAbove || isBelow || overlapsCenter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the currently sourced tuner channel from this tuner and shuts down the tuner if no other sources exist.
|
||||
*/
|
||||
public void releaseChannel(TunerChannelSource tunerChannelSource)
|
||||
{
|
||||
if(tunerChannelSource != null)
|
||||
{
|
||||
mTunedChannels.remove(tunerChannelSource.getTunerChannel());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Local Oscillator frequency to accomodate the current set of tuned channels.
|
||||
*
|
||||
* If there is only a single tuned channel, it is placed immediately to the right of the usable bandwidth right
|
||||
* of the central DC spike.
|
||||
*
|
||||
* Otherwise, it places the highest channel frequency at the upper end of the tuner bandwidth, and then iteratively
|
||||
* moves the center frequency higher until all channels fit within the bandwidth and none of the channels overlap
|
||||
* any defined central DC spike unusable region. If a center tune frequency cannot be calculated, throw an
|
||||
* exception so that the most recently added channel can be removed.
|
||||
*
|
||||
* Note: the tuned frequency is not changed until a legitimate new frequency can be calculated. If an exception
|
||||
* is thrown, the current frequency is retained, so that the recently added channel can be removed and all other
|
||||
* channels can continue as previously arranged.
|
||||
*
|
||||
* @throws SourceException if the set of tuner channels, including a recently added channel, cannot be tuned
|
||||
* within the current tuner bandwidth and any central DC spike unusable region.
|
||||
*/
|
||||
private void updateLOFrequency() throws SourceException
|
||||
{
|
||||
long candidateFrequency;
|
||||
|
||||
boolean isValidCandidateFrequency = true;
|
||||
|
||||
//If there is only 1 channel set the center frequency so that the channel is positioned to the right of center
|
||||
if(mTunedChannels.size() == 1)
|
||||
{
|
||||
candidateFrequency = mTunedChannels.first().getMinFrequency() - mMiddleUnusable + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
long minLockedFrequency = mTunedChannels.first().getMinFrequency();
|
||||
long maxLockedFrequency = mTunedChannels.last().getMaxFrequency();
|
||||
|
||||
//Start by placing the highest frequency channel at the high end of the spectrum
|
||||
candidateFrequency = maxLockedFrequency - (getUsableBandwidth() / 2);
|
||||
|
||||
//Iterate the channels and make sure that none of them overlap the center DC spike buffer, if one exists
|
||||
if(mMiddleUnusable > 0)
|
||||
{
|
||||
boolean processingRequired = true;
|
||||
|
||||
while(isValidCandidateFrequency && processingRequired)
|
||||
{
|
||||
processingRequired = false;
|
||||
|
||||
long minAvoid = candidateFrequency - mMiddleUnusable;
|
||||
long maxAvoid = candidateFrequency + mMiddleUnusable;
|
||||
|
||||
//If any of the center channel(s) overlap the central DC spike avoid area, we'll iteratively
|
||||
//increase the tuned frequency causing the set of channels to move left in the tuned bandwidth until
|
||||
//we either find a good center tune frequency, or we walk the lowest frequency channel out of the
|
||||
//minimum tuned range, in which case we'll throw an exception indicating we don't have a solution.
|
||||
for(TunerChannel channel : mTunedChannels)
|
||||
{
|
||||
if(channel.overlaps(minAvoid, maxAvoid))
|
||||
{
|
||||
//Calculate a tuned frequency adjustment that places this overlapping channel just to the
|
||||
//left of the central DC spike avoid zone
|
||||
long adjustment = channel.getMaxFrequency() - minAvoid + 1;
|
||||
|
||||
//If the candidate frequency doesn't push the lowest channel out of bounds, make adjustment
|
||||
if(candidateFrequency + adjustment - (getUsableBandwidth() / 2) <= minLockedFrequency)
|
||||
{
|
||||
candidateFrequency += adjustment;
|
||||
processingRequired = true;
|
||||
}
|
||||
//Otherwise, punt and indicate that we can't find a center frequency or add the channel
|
||||
else
|
||||
{
|
||||
isValidCandidateFrequency = false;
|
||||
}
|
||||
|
||||
//break out of the for/each loop, so that we can start over again with all of the channels
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isValidCandidateFrequency)
|
||||
{
|
||||
mFrequencyController.setFrequency(candidateFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SourceException("Couldn't calculate viable center frequency from set of tuner channels");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Usable bandwidth - total bandwidth minus the unusable space at either end of the spectrum.
|
||||
*/
|
||||
private int getUsableBandwidth()
|
||||
{
|
||||
return (int)(getBandwidth() * mUsableBandwidthPercentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to be notified any time that the tuner changes frequency or bandwidth/sample rate.
|
||||
*
|
||||
* Note: this is normally used by the Tuner. Any additional listeners can be registered on the tuner.
|
||||
*/
|
||||
public void addListener(IFrequencyChangeProcessor processor)
|
||||
{
|
||||
mFrequencyController.addListener(processor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the frequency change listener
|
||||
*/
|
||||
public void removeListener( IFrequencyChangeProcessor processor )
|
||||
public void removeListener(IFrequencyChangeProcessor processor)
|
||||
{
|
||||
mFrequencyController.removeFrequencyChangeProcessor( processor );
|
||||
mFrequencyController.removeFrequencyChangeProcessor(processor);
|
||||
}
|
||||
}
|
||||
|
||||
54
src/util/MemoryUsageLogger.java
Normal file
54
src/util/MemoryUsageLogger.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* 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 util;
|
||||
|
||||
import ch.qos.logback.classic.pattern.ClassicConverter;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
/**
|
||||
* Custom logging plugin for logging memory usage with logback
|
||||
*
|
||||
* Add the following to the logback.xml configuration file to use this custom converter:
|
||||
*
|
||||
* <conversionRule conversionWord="memory_usage" converterClass="util.MemoryUsageLogger" />
|
||||
*
|
||||
* Update the encoder pattern to include %memory_usage within the pattern
|
||||
*/
|
||||
public class MemoryUsageLogger extends ClassicConverter
|
||||
{
|
||||
@Override
|
||||
public String convert(ILoggingEvent iLoggingEvent)
|
||||
{
|
||||
//Method argument is ignored - we simply return memory usage statistics
|
||||
long allocated = Runtime.getRuntime().totalMemory();
|
||||
long free = Runtime.getRuntime().freeMemory();
|
||||
long used = allocated - free;
|
||||
|
||||
int usedPercentage = (int)((double)(used) / (double)allocated * 100.0);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("[").append(FileUtils.byteCountToDisplaySize(used).replace(" ", ""));
|
||||
sb.append("/").append(FileUtils.byteCountToDisplaySize(allocated).replace(" ", ""));
|
||||
sb.append(" ").append(usedPercentage).append("%]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user