Compare commits

...

6 Commits

Author SHA1 Message Date
Denny Sheirer
a24c3736f4 #1340 Updates tuner and frequency controllers to apply locking to ensure multi-threaded access to freuqency control planes doesn't allow center tuned frequency to become indeterminant which can result in channels being mistuned and stop decoding. (#1341)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
2022-11-20 07:55:30 -05:00
Denny Sheirer
ac61b681e6 #1338 Updates logger to include date in log entries. Removes info logger when creating channel log files. (#1339)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
2022-11-20 05:54:05 -05:00
Denny Sheirer
0866bfd555 #1336 Resolves issue with color setting changes not inheriting the correct translucency which causes the channel overlays in the spectral display to be solid color instead of semi-transparent. (#1337)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
2022-11-20 05:43:28 -05:00
Denny Sheirer
79fed701d9 #1337 Updates build to use JDK19 for distribution images and updates all 3rd-party libraries to latest. Note: build system continues to be compatible with JDK 17 and 18, but Gradle 7.6 to build distribution images. Gradle 7.6 is not released yet, but RC3 is available. (#1335)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
2022-11-20 05:36:41 -05:00
Denny Sheirer
48a93990e8 #1291 Updates DC removal processing across tuner sample streams to run on a periodic timer to reduce overall processing requirements. Updates build version to 0.5.0 beta 5 (#1292)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
2022-07-23 08:14:31 -04:00
Denny Sheirer
414ba25d61 #1284 Resolves issue with inherent DC bias (-0.5) in RTL2832 sample conversion code. (#1285)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
2022-07-16 08:58:09 -04:00
29 changed files with 926 additions and 463 deletions

2
.idea/misc.xml generated
View File

@@ -4,7 +4,7 @@
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="liberica-18.0.1-fx" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="liberica-19.0.1.fx" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -28,7 +28,7 @@ repositories {
maven { url "https://jitpack.io" }
}
version = '0.5.0'
version = '0.5.0-beta6'
sourceCompatibility = '17'
sourceSets {
@@ -43,10 +43,10 @@ test {
dependencies {
// JUnit Tests
testImplementation 'junit:junit:4.12'
testImplementation 'org.assertj:assertj-core:3.8.0'
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
testImplementation 'org.mockito:mockito-core:3.+'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.assertj:assertj-core:3.23.1'
testImplementation('org.junit.jupiter:junit-jupiter-api:5.9.0')
testImplementation 'org.mockito:mockito-core:4.8.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
@@ -56,33 +56,33 @@ dependencies {
implementation 'com.github.nwaldispuehl:java-lame:v3.98.4'
//mavenCentral/jcenter imports
implementation 'ch.qos.logback:logback-classic:1.2.3'
implementation 'ch.qos.logback:logback-core:1.2.3'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.2'
implementation 'com.fazecast:jSerialComm:2.5.0'
implementation 'ch.qos.logback:logback-classic:1.4.4'
implementation 'ch.qos.logback:logback-core:1.4.4'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.14.0'
implementation 'com.fazecast:jSerialComm:2.9.2'
implementation 'com.github.jiconfont:jiconfont-font_awesome:4.7.0.1'
implementation 'com.github.jiconfont:jiconfont-javafx:1.0.0'
implementation 'com.github.jiconfont:jiconfont-swing:1.0.1'
implementation 'com.github.wendykierp:JTransforms:3.1'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.google.guava:guava:27.0.1-jre'
implementation 'com.google.code.gson:gson:2.10'
implementation 'com.google.guava:guava:31.1-jre'
implementation 'com.jidesoft:jide-oss:3.6.18'
implementation 'com.miglayout:miglayout-swing:5.2'
implementation 'com.miglayout:miglayout-swing:11.0'
implementation 'com.mpatric:mp3agic:0.9.1'
implementation 'commons-io:commons-io:2.7'
implementation 'commons-io:commons-io:2.11.0'
implementation 'eu.hansolo:charts:1.0.5'
implementation 'io.github.dsheirer:radio-reference-api:15.1.8'
implementation 'javax.usb:usb-api:1.0.2'
implementation 'net.coderazzi:tablefilter-swing:5.4.0'
implementation 'org.apache.commons:commons-compress:1.20'
implementation 'org.apache.commons:commons-lang3:3.8.1'
implementation 'net.coderazzi:tablefilter-swing:5.5.4'
implementation 'org.apache.commons:commons-compress:1.21'
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'org.apache.commons:commons-math3:3.6.1'
implementation 'org.apache.commons:commons-csv:1.9.0'
implementation 'org.apache.mina:mina-core:2.1.3'
implementation 'org.apache.mina:mina-http:2.1.3'
implementation 'org.controlsfx:controlsfx:11.1.0'
implementation 'org.rauschig:jarchivelib:1.0.0'
implementation 'org.slf4j:slf4j-api:1.7.25'
implementation 'org.apache.mina:mina-core:2.2.1'
implementation 'org.apache.mina:mina-http:2.2.1'
implementation 'org.controlsfx:controlsfx:11.1.2'
implementation 'org.rauschig:jarchivelib:1.2.0'
implementation 'org.slf4j:slf4j-api:2.0.3'
implementation 'org.usb4java:libusb4java:1.3.0'
implementation 'org.usb4java:usb4java:1.3.0'
implementation 'org.usb4java:usb4java-javax:1.3.0'
@@ -139,11 +139,11 @@ String targetWindowsX86_64 = 'windows-x86_64-v' + version
* author's development environment, but can be changed to your environment if you want to use this build pattern.
*/
String jdk_base = '/home/denny/java_jdks/'
String jdk_linux_aarch64 = jdk_base + 'linux-arm64/jdk-18.0.1-full'
String jdk_linux_x86_64 = jdk_base + 'linux-x64/jdk-18.0.1-full'
String jdk_osx_x86_64 = jdk_base + 'osx-x64/jdk-18.0.1-full.jdk'
String jdk_osx_aarch64 = jdk_base + 'osx-arm64/jdk-18.0.1-full.jdk'
String jdk_windows_x86_64 = jdk_base + 'windows-x64/jdk-18.0.1-full'
String jdk_linux_aarch64 = jdk_base + 'linux-arm64/jdk-19.0.1-full'
String jdk_linux_x86_64 = jdk_base + 'linux-x64/jdk-19.0.1-full'
String jdk_osx_x86_64 = jdk_base + 'osx-x64/jdk-19.0.1-full.jdk'
String jdk_osx_aarch64 = jdk_base + 'osx-arm64/jdk-19.0.1-full.jdk'
String jdk_windows_x86_64 = jdk_base + 'windows-x64/jdk-19.0.1-full'
boolean hasTargetJdks = file(jdk_linux_x86_64).exists() || file(jdk_linux_aarch64).exists() ||
file(jdk_osx_x86_64).exists() || file(jdk_osx_aarch64).exists() || file(jdk_windows_x86_64).exists()
@@ -172,19 +172,19 @@ runtime {
println("Building all runtime images")
println("The [ > jre: ] build phase downloads several target JDKs and this may take a while ... please wait")
targetPlatform(targetLinuxAarch64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/18.0.1+12/bellsoft-jdk18.0.1+12-linux-aarch64-full.tar.gz")
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-linux-aarch64-full.tar.gz")
}
targetPlatform(targetLinuxX86_64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/18.0.1+12/bellsoft-jdk18.0.1+12-linux-amd64-full.tar.gz")
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-linux-amd64-full.tar.gz")
}
targetPlatform(targetOsxAarch64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/18.0.1+12/bellsoft-jdk18.0.1+12-macos-aarch64-full.tar.gz")
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-macos-aarch64-full.tar.gz")
}
targetPlatform(targetOsxX86_64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/18.0.1+12/bellsoft-jdk18.0.1+12-macos-amd64-full.tar.gz")
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-macos-amd64-full.tar.gz")
}
targetPlatform(targetWindowsX86_64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/18.0.1+12/bellsoft-jdk18.0.1+12-windows-amd64-full.zip")
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-windows-amd64-full.zip")
}
}
//Build using pre-downloaded JDKs

Binary file not shown.

View File

@@ -19,6 +19,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-rc-3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

290
gradlew vendored
View File

@@ -1,94 +1,132 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# ******************************************************************************
# Copyright (C) 2014-2022 Dennis Sheirer
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 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.
#
# https://www.apache.org/licenses/LICENSE-2.0
# 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.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# *****************************************************************************
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -97,7 +135,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -105,79 +143,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

25
gradlew.bat vendored
View File

@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -51,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -61,28 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -21,7 +21,6 @@ package io.github.dsheirer.buffer;
import io.github.dsheirer.sample.complex.ComplexSamples;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import java.util.Iterator;
/**
@@ -42,7 +41,7 @@ public class ByteNativeBuffer implements INativeBuffer
for(int x = 0; x < 256; x++)
{
LOOKUP_VALUES[x] = (float)(x - 127) / 128.0f;
LOOKUP_VALUES[x] = ((float)x - 127.5f) / 128.0f;
}
}

View File

@@ -26,16 +26,20 @@ import java.nio.ByteBuffer;
*/
public class ByteNativeBufferFactory implements INativeBufferFactory
{
private static final float DC_FILTER_GAIN = 0.007f; //Normalizes DC over period of ~142 (1 / .007) buffers
private float mAverageDc = 0.0f;
private DcCorrectionManager mDcCorrectionManager = new DcCorrectionManager();
@Override
public INativeBuffer getBuffer(ByteBuffer samples, long timestamp)
{
byte[] copy = new byte[samples.capacity()];
samples.get(copy);
calculateDc(copy);
return new ByteNativeBuffer(copy, timestamp, mAverageDc);
if(mDcCorrectionManager.shouldCalculateDc())
{
calculateDc(copy);
}
return new ByteNativeBuffer(copy, timestamp, mDcCorrectionManager.getAverageDc());
}
/**
@@ -45,7 +49,7 @@ public class ByteNativeBufferFactory implements INativeBufferFactory
*/
private void calculateDc(byte[] samples)
{
double dcAccumulator = 0;
float dcAccumulator = 0;
for(byte sample: samples)
{
@@ -53,9 +57,8 @@ public class ByteNativeBufferFactory implements INativeBufferFactory
}
dcAccumulator /= samples.length;
dcAccumulator -= 127.0f;
dcAccumulator -= 127.5f;
dcAccumulator /= 128.0f;
dcAccumulator -= mAverageDc;
mAverageDc += (dcAccumulator * DC_FILTER_GAIN);
mDcCorrectionManager.adjust(dcAccumulator);
}
}

View File

@@ -0,0 +1,114 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.buffer;
import java.util.concurrent.TimeUnit;
/**
* Provides DC offset correction value and manages calculation processing interval.
*/
public class DcCorrectionManager
{
// private static Logger mLog = LoggerFactory.getLogger(DcCorrectionManager.class);
// private DecimalFormat mDecimalFormat = new DecimalFormat("0.00000");
/**
* Time (delay) interval for running DC removal calculations
*/
private static final long DC_PROCESSING_INTERVAL = TimeUnit.SECONDS.toMillis(50);
/**
* Number of buffers to process per DC calculation processing interval.
*/
private static final int DC_CALCULATIONS_PER_INTERVAL = 5;
/**
* DC offset processing residual threshold for ending DC processing interval
*/
private static final float TARGET_DC_OFFSET_REMAINING = 0.0001f;
/**
* Number of DC calculations remaining in the current interval
*/
private int mDcCalculationsRemaining = DC_CALCULATIONS_PER_INTERVAL; //Initial value for coarse correction
/**
* Last time DC offset calculation interval ran
*/
private long mLastDcCalculationTimestamp = 0;
/**
* DC offset calculation gain.
*/
private static final float DC_FILTER_GAIN = 0.05f;
/**
* Current DC offset correction value
*/
private float mAverageDc = 0.0f;
public float getAverageDc()
{
return mAverageDc;
}
/**
* Adjusts the average DC based on the current calculated DC offset present in a sample buffer.
* @param calculatedDc for the current sample buffer
*/
public void adjust(float calculatedDc)
{
//Calculate the average scaled DC offset so that it can be applied in the native buffer's converted samples
float residualOffsetToRemove = calculatedDc - mAverageDc;
mAverageDc += (residualOffsetToRemove * DC_FILTER_GAIN);
if(Math.abs(residualOffsetToRemove) < TARGET_DC_OFFSET_REMAINING)
{
mDcCalculationsRemaining--;
}
//
// mLog.info("DC: " + mDecimalFormat.format(mAverageDc) +
// " RES:" + mDecimalFormat.format(residualOffsetToRemove) +
// " REM:" + mDcCalculationsRemaining);
}
/**
* Indicates if a DC offset calculation for a buffer should be performed.
*/
public boolean shouldCalculateDc()
{
if(System.currentTimeMillis() > (mLastDcCalculationTimestamp + DC_PROCESSING_INTERVAL))
{
if(mDcCalculationsRemaining > 0)
{
return true;
}
else
{
//Reset the number of buffers to calculate and the timestamp
mDcCalculationsRemaining = DC_CALCULATIONS_PER_INTERVAL;
mLastDcCalculationTimestamp = System.currentTimeMillis();
}
}
return false;
}
}

View File

@@ -20,14 +20,51 @@
package io.github.dsheirer.buffer;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
/**
* Implements a factory for creating SignedByteNativeBuffer instances
*/
public class SignedByteNativeBufferFactory implements INativeBufferFactory
{
private static final float DC_FILTER_GAIN = 0.02f; //Normalizes DC over period of ~50 (1 / .02) buffers
/**
* DC removal calculations will run once a minute
*/
private static final long DC_PROCESSING_INTERVAL = TimeUnit.SECONDS.toMillis(60);
/**
* Number of buffers to process per DC calculation processing interval.
*/
private static final int DC_CALCULATIONS_PER_INTERVAL = 5;
/**
* DC offset processing residual threshold for ending DC processing interval
*/
private static final float TARGET_DC_OFFSET_REMAINING = 0.0002f;
/**
* Number of DC calculations remaining in the current interval
*/
private int mDcCalculationsRemaining = DC_CALCULATIONS_PER_INTERVAL; //Initial value for coarse correction
/**
* Last time DC offset calculation interval ran
*/
private long mLastDcCalculationTimestamp = 0;
/**
* DC offset calculation gain.
*/
private static final float DC_FILTER_GAIN = 0.05f;
/**
* Current DC offset correction for Inphase samples
*/
private float mIAverageDc = 0.0f;
/**
* Current DC offset correction for Quadrature samples
*/
private float mQAverageDc = 0.0f;
@Override
@@ -35,10 +72,37 @@ public class SignedByteNativeBufferFactory implements INativeBufferFactory
{
byte[] copy = new byte[samples.capacity()];
samples.get(copy);
calculateDc(copy);
if(shouldCalculateDc())
{
calculateDc(copy);
}
return new SignedByteNativeBuffer(copy, timestamp, mIAverageDc, mQAverageDc);
}
/**
* Indicates if a DC offset calculation for a buffer should be performed.
*/
private boolean shouldCalculateDc()
{
if(System.currentTimeMillis() > (mLastDcCalculationTimestamp + DC_PROCESSING_INTERVAL))
{
if(mDcCalculationsRemaining > 0)
{
return true;
}
else
{
//Reset the number of buffers to calculate and the timestamp
mDcCalculationsRemaining = DC_CALCULATIONS_PER_INTERVAL;
mLastDcCalculationTimestamp = System.currentTimeMillis();
}
}
return false;
}
/**
* Calculates the average DC in the sample stream so that it can be subtracted from the samples when the
* native buffer is used.
@@ -46,8 +110,8 @@ public class SignedByteNativeBufferFactory implements INativeBufferFactory
*/
private void calculateDc(byte[] samples)
{
double iDcAccumulator = 0;
double qDcAccumulator = 0;
float iDcAccumulator = 0;
float qDcAccumulator = 0;
for(int x = 0; x < samples.length; x += 2)
{
@@ -64,5 +128,10 @@ public class SignedByteNativeBufferFactory implements INativeBufferFactory
qDcAccumulator /= 128.0f;
qDcAccumulator -= mQAverageDc;
mQAverageDc += (qDcAccumulator * DC_FILTER_GAIN);
if(Math.abs(iDcAccumulator) < TARGET_DC_OFFSET_REMAINING && Math.abs(qDcAccumulator) < TARGET_DC_OFFSET_REMAINING)
{
mDcCalculationsRemaining--;
}
}
}

View File

@@ -19,6 +19,7 @@
package io.github.dsheirer.buffer.airspy;
import io.github.dsheirer.buffer.DcCorrectionManager;
import java.nio.ByteBuffer;
/**
@@ -26,40 +27,61 @@ import java.nio.ByteBuffer;
*/
public class ScalarPackedSampleConverter implements IAirspySampleConverter
{
private static final float DC_FILTER_GAIN = 0.007f; //Normalizes DC over period of ~142 (1 / .007) buffers
private float mAverageDc = 0.0f;
/**
* Manages DC calculations and processing interval
*/
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
@Override
public short[] convert(ByteBuffer buffer)
{
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
int offset = 0;
long dcAccumulator = 0;
short sample;
short[] samples;
byte b1, b2, b3;
samples = new short[buffer.capacity() / 3 * 2];
for(int x = 0; x < samples.length; x += 2)
if(shouldCalculateDc)
{
b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
b3 = buffer.get(offset++);
long dcAccumulator = 0;
sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
samples[x] = sample;
dcAccumulator += sample;
for(int x = 0; x < samples.length; x += 2)
{
b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
b3 = buffer.get(offset++);
sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF));
samples[x] = sample;
dcAccumulator += sample;
sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
samples[x] = sample;
dcAccumulator += sample;
sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF));
samples[x] = sample;
dcAccumulator += sample;
}
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
mDcCalculationManager.adjust(averageDcNow);
}
else
{
for(int x = 0; x < samples.length; x += 2)
{
b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
b3 = buffer.get(offset++);
//Calculate the average scaled DC offset so that it can be applied in the native buffer's converted samples
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
averageDcNow -= mAverageDc;
mAverageDc += (averageDcNow * DC_FILTER_GAIN);
sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
samples[x] = sample;
sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF));
samples[x] = sample;
}
}
return samples;
}
@@ -67,7 +89,7 @@ public class ScalarPackedSampleConverter implements IAirspySampleConverter
@Override
public float getAverageDc()
{
return mAverageDc;
return mDcCalculationManager.getAverageDc();
}
public static void main(String[] args)

View File

@@ -19,6 +19,7 @@
package io.github.dsheirer.buffer.airspy;
import io.github.dsheirer.buffer.DcCorrectionManager;
import java.nio.ByteBuffer;
/**
@@ -26,34 +27,50 @@ import java.nio.ByteBuffer;
*/
public class ScalarUnpackedSampleConverter implements IAirspySampleConverter
{
private static final float DC_FILTER_GAIN = 0.007f; //Normalizes DC over period of ~142 (1 / .007) buffers
private float mAverageDc = 0.0f;
/**
* Manages DC calculations and processing interval
*/
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
@Override
public short[] convert(ByteBuffer buffer)
{
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
int offset = 0;
long dcAccumulator = 0;
short sample;
short[] samples;
byte b1, b2;
samples = new short[buffer.capacity() / 2];
for(int x = 0; x < samples.length; x++)
if(shouldCalculateDc)
{
b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[x] = sample;
dcAccumulator += sample;
}
long dcAccumulator = 0;
//Calculate the average scaled DC offset so that it can be applied in the native buffer's converted samples
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
averageDcNow -= mAverageDc;
mAverageDc += (averageDcNow * DC_FILTER_GAIN);
for(int x = 0; x < samples.length; x++)
{
b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[x] = sample;
dcAccumulator += sample;
}
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
mDcCalculationManager.adjust(averageDcNow);
}
else
{
for(int x = 0; x < samples.length; x++)
{
b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[x] = sample;
}
}
return samples;
}
@@ -61,6 +78,6 @@ public class ScalarUnpackedSampleConverter implements IAirspySampleConverter
@Override
public float getAverageDc()
{
return mAverageDc;
return mDcCalculationManager.getAverageDc();
}
}

View File

@@ -19,25 +19,29 @@
package io.github.dsheirer.buffer.airspy;
import io.github.dsheirer.buffer.DcCorrectionManager;
import java.nio.ByteBuffer;
import jdk.incubator.vector.ShortVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
import java.nio.ByteBuffer;
/**
* Scalar implementation of airspy sample converter for un-packed samples.
*/
public class VectorUnpackedSampleConverter implements IAirspySampleConverter
{
private static final VectorSpecies<Short> VECTOR_SPECIES = ShortVector.SPECIES_PREFERRED;
private static final float DC_FILTER_GAIN = 0.007f; //Normalizes DC over period of ~142 (1 / .007) buffers
private float mAverageDc = 0.0f;
/**
* Manages DC calculations and processing interval
*/
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
@Override
public short[] convert(ByteBuffer buffer)
{
long dcAccumulator = 0;
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
short sample;
short[] samples;
byte b1, b2;
@@ -53,44 +57,73 @@ public class VectorUnpackedSampleConverter implements IAirspySampleConverter
short[] bytes1 = new short[VECTOR_SPECIES.length()];
short[] bytes2 = new short[VECTOR_SPECIES.length()];
for(; samplesOffset < VECTOR_SPECIES.loopBound(samples.length); samplesOffset += VECTOR_SPECIES.length())
if(shouldCalculateDc)
{
bytesOffset = 0;
long dcAccumulator = 0;
while(bytesOffset < VECTOR_SPECIES.length())
for(; samplesOffset < VECTOR_SPECIES.loopBound(samples.length); samplesOffset += VECTOR_SPECIES.length())
{
bytes1[bytesOffset] = (short)(buffer.get(rawPointer++) & 0xFF);
bytes2[bytesOffset++] = (short)(buffer.get(rawPointer++) & 0x0F);
bytesOffset = 0;
while(bytesOffset < VECTOR_SPECIES.length())
{
bytes1[bytesOffset] = (short)(buffer.get(rawPointer++) & 0xFF);
bytes2[bytesOffset++] = (short)(buffer.get(rawPointer++) & 0x0F);
}
vector = ShortVector.fromArray(VECTOR_SPECIES, bytes1, 0)
.or(ShortVector.fromArray(VECTOR_SPECIES, bytes2, 0).lanewise(VectorOperators.LSHL, 8));
dcAccumulator += vector.reduceLanes(VectorOperators.ADD);
vector.intoArray(samples, samplesOffset);
}
vector = ShortVector.fromArray(VECTOR_SPECIES, bytes1, 0)
.or(ShortVector.fromArray(VECTOR_SPECIES, bytes2, 0).lanewise(VectorOperators.LSHL, 8));
dcAccumulator += vector.reduceLanes(VectorOperators.ADD);
vector.intoArray(samples, samplesOffset);
}
for(; samplesOffset < samples.length; samplesOffset++)
{
b1 = buffer.get(rawPointer++);
b2 = buffer.get(rawPointer++);
for(; samplesOffset < samples.length; samplesOffset++)
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[samplesOffset] = sample;
dcAccumulator += sample;
}
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
mDcCalculationManager.adjust(averageDcNow);
}
else
{
b1 = buffer.get(rawPointer++);
b2 = buffer.get(rawPointer++);
for(; samplesOffset < VECTOR_SPECIES.loopBound(samples.length); samplesOffset += VECTOR_SPECIES.length())
{
bytesOffset = 0;
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[samplesOffset] = sample;
dcAccumulator += sample;
while(bytesOffset < VECTOR_SPECIES.length())
{
bytes1[bytesOffset] = (short)(buffer.get(rawPointer++) & 0xFF);
bytes2[bytesOffset++] = (short)(buffer.get(rawPointer++) & 0x0F);
}
vector = ShortVector.fromArray(VECTOR_SPECIES, bytes1, 0)
.or(ShortVector.fromArray(VECTOR_SPECIES, bytes2, 0).lanewise(VectorOperators.LSHL, 8));
vector.intoArray(samples, samplesOffset);
}
for(; samplesOffset < samples.length; samplesOffset++)
{
b1 = buffer.get(rawPointer++);
b2 = buffer.get(rawPointer++);
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[samplesOffset] = sample;
}
}
//Calculate the average scaled DC offset so that it can be applied in the native buffer's converted samples
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
averageDcNow -= mAverageDc;
mAverageDc += (averageDcNow * DC_FILTER_GAIN);
return samples;
}
@Override
public float getAverageDc()
{
return mAverageDc;
return mDcCalculationManager.getAverageDc();
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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
@@ -14,21 +14,20 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
* ****************************************************************************
*/
package io.github.dsheirer.module.log;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.util.TimeStamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class EventLogger extends Module
{
@@ -78,9 +77,6 @@ public abstract class EventLogger extends Module
sb.append(mFileNameSuffix);
mLogFileName = sb.toString();
mLog.info("Creating log file:" + mLogFileName);
mLogFile = new OutputStreamWriter(new FileOutputStream(mLogFileName));
write(getHeader());

View File

@@ -1,26 +1,26 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.settings;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import java.awt.*;
import java.awt.Color;
public class ColorSetting extends Setting
{
@@ -91,7 +91,7 @@ public class ColorSetting extends Setting
return new Color(temp.getRed(),
temp.getGreen(),
temp.getBlue(),
mAlpha);
getColorSettingName().getTranslucency());
}
public void setColor(Color color)
@@ -111,84 +111,81 @@ public class ColorSetting extends Setting
public enum ColorSettingName
{
CHANNEL_CONFIG(getTranslucent(Color.LIGHT_GRAY, CONFIG_TRANSLUCENCY),
"Channel", "Channel Color"),
CHANNEL_CONFIG_PROCESSING(getTranslucent(Color.GREEN, CONFIG_TRANSLUCENCY),
"Channel Processing", "Processing Channel Color"),
CHANNEL_CONFIG_SELECTED(getTranslucent(Color.BLUE, CONFIG_TRANSLUCENCY),
"Channel Selected", "Selected Channel Color"),
CHANNEL_STATE_BACKGROUND(Color.BLACK, "Background",
"Channel State Background"),
CHANNEL_STATE_GRADIENT_TOP_CALL(Color.BLACK,
"Call Gradient Top", "Channel Call State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_CALL(Color.BLUE,
"Call Gradient Middle", "Channel Call State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_CONTROL(Color.BLACK,
"Control Gradient Top", "Channel Control State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL(new Color(0xC64F00),
"Control Gradient Middle", "Channel Control State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_DATA(Color.BLACK,
"Data Gradient Top", "Channel Data State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_DATA(new Color(0xCC00CC),
"Data Gradient Middle", "Channel Data State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_FADE(Color.BLACK,
"Fade Gradient Top", "Channel Fade State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_FADE(Color.DARK_GRAY,
"Fade Gradient Middle", "Channel Fade State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_IDLE(Color.BLACK,
"Idle Gradient Top", "Channel Idle State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_IDLE(Color.DARK_GRAY,
"Idle Gradient Middle", "Channel Idle State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_NO_TUNER(Color.RED,
"No Tuner Gradient Top", "Channel No Tuner State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER(new Color(0x990000),
"No Tuner Gradient Middle", "Channel No Tuner State Gradient Middle"),
CHANNEL_STATE_LABEL_DETAILS(Color.LIGHT_GRAY,
"Details", "Details Label Color"),
CHANNEL_STATE_LABEL_DECODER(Color.GREEN,
"Decoder", "Decoder Label Color"),
CHANNEL_STATE_LABEL_AUX_DECODER(Color.YELLOW,
"Aux Decoder", "Aux Decoder Label Color"),
CHANNEL_STATE_SELECTED_CHANNEL(Color.YELLOW,
"Selected Channel Indicator", "Selected Channel Indicator Color"),
SPECTRUM_BACKGROUND(Color.BLACK,
"Background", "Spectrum Background Color"),
SPECTRUM_CURSOR(Color.ORANGE,
"Cursor", "Spectrum Cursor Color"),
SPECTRUM_GRADIENT_BOTTOM(getTranslucent(Color.GREEN, SPECTRUM_TRANSLUCENCY),
"Gradient Bottom", "Spectrum Gradient Bottom Color"),
SPECTRUM_GRADIENT_TOP(getTranslucent(Color.WHITE, SPECTRUM_TRANSLUCENCY),
"Gradient Top", "Spectrum Gradient Top Color"),
SPECTRUM_LINE(getTranslucent(Color.LIGHT_GRAY, SPECTRUM_TRANSLUCENCY),
"Line", "Spectrum Lines and Text Color"),
UNKNOWN(Color.RED, "Unknown", "Unknown Setting Color");
CHANNEL_CONFIG(Color.LIGHT_GRAY, CONFIG_TRANSLUCENCY, "Channel", "Channel Color"),
CHANNEL_CONFIG_PROCESSING(Color.GREEN, CONFIG_TRANSLUCENCY, "Channel Processing", "Processing Channel Color"),
CHANNEL_CONFIG_SELECTED(Color.BLUE, CONFIG_TRANSLUCENCY, "Channel Selected", "Selected Channel Color"),
CHANNEL_STATE_BACKGROUND(Color.BLACK, NO_TRANSLUCENCY, "Background", "Channel State Background"),
CHANNEL_STATE_GRADIENT_TOP_CALL(Color.BLACK, NO_TRANSLUCENCY, "Call Gradient Top", "Channel Call State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_CALL(Color.BLUE, NO_TRANSLUCENCY, "Call Gradient Middle", "Channel Call State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_CONTROL(Color.BLACK, NO_TRANSLUCENCY, "Control Gradient Top", "Channel Control State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL(new Color(0xC64F00), NO_TRANSLUCENCY, "Control Gradient Middle", "Channel Control State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_DATA(Color.BLACK, NO_TRANSLUCENCY, "Data Gradient Top", "Channel Data State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_DATA(new Color(0xCC00CC), NO_TRANSLUCENCY, "Data Gradient Middle", "Channel Data State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_FADE(Color.BLACK, NO_TRANSLUCENCY, "Fade Gradient Top", "Channel Fade State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_FADE(Color.DARK_GRAY, NO_TRANSLUCENCY, "Fade Gradient Middle", "Channel Fade State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_IDLE(Color.BLACK, NO_TRANSLUCENCY, "Idle Gradient Top", "Channel Idle State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_IDLE(Color.DARK_GRAY, NO_TRANSLUCENCY, "Idle Gradient Middle", "Channel Idle State Gradient Middle"),
CHANNEL_STATE_GRADIENT_TOP_NO_TUNER(Color.RED, NO_TRANSLUCENCY, "No Tuner Gradient Top", "Channel No Tuner State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER(new Color(0x990000), NO_TRANSLUCENCY, "No Tuner Gradient Middle", "Channel No Tuner State Gradient Middle"),
CHANNEL_STATE_LABEL_DETAILS(Color.LIGHT_GRAY, NO_TRANSLUCENCY, "Details", "Details Label Color"),
CHANNEL_STATE_LABEL_DECODER(Color.GREEN, NO_TRANSLUCENCY, "Decoder", "Decoder Label Color"),
CHANNEL_STATE_LABEL_AUX_DECODER(Color.YELLOW, NO_TRANSLUCENCY, "Aux Decoder", "Aux Decoder Label Color"),
CHANNEL_STATE_SELECTED_CHANNEL(Color.YELLOW, NO_TRANSLUCENCY, "Selected Channel Indicator", "Selected Channel Indicator Color"),
SPECTRUM_BACKGROUND(Color.BLACK, NO_TRANSLUCENCY, "Background", "Spectrum Background Color"),
SPECTRUM_CURSOR(Color.ORANGE, NO_TRANSLUCENCY, "Cursor", "Spectrum Cursor Color"),
SPECTRUM_GRADIENT_BOTTOM(Color.GREEN, SPECTRUM_TRANSLUCENCY, "Gradient Bottom", "Spectrum Gradient Bottom Color"),
SPECTRUM_GRADIENT_TOP(Color.WHITE, SPECTRUM_TRANSLUCENCY, "Gradient Top", "Spectrum Gradient Top Color"),
SPECTRUM_LINE(Color.LIGHT_GRAY, SPECTRUM_TRANSLUCENCY, "Line", "Spectrum Lines and Text Color"),
UNKNOWN(Color.RED, NO_TRANSLUCENCY,"Unknown", "Unknown Setting Color");
private Color mDefaultColor;
private int mTranslucency;
private String mLabel;
private String mDialogTitle;
private ColorSettingName(Color defaultColor,
String label,
String dialogTitle)
/**
* Enumeration of color names used in user interface, primarily in the spectral displays.
* @param defaultColor to use and for resets.
* @param translucency value for the color.
* @param label for menu items
* @param dialogTitle for selecting in the color chooser.
*/
ColorSettingName(Color defaultColor, int translucency, String label, String dialogTitle)
{
mDefaultColor = defaultColor;
mTranslucency = translucency;
mLabel = label;
mDialogTitle = dialogTitle;
}
/**
* Default color to use when resetting to default.
*/
public Color getDefaultColor()
{
return mDefaultColor;
}
/**
* Translucency to apply to the color
*/
public int getTranslucency()
{
return mTranslucency;
}
/**
* Label for the setting
*/
public String getLabel()
{
return mLabel;
}
/**
* Title to use in dialogs
* @return title
*/
public String getDialogTitle()
{
return mDialogTitle;

View File

@@ -197,7 +197,7 @@ public class SourceEvent
/**
* Creates a new locked state change event
*/
public static SourceEvent lockedState()
public static SourceEvent lockedSampleRateState()
{
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED, 1);
}
@@ -205,7 +205,7 @@ public class SourceEvent
/**
* Creates a new unlocked state change event
*/
public static SourceEvent unlockedState()
public static SourceEvent unlockedSampleRateState()
{
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED, 0);
}

View File

@@ -36,11 +36,11 @@ import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
import io.github.dsheirer.source.tuner.frequency.FrequencyController;
import io.github.dsheirer.source.tuner.frequency.FrequencyController.Tunable;
import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.SortedSet;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener,
INativeBufferProvider, Listener<INativeBuffer>, ITunerErrorListener
@@ -70,6 +70,17 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
mFrequencyErrorCorrectionManager = new FrequencyErrorCorrectionManager(this);
}
/**
* Lock for the frequency controller. This should only be used by the channel source manager to lock access to the
* frequency controller while creating a channel source, to block multi-threaded access to the frequency controller
* which might put the center tuned frequency value in an indeterminant state.
* @return frequency controller lock
*/
public ReentrantLock getFrequencyControllerLock()
{
return mFrequencyController.getFrequencyControllerLock();
}
/**
* Frequency correction manager for this tuner controller.
*/
@@ -182,29 +193,30 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
}
/**
* Indicates if the frequency and sample rate controls are locked by another process. User interface controls
* should monitor source events and check the locked state via this method to correctly render the enabled state
* of the frequency and sample rate controls.
* Indicates if the sample rate control is locked by another process. User interface controls should monitor source
* events and check the locked state via this method to correctly render the enabled state of the sample rate control.
*
* @return true if the tuner controller is locked.
*/
public boolean isLocked()
public boolean isLockedSampleRate()
{
return mFrequencyController.isLocked();
//Note: this access is not protected by the mFrequencyControllerLock
return mFrequencyController.isSampleRateLocked();
}
/**
* Sets the frequency and sample rate locked state to the locked argument value. This should only be changed
* by a downstream consumer of samples to prevent users or other processes from modifying the center frequency
* and/or sample rate of the tuner while processing samples.
* Sets the sample rate locked state to the locked argument value. This should only be changed by a downstream
* consumer of samples to prevent users or other processes from modifying the sample rate of the tuner while
* processing samples.
*
* @param locked true to indicate the tuner controller is in a locked state, otherwise false.
*/
public void setLocked(boolean locked)
public void setLockedSampleRate(boolean locked)
{
try
{
mFrequencyController.setLocked(locked);
//Note: this access is not protected by the mFrequencyControllerLock
mFrequencyController.setSampleRateLocked(locked);
}
catch(SourceException se)
{
@@ -214,6 +226,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
public int getBandwidth()
{
//Note: this access is not protected by the mFrequencyControllerLock
return mFrequencyController.getBandwidth();
}
@@ -225,7 +238,15 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public void setFrequency(long frequency) throws SourceException
{
mFrequencyController.setFrequency(frequency);
try
{
getFrequencyControllerLock().lock();
mFrequencyController.setFrequency(frequency);
}
finally
{
getFrequencyControllerLock().unlock();
}
}
/**
@@ -235,28 +256,73 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public long getFrequency()
{
return mFrequencyController.getFrequency();
long frequency;
try
{
getFrequencyControllerLock().lock();
frequency = mFrequencyController.getFrequency();
}
finally
{
getFrequencyControllerLock().unlock();
}
return frequency;
}
@Override
public boolean canTune(long frequency)
{
return mFrequencyController.canTune(frequency);
boolean canTune;
try
{
getFrequencyControllerLock().lock();
canTune = mFrequencyController.canTune(frequency);
}
finally
{
getFrequencyControllerLock().unlock();
}
return canTune;
}
public double getSampleRate()
{
//Note: this access is not protected by the mFrequencyControllerLock
return mFrequencyController.getSampleRate();
}
public double getFrequencyCorrection()
{
return mFrequencyController.getFrequencyCorrection();
double correction;
try
{
getFrequencyControllerLock().lock();
correction = mFrequencyController.getFrequencyCorrection();
}
finally
{
getFrequencyControllerLock().unlock();
}
return correction;
}
public void setFrequencyCorrection(double correction) throws SourceException
{
mFrequencyController.setFrequencyCorrection(correction);
try
{
getFrequencyControllerLock().lock();
mFrequencyController.setFrequencyCorrection(correction);
}
finally
{
getFrequencyControllerLock().unlock();
}
}
/**
@@ -265,7 +331,19 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public long getMinimumFrequency()
{
return mFrequencyController.getMinimumFrequency();
long minimum;
try
{
getFrequencyControllerLock().lock();
minimum = mFrequencyController.getMinimumFrequency();
}
finally
{
getFrequencyControllerLock().unlock();
}
return minimum;
}
/**
@@ -274,7 +352,15 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public void setMinimumFrequency(long minimum)
{
mFrequencyController.setMinimumFrequency(minimum);
try
{
getFrequencyControllerLock().lock();
mFrequencyController.setMinimumFrequency(minimum);
}
finally
{
getFrequencyControllerLock().unlock();
}
}
/**
@@ -283,7 +369,19 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public long getMaximumFrequency()
{
return mFrequencyController.getMaximumFrequency();
long maximum;
try
{
getFrequencyControllerLock().lock();
maximum = mFrequencyController.getMaximumFrequency();
}
finally
{
getFrequencyControllerLock().unlock();
}
return maximum;
}
/**
@@ -292,17 +390,49 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public void setMaximumFrequency(long maximum)
{
mFrequencyController.setMaximumFrequency(maximum);
try
{
getFrequencyControllerLock().lock();
mFrequencyController.setMaximumFrequency(maximum);
}
finally
{
getFrequencyControllerLock().unlock();
}
}
public long getMinTunedFrequency() throws SourceException
{
return mFrequencyController.getFrequency() - (getUsableBandwidth() / 2);
long minTuned;
try
{
getFrequencyControllerLock().lock();
minTuned = mFrequencyController.getFrequency() - (getUsableBandwidth() / 2);
}
finally
{
getFrequencyControllerLock().unlock();
}
return minTuned;
}
public long getMaxTunedFrequency() throws SourceException
{
return mFrequencyController.getFrequency() + (getUsableBandwidth() / 2);
long maxTuned;
try
{
getFrequencyControllerLock().lock();
maxTuned = mFrequencyController.getFrequency() + (getUsableBandwidth() / 2);
}
finally
{
getFrequencyControllerLock().unlock();
}
return maxTuned;
}
/**
@@ -425,7 +555,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public void addListener( ISourceEventProcessor processor )
{
mFrequencyController.addListener(processor);
mFrequencyController.addSourceEventProcessor(processor);
}
/**
@@ -433,7 +563,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public void removeListener( ISourceEventProcessor processor )
{
mFrequencyController.removeFrequencyChangeProcessor(processor);
mFrequencyController.removeSourceEventProcessor(processor);
}
/**

View File

@@ -25,6 +25,9 @@ import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.source.tuner.manager.TunerStatus;
import io.github.dsheirer.source.tuner.ui.TunerEditor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,9 +43,6 @@ import javax.swing.JSlider;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
/**
* Airspy tuner editor/controller
@@ -102,7 +102,7 @@ public class AirspyTunerEditor extends TunerEditor<AirspyTuner, AirspyTunerConfi
getTunerStatusLabel().setText(status);
getButtonPanel().updateControls();
getFrequencyPanel().updateControls();
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked());
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
getTunerInfoButton().setEnabled(hasTuner());
updateGainComponents((hasTuner() && hasConfiguration()) ? getConfiguration().getGain() : null);
@@ -626,7 +626,7 @@ public class AirspyTunerEditor extends TunerEditor<AirspyTuner, AirspyTunerConfi
*/
private void updateSampleRateToolTip()
{
if(hasTuner() && getTuner().getController().isLocked())
if(hasTuner() && getTuner().getController().isLockedSampleRate())
{
mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
}

View File

@@ -22,12 +22,11 @@ import io.github.dsheirer.source.ISourceEventProcessor;
import io.github.dsheirer.source.InvalidFrequencyException;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FrequencyController
{
@@ -40,8 +39,12 @@ public class FrequencyController
private long mMaximumFrequency = 0;
private double mFrequencyCorrection = 0.0d;
private double mSampleRate = 0.0d;
private boolean mLocked = false;
private boolean mSampleRateLocked = false;
private List<ISourceEventProcessor> mProcessors = new ArrayList<>();
/**
* Lock protecting access to frequency control plane, source event processor subscriptions and event broadcasting
*/
private ReentrantLock mLock = new ReentrantLock();
/**
@@ -53,6 +56,15 @@ public class FrequencyController
mTunable = tunable;
}
/**
* Lock for controlling access to frequency control plane, event processor subscriptions and source event broadcasts.
*/
public ReentrantLock getFrequencyControllerLock()
{
return mLock;
}
/**
* Prepare for disposal of this instance.
*/
@@ -63,36 +75,36 @@ public class FrequencyController
}
/**
* Indicates if this frequency controller is locked by a remote process. A locked state indicates that neither
* the frequency nor the sample rate should be changed. The remote process will typically be a downstream
* Indicates if the sample rate for this frequency controller is locked by a remote process. A locked state
* indicates that the sample rate cannot be changed. The remote process will typically be a downstream
* consumer of the data produced by the device being controlled (ie polyphase channelizer) that does not want
* the sample rate or frequency to be changed while processing is ongoing.
* the sample rate to be changed while processing (ie channels have been allocated) is ongoing.
*
* @return true if locked or false if not locked.
*/
public boolean isLocked()
public boolean isSampleRateLocked()
{
return mLocked;
return mSampleRateLocked;
}
/**
* Sets the lock state for this frequency controller. Set to true when a consumer process requires that the
* frequency and sample rate controls be locked so that only the singular consumer process makes changes. This
* sample rate control be locked so that only the single consumer process makes changes to the sample rate. This
* is primarily to lock down user interface controls when the polyphase channelizer is producing channels.
*
* @param locked state
* @param sampleRateLocked state
*/
public void setLocked(boolean locked) throws SourceException
public void setSampleRateLocked(boolean sampleRateLocked) throws SourceException
{
mLocked = locked;
mSampleRateLocked = sampleRateLocked;
if(mLocked)
if(mSampleRateLocked)
{
broadcast(SourceEvent.lockedState());
broadcast(SourceEvent.lockedSampleRateState());
}
else
{
broadcast(SourceEvent.unlockedState());
broadcast(SourceEvent.unlockedSampleRateState());
}
}
@@ -111,7 +123,7 @@ public class FrequencyController
{
if(sampleRate != mSampleRate)
{
if(!mLocked)
if(!mSampleRateLocked)
{
mSampleRate = sampleRate;
@@ -284,7 +296,7 @@ public class FrequencyController
/**
* Adds listener to receive frequency change events
*/
public void addListener(ISourceEventProcessor processor)
public void addSourceEventProcessor(ISourceEventProcessor processor)
{
mLock.lock();
@@ -304,7 +316,7 @@ public class FrequencyController
/**
* Removes listener from receiving frequency change events
*/
public void removeFrequencyChangeProcessor(ISourceEventProcessor processor)
public void removeSourceEventProcessor(ISourceEventProcessor processor)
{
mLock.lock();

View File

@@ -120,7 +120,7 @@ public class HackRFTunerEditor extends TunerEditor<HackRFTuner,HackRFTunerConfig
getTunerStatusLabel().setText(status);
getButtonPanel().updateControls();
getFrequencyPanel().updateControls();
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked());
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
updateSampleRateToolTip();
getTunerInfoButton().setEnabled(hasTuner());
@@ -295,7 +295,7 @@ public class HackRFTunerEditor extends TunerEditor<HackRFTuner,HackRFTunerConfig
*/
private void updateSampleRateToolTip()
{
if(hasTuner() && getTuner().getController().isLocked())
if(hasTuner() && getTuner().getController().isLockedSampleRate())
{
mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
}

View File

@@ -93,38 +93,48 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
return null;
}
if(CenterFrequencyCalculator.canTune(tunerChannel, mTunerController, mTunerChannels))
TunerChannelSource source = null;
try
{
try
mTunerController.getFrequencyControllerLock().lock();
if(CenterFrequencyCalculator.canTune(tunerChannel, mTunerController, mTunerChannels))
{
//Attempt to create the channel source first, in case we get a filter design exception
HalfBandTunerChannelSource tunerChannelSource = new HalfBandTunerChannelSource(mChannelSourceEventProcessor,
tunerChannel, mTunerController.getSampleRate(), channelSpecification);
try
{
//Attempt to create the channel source first, in case we get a filter design exception
HalfBandTunerChannelSource tunerChannelSource = new HalfBandTunerChannelSource(mChannelSourceEventProcessor,
tunerChannel, mTunerController.getSampleRate(), channelSpecification);
//Add to the list of channel sources so that it will receive the tuner frequency change
mChannelSources.add(tunerChannelSource);
//Add to the list of channel sources so that it will receive the tuner frequency change
mChannelSources.add(tunerChannelSource);
//Set the current tuner frequency
tunerChannelSource.setFrequency(mTunerController.getFrequency());
//Set the current tuner frequency
tunerChannelSource.setFrequency(mTunerController.getFrequency());
//Add to the channel list and update the tuner center frequency as needed
mTunerChannels.add(tunerChannel);
updateTunerFrequency();
//Add to the channel list and update the tuner center frequency as needed
mTunerChannels.add(tunerChannel);
updateTunerFrequency();
//Lock the tuner controller frequency and sample rate
mTunerController.setLocked(true);
//Lock the tuner controller frequency and sample rate
mTunerController.setLockedSampleRate(true);
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
return tunerChannelSource;
}
catch(FilterDesignException fde)
{
mLog.error("Error creating CIC tuner channel source - couldn't design cleanup filter", fde);
source = tunerChannelSource;
}
catch(FilterDesignException fde)
{
mLog.error("Error creating CIC tuner channel source - couldn't design cleanup filter", fde);
}
}
}
finally
{
mTunerController.getFrequencyControllerLock().unlock();
}
return null;
return source;
}
@Override
@@ -294,7 +304,7 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
//Unlock the tuner controller if there are no more channels
if(getTunerChannelCount() == 0)
{
mTunerController.setLocked(false);
mTunerController.setLockedSampleRate(false);
}
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
}

View File

@@ -82,18 +82,30 @@ public class PassThroughSourceManager extends ChannelSourceManager
@Override
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
{
if(!mRunning)
{
return null;
}
PassThroughChannelSource channelSource = new PassThroughChannelSource(new SourceEventProxy(),
mTunerController, tunerChannel);
TunerChannelSource source = null;
mTunerChannels.add(tunerChannel);
mTunerChannelSources.add(channelSource);
try
{
mTunerController.getFrequencyControllerLock().lock();
PassThroughChannelSource channelSource = new PassThroughChannelSource(new SourceEventProxy(),
mTunerController, tunerChannel);
return channelSource;
mTunerChannels.add(tunerChannel);
mTunerChannelSources.add(channelSource);
source = channelSource;
}
finally
{
mTunerController.getFrequencyControllerLock().unlock();
}
return source;
}
@Override

View File

@@ -25,12 +25,11 @@ import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import java.util.SortedSet;
import org.apache.commons.math3.util.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.SortedSet;
/**
* PolyphaseChannelSourceManager is responsible for managing the tuner's center tuned frequency and providing access to
* polyphase tuner channel sources (ie DDCs). This class is responsible for determining IF a requested channel can
@@ -416,48 +415,56 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
@Override
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
{
if(isTunable(tunerChannel))
TunerChannelSource tunerChannelSource = null;
try
{
//Get a new set of currently tuned channels
SortedSet<TunerChannel> tunerChannels = getTunerChannels();
//Add the requested channel to the list
tunerChannels.add(tunerChannel);
if(canTune(tunerChannels))
mTunerController.getFrequencyControllerLock().lock();
if(isTunable(tunerChannel))
{
long currentCenterFrequency = mTunerController.getFrequency();
long updatedCenterFrequency = 0;
//Get a new set of currently tuned channels
SortedSet<TunerChannel> tunerChannels = getTunerChannels();
//Attempt to adjust the center frequency before we allocate the channel
try
//Add the requested channel to the list
tunerChannels.add(tunerChannel);
if(canTune(tunerChannels))
{
updatedCenterFrequency = getCenterFrequency(tunerChannels, currentCenterFrequency);
long currentCenterFrequency = mTunerController.getFrequency();
long updatedCenterFrequency = 0;
if(updatedCenterFrequency != currentCenterFrequency && updatedCenterFrequency != 0)
//Attempt to adjust the center frequency before we allocate the channel
try
{
mTunerController.setFrequency(updatedCenterFrequency);
}
updatedCenterFrequency = getCenterFrequency(tunerChannels, currentCenterFrequency);
//If we're successful to here, allocate the channel
return mPolyphaseChannelManager.getChannel(tunerChannel);
}
catch(SourceException se)
{
//Tuner controller threw an error trying to tune to the updated frequency
mLog.error("Error while updating tuner controller with new center frequency [" +
updatedCenterFrequency + "] - unable to allocate new tuner channel", se);
}
catch(IllegalArgumentException iae)
{
//Center frequency calculation failed
// mLog.debug("Couldn't calculate new tuner center frequency for new tuner channel [" +
// tunerChannel + "] unable to allocate new tuner channel");
if(updatedCenterFrequency != currentCenterFrequency && updatedCenterFrequency != 0)
{
mTunerController.setFrequency(updatedCenterFrequency);
}
//If we're successful to here, allocate the channel
tunerChannelSource = mPolyphaseChannelManager.getChannel(tunerChannel);
}
catch(SourceException se)
{
//Tuner controller threw an error trying to tune to the updated frequency
mLog.error("Error while updating tuner controller with new center frequency [" +
updatedCenterFrequency + "] - unable to allocate new tuner channel", se);
}
catch(IllegalArgumentException iae)
{
//Center frequency calculation failed
}
}
}
}
finally
{
mTunerController.getFrequencyControllerLock().unlock();
}
return null;
return tunerChannelSource;
}
/**
@@ -474,7 +481,7 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
broadcast(sourceEvent);
//Lock the frequency and sample rate controls on the tuner controller so users can't change them
//when the polyphase manager has channels allocated
mTunerController.setLocked(getTunerChannelCount() > 0);
mTunerController.setLockedSampleRate(getTunerChannelCount() > 0);
break;
case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED:
//Rebroadcast these frequency measurement errors to the tuner and tuner controller

View File

@@ -29,6 +29,8 @@ import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KGain;
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KLNAGain;
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain;
import io.github.dsheirer.source.tuner.ui.TunerEditor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,8 +43,6 @@ import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.SpinnerNumberModel;
import javax.usb.UsbException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* E4000 tuner editor
@@ -383,7 +383,7 @@ public class E4KTunerEditor extends TunerEditor<RTL2832Tuner, E4KTunerConfigurat
*/
private void updateSampleRateToolTip()
{
if(hasTuner() && getTuner().getTunerController().isLocked())
if(hasTuner() && getTuner().getTunerController().isLockedSampleRate())
{
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
}

View File

@@ -416,7 +416,7 @@ public class R820TTunerEditor extends TunerEditor<RTL2832Tuner,R820TTunerConfigu
*/
private void updateSampleRateToolTip()
{
if(hasTuner() && getTuner().getTunerController().isLocked())
if(hasTuner() && getTuner().getTunerController().isLockedSampleRate())
{
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
}

View File

@@ -25,10 +25,6 @@ import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
import io.github.dsheirer.source.tuner.manager.DiscoveredUSBTuner;
import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener;
import io.github.dsheirer.source.tuner.manager.TunerStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
import java.awt.EventQueue;
import java.text.DecimalFormat;
import java.util.ArrayList;
@@ -36,6 +32,10 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
/**
* Model for discovered tuners
@@ -449,7 +449,7 @@ public class DiscoveredTunerModel extends AbstractTableModel implements Listener
if(discoveredTuner.hasTuner())
{
int channelCount = discoveredTuner.getTuner().getChannelSourceManager().getTunerChannelCount();
return channelCount + " (" + (discoveredTuner.getTuner().getTunerController().isLocked() ? "LOCKED)" : "UNLOCKED)");
return channelCount + " (" + (discoveredTuner.getTuner().getTunerController().isLockedSampleRate() ? "LOCKED)" : "UNLOCKED)");
}
else
{

View File

@@ -619,8 +619,8 @@ public abstract class TunerEditor<T extends Tuner,C extends TunerConfiguration>
{
getFrequencyControl().clearListeners();
getFrequencyControl().addListener(mFrequencyAndCorrectionChangeListener);
getFrequencyControl().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked());
getTunerLockedStatusLabel().setVisible(hasTuner() && getTuner().getTunerController().isLocked());
getFrequencyControl().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
getTunerLockedStatusLabel().setVisible(hasTuner() && getTuner().getTunerController().isLockedSampleRate());
getFrequencyCorrectionSpinner().setEnabled(hasTuner());
getAutoPPMCheckBox().setEnabled(hasTuner());

View File

@@ -143,7 +143,7 @@ public class TunerViewPanel extends JPanel
if(selectedTuner != null && selectedTuner.hasTuner() && tunerEvent.getTuner() == selectedTuner.getTuner())
{
mDiscoveredTunerEditor.setTunerLockState(selectedTuner.getTuner().getTunerController().isLocked());
mDiscoveredTunerEditor.setTunerLockState(selectedTuner.getTuner().getTunerController().isLockedSampleRate());
}
}
}

View File

@@ -1,7 +1,6 @@
<!--
~ *******************************************************************************
~ sdrtrunk
~ Copyright (C) 2014-2018 Dennis Sheirer
~ ******************************************************************************
~ Copyright (C) 2014-2022 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
@@ -15,7 +14,7 @@
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>
~ ******************************************************************************
~ *****************************************************************************
-->
<configuration>
<conversionRule conversionWord="memory_usage" converterClass="io.github.dsheirer.util.MemoryUsageLogger" />
@@ -24,7 +23,7 @@
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-12(%d{HH:mm:ss.SSS}) %-5level %logger{25} - %msg %memory_usage%n</pattern>
<pattern>%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) %-5level %logger{25} - %msg %memory_usage%n</pattern>
</encoder>
</appender>