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"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" /> <file type="web" url="file://$PROJECT_DIR$" />
</component> </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" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

View File

@@ -28,7 +28,7 @@ repositories {
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
} }
version = '0.5.0' version = '0.5.0-beta6'
sourceCompatibility = '17' sourceCompatibility = '17'
sourceSets { sourceSets {
@@ -43,10 +43,10 @@ test {
dependencies { dependencies {
// JUnit Tests // JUnit Tests
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.13.2'
testImplementation 'org.assertj:assertj-core:3.8.0' testImplementation 'org.assertj:assertj-core:3.23.1'
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0") testImplementation('org.junit.jupiter:junit-jupiter-api:5.9.0')
testImplementation 'org.mockito:mockito-core:3.+' testImplementation 'org.mockito:mockito-core:4.8.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
@@ -56,33 +56,33 @@ dependencies {
implementation 'com.github.nwaldispuehl:java-lame:v3.98.4' implementation 'com.github.nwaldispuehl:java-lame:v3.98.4'
//mavenCentral/jcenter imports //mavenCentral/jcenter imports
implementation 'ch.qos.logback:logback-classic:1.2.3' implementation 'ch.qos.logback:logback-classic:1.4.4'
implementation 'ch.qos.logback:logback-core:1.2.3' implementation 'ch.qos.logback:logback-core:1.4.4'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.2' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.14.0'
implementation 'com.fazecast:jSerialComm:2.5.0' implementation 'com.fazecast:jSerialComm:2.9.2'
implementation 'com.github.jiconfont:jiconfont-font_awesome:4.7.0.1' 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-javafx:1.0.0'
implementation 'com.github.jiconfont:jiconfont-swing:1.0.1' implementation 'com.github.jiconfont:jiconfont-swing:1.0.1'
implementation 'com.github.wendykierp:JTransforms:3.1' implementation 'com.github.wendykierp:JTransforms:3.1'
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.10'
implementation 'com.google.guava:guava:27.0.1-jre' implementation 'com.google.guava:guava:31.1-jre'
implementation 'com.jidesoft:jide-oss:3.6.18' 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 '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 'eu.hansolo:charts:1.0.5'
implementation 'io.github.dsheirer:radio-reference-api:15.1.8' implementation 'io.github.dsheirer:radio-reference-api:15.1.8'
implementation 'javax.usb:usb-api:1.0.2' implementation 'javax.usb:usb-api:1.0.2'
implementation 'net.coderazzi:tablefilter-swing:5.4.0' implementation 'net.coderazzi:tablefilter-swing:5.5.4'
implementation 'org.apache.commons:commons-compress:1.20' implementation 'org.apache.commons:commons-compress:1.21'
implementation 'org.apache.commons:commons-lang3:3.8.1' implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'org.apache.commons:commons-math3:3.6.1' implementation 'org.apache.commons:commons-math3:3.6.1'
implementation 'org.apache.commons:commons-csv:1.9.0' implementation 'org.apache.commons:commons-csv:1.9.0'
implementation 'org.apache.mina:mina-core:2.1.3' implementation 'org.apache.mina:mina-core:2.2.1'
implementation 'org.apache.mina:mina-http:2.1.3' implementation 'org.apache.mina:mina-http:2.2.1'
implementation 'org.controlsfx:controlsfx:11.1.0' implementation 'org.controlsfx:controlsfx:11.1.2'
implementation 'org.rauschig:jarchivelib:1.0.0' implementation 'org.rauschig:jarchivelib:1.2.0'
implementation 'org.slf4j:slf4j-api:1.7.25' implementation 'org.slf4j:slf4j-api:2.0.3'
implementation 'org.usb4java:libusb4java:1.3.0' implementation 'org.usb4java:libusb4java:1.3.0'
implementation 'org.usb4java:usb4java:1.3.0' implementation 'org.usb4java:usb4java:1.3.0'
implementation 'org.usb4java:usb4java-javax: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. * 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_base = '/home/denny/java_jdks/'
String jdk_linux_aarch64 = jdk_base + 'linux-arm64/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-18.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-18.0.1-full.jdk' String jdk_osx_x86_64 = jdk_base + 'osx-x64/jdk-19.0.1-full.jdk'
String jdk_osx_aarch64 = jdk_base + 'osx-arm64/jdk-18.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-18.0.1-full' 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() || 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() 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("Building all runtime images")
println("The [ > jre: ] build phase downloads several target JDKs and this may take a while ... please wait") println("The [ > jre: ] build phase downloads several target JDKs and this may take a while ... please wait")
targetPlatform(targetLinuxAarch64) { 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) { 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) { 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) { 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) { 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 //Build using pre-downloaded JDKs

Binary file not shown.

View File

@@ -19,6 +19,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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"); # This program is free software: you can redistribute it and/or modify
# you may not use this file except in compliance with the License. # it under the terms of the GNU General Public License as published by
# You may obtain a copy of the License at # 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 # You should have received a copy of the GNU General Public License
# distributed under the License is distributed on an "AS IS" BASIS, # along with this program. If not, see <http://www.gnu.org/licenses/>
# 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.
# #
############################################################################## ##############################################################################
## #
## 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 # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" 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. # 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"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 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." location of your Java installation."
fi fi
else 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. 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 Please set the JAVA_HOME variable in your environment to match the
@@ -105,79 +143,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
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" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=`save "$@"` # * --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 # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 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" "$@" exec "$JAVACMD" "$@"

25
gradlew.bat vendored
View File

@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% 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. @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" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 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_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -61,28 +64,14 @@ echo location of your Java installation.
goto fail 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 :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @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 :end
@rem End local scope for the variables with windows NT shell @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.ComplexSamples;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples; import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import java.util.Iterator; import java.util.Iterator;
/** /**
@@ -42,7 +41,7 @@ public class ByteNativeBuffer implements INativeBuffer
for(int x = 0; x < 256; x++) 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 public class ByteNativeBufferFactory implements INativeBufferFactory
{ {
private static final float DC_FILTER_GAIN = 0.007f; //Normalizes DC over period of ~142 (1 / .007) buffers private DcCorrectionManager mDcCorrectionManager = new DcCorrectionManager();
private float mAverageDc = 0.0f;
@Override @Override
public INativeBuffer getBuffer(ByteBuffer samples, long timestamp) public INativeBuffer getBuffer(ByteBuffer samples, long timestamp)
{ {
byte[] copy = new byte[samples.capacity()]; byte[] copy = new byte[samples.capacity()];
samples.get(copy); 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) private void calculateDc(byte[] samples)
{ {
double dcAccumulator = 0; float dcAccumulator = 0;
for(byte sample: samples) for(byte sample: samples)
{ {
@@ -53,9 +57,8 @@ public class ByteNativeBufferFactory implements INativeBufferFactory
} }
dcAccumulator /= samples.length; dcAccumulator /= samples.length;
dcAccumulator -= 127.0f; dcAccumulator -= 127.5f;
dcAccumulator /= 128.0f; dcAccumulator /= 128.0f;
dcAccumulator -= mAverageDc; mDcCorrectionManager.adjust(dcAccumulator);
mAverageDc += (dcAccumulator * DC_FILTER_GAIN);
} }
} }

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; package io.github.dsheirer.buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
/** /**
* Implements a factory for creating SignedByteNativeBuffer instances * Implements a factory for creating SignedByteNativeBuffer instances
*/ */
public class SignedByteNativeBufferFactory implements INativeBufferFactory 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; private float mIAverageDc = 0.0f;
/**
* Current DC offset correction for Quadrature samples
*/
private float mQAverageDc = 0.0f; private float mQAverageDc = 0.0f;
@Override @Override
@@ -35,10 +72,37 @@ public class SignedByteNativeBufferFactory implements INativeBufferFactory
{ {
byte[] copy = new byte[samples.capacity()]; byte[] copy = new byte[samples.capacity()];
samples.get(copy); samples.get(copy);
calculateDc(copy);
if(shouldCalculateDc())
{
calculateDc(copy);
}
return new SignedByteNativeBuffer(copy, timestamp, mIAverageDc, mQAverageDc); 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 * Calculates the average DC in the sample stream so that it can be subtracted from the samples when the
* native buffer is used. * native buffer is used.
@@ -46,8 +110,8 @@ public class SignedByteNativeBufferFactory implements INativeBufferFactory
*/ */
private void calculateDc(byte[] samples) private void calculateDc(byte[] samples)
{ {
double iDcAccumulator = 0; float iDcAccumulator = 0;
double qDcAccumulator = 0; float qDcAccumulator = 0;
for(int x = 0; x < samples.length; x += 2) for(int x = 0; x < samples.length; x += 2)
{ {
@@ -64,5 +128,10 @@ public class SignedByteNativeBufferFactory implements INativeBufferFactory
qDcAccumulator /= 128.0f; qDcAccumulator /= 128.0f;
qDcAccumulator -= mQAverageDc; qDcAccumulator -= mQAverageDc;
mQAverageDc += (qDcAccumulator * DC_FILTER_GAIN); 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; package io.github.dsheirer.buffer.airspy;
import io.github.dsheirer.buffer.DcCorrectionManager;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
@@ -26,40 +27,61 @@ import java.nio.ByteBuffer;
*/ */
public class ScalarPackedSampleConverter implements IAirspySampleConverter 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 @Override
public short[] convert(ByteBuffer buffer) public short[] convert(ByteBuffer buffer)
{ {
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
int offset = 0; int offset = 0;
long dcAccumulator = 0;
short sample; short sample;
short[] samples; short[] samples;
byte b1, b2, b3; byte b1, b2, b3;
samples = new short[buffer.capacity() / 3 * 2]; samples = new short[buffer.capacity() / 3 * 2];
for(int x = 0; x < samples.length; x += 2) if(shouldCalculateDc)
{ {
b1 = buffer.get(offset++); long dcAccumulator = 0;
b2 = buffer.get(offset++);
b3 = buffer.get(offset++);
sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4)); for(int x = 0; x < samples.length; x += 2)
samples[x] = sample; {
dcAccumulator += sample; b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
b3 = buffer.get(offset++);
sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF)); sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
samples[x] = sample; samples[x] = sample;
dcAccumulator += 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 sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f; samples[x] = sample;
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
averageDcNow -= mAverageDc; sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF));
mAverageDc += (averageDcNow * DC_FILTER_GAIN); samples[x] = sample;
}
}
return samples; return samples;
} }
@@ -67,7 +89,7 @@ public class ScalarPackedSampleConverter implements IAirspySampleConverter
@Override @Override
public float getAverageDc() public float getAverageDc()
{ {
return mAverageDc; return mDcCalculationManager.getAverageDc();
} }
public static void main(String[] args) public static void main(String[] args)

View File

@@ -19,6 +19,7 @@
package io.github.dsheirer.buffer.airspy; package io.github.dsheirer.buffer.airspy;
import io.github.dsheirer.buffer.DcCorrectionManager;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
@@ -26,34 +27,50 @@ import java.nio.ByteBuffer;
*/ */
public class ScalarUnpackedSampleConverter implements IAirspySampleConverter 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 @Override
public short[] convert(ByteBuffer buffer) public short[] convert(ByteBuffer buffer)
{ {
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
int offset = 0; int offset = 0;
long dcAccumulator = 0;
short sample; short sample;
short[] samples; short[] samples;
byte b1, b2; byte b1, b2;
samples = new short[buffer.capacity() / 2]; samples = new short[buffer.capacity() / 2];
for(int x = 0; x < samples.length; x++) if(shouldCalculateDc)
{ {
b1 = buffer.get(offset++); long dcAccumulator = 0;
b2 = buffer.get(offset++);
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[x] = sample;
dcAccumulator += sample;
}
//Calculate the average scaled DC offset so that it can be applied in the native buffer's converted samples for(int x = 0; x < samples.length; x++)
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f; {
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT; b1 = buffer.get(offset++);
averageDcNow -= mAverageDc; b2 = buffer.get(offset++);
mAverageDc += (averageDcNow * DC_FILTER_GAIN); 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; return samples;
} }
@@ -61,6 +78,6 @@ public class ScalarUnpackedSampleConverter implements IAirspySampleConverter
@Override @Override
public float getAverageDc() public float getAverageDc()
{ {
return mAverageDc; return mDcCalculationManager.getAverageDc();
} }
} }

View File

@@ -19,25 +19,29 @@
package io.github.dsheirer.buffer.airspy; 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.ShortVector;
import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies; import jdk.incubator.vector.VectorSpecies;
import java.nio.ByteBuffer;
/** /**
* Scalar implementation of airspy sample converter for un-packed samples. * Scalar implementation of airspy sample converter for un-packed samples.
*/ */
public class VectorUnpackedSampleConverter implements IAirspySampleConverter public class VectorUnpackedSampleConverter implements IAirspySampleConverter
{ {
private static final VectorSpecies<Short> VECTOR_SPECIES = ShortVector.SPECIES_PREFERRED; 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 @Override
public short[] convert(ByteBuffer buffer) public short[] convert(ByteBuffer buffer)
{ {
long dcAccumulator = 0; boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
short sample; short sample;
short[] samples; short[] samples;
byte b1, b2; byte b1, b2;
@@ -53,44 +57,73 @@ public class VectorUnpackedSampleConverter implements IAirspySampleConverter
short[] bytes1 = new short[VECTOR_SPECIES.length()]; short[] bytes1 = new short[VECTOR_SPECIES.length()];
short[] bytes2 = 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); bytesOffset = 0;
bytes2[bytesOffset++] = (short)(buffer.get(rawPointer++) & 0x0F);
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) for(; samplesOffset < samples.length; samplesOffset++)
.or(ShortVector.fromArray(VECTOR_SPECIES, bytes2, 0).lanewise(VectorOperators.LSHL, 8)); {
dcAccumulator += vector.reduceLanes(VectorOperators.ADD); b1 = buffer.get(rawPointer++);
vector.intoArray(samples, samplesOffset); 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++); for(; samplesOffset < VECTOR_SPECIES.loopBound(samples.length); samplesOffset += VECTOR_SPECIES.length())
b2 = buffer.get(rawPointer++); {
bytesOffset = 0;
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF)); while(bytesOffset < VECTOR_SPECIES.length())
samples[samplesOffset] = sample; {
dcAccumulator += sample; 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; return samples;
} }
@Override @Override
public float getAverageDc() 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 * 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 * 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 * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> * along with this program. If not, see <http://www.gnu.org/licenses/>
* * ****************************************************************************
******************************************************************************/ */
package io.github.dsheirer.module.log; package io.github.dsheirer.module.log;
import io.github.dsheirer.module.Module; import io.github.dsheirer.module.Module;
import io.github.dsheirer.util.TimeStamp; import io.github.dsheirer.util.TimeStamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Writer; import java.io.Writer;
import java.nio.file.Path; import java.nio.file.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class EventLogger extends Module public abstract class EventLogger extends Module
{ {
@@ -78,9 +77,6 @@ public abstract class EventLogger extends Module
sb.append(mFileNameSuffix); sb.append(mFileNameSuffix);
mLogFileName = sb.toString(); mLogFileName = sb.toString();
mLog.info("Creating log file:" + mLogFileName);
mLogFile = new OutputStreamWriter(new FileOutputStream(mLogFileName)); mLogFile = new OutputStreamWriter(new FileOutputStream(mLogFileName));
write(getHeader()); 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 * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public 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/> * along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/ * ****************************************************************************
*/
package io.github.dsheirer.settings; package io.github.dsheirer.settings;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import java.awt.Color;
import java.awt.*;
public class ColorSetting extends Setting public class ColorSetting extends Setting
{ {
@@ -91,7 +91,7 @@ public class ColorSetting extends Setting
return new Color(temp.getRed(), return new Color(temp.getRed(),
temp.getGreen(), temp.getGreen(),
temp.getBlue(), temp.getBlue(),
mAlpha); getColorSettingName().getTranslucency());
} }
public void setColor(Color color) public void setColor(Color color)
@@ -111,84 +111,81 @@ public class ColorSetting extends Setting
public enum ColorSettingName public enum ColorSettingName
{ {
CHANNEL_CONFIG(getTranslucent(Color.LIGHT_GRAY, CONFIG_TRANSLUCENCY), CHANNEL_CONFIG(Color.LIGHT_GRAY, CONFIG_TRANSLUCENCY, "Channel", "Channel Color"),
"Channel", "Channel Color"), CHANNEL_CONFIG_PROCESSING(Color.GREEN, CONFIG_TRANSLUCENCY, "Channel Processing", "Processing Channel Color"),
CHANNEL_CONFIG_PROCESSING(getTranslucent(Color.GREEN, CONFIG_TRANSLUCENCY), CHANNEL_CONFIG_SELECTED(Color.BLUE, CONFIG_TRANSLUCENCY, "Channel Selected", "Selected Channel Color"),
"Channel Processing", "Processing Channel Color"), CHANNEL_STATE_BACKGROUND(Color.BLACK, NO_TRANSLUCENCY, "Background", "Channel State Background"),
CHANNEL_CONFIG_SELECTED(getTranslucent(Color.BLUE, CONFIG_TRANSLUCENCY), CHANNEL_STATE_GRADIENT_TOP_CALL(Color.BLACK, NO_TRANSLUCENCY, "Call Gradient Top", "Channel Call State Gradient Top"),
"Channel Selected", "Selected Channel Color"), 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_BACKGROUND(Color.BLACK, "Background", CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL(new Color(0xC64F00), NO_TRANSLUCENCY, "Control Gradient Middle", "Channel Control State Gradient Middle"),
"Channel State Background"), CHANNEL_STATE_GRADIENT_TOP_DATA(Color.BLACK, NO_TRANSLUCENCY, "Data Gradient Top", "Channel Data State Gradient Top"),
CHANNEL_STATE_GRADIENT_TOP_CALL(Color.BLACK, CHANNEL_STATE_GRADIENT_MIDDLE_DATA(new Color(0xCC00CC), NO_TRANSLUCENCY, "Data Gradient Middle", "Channel Data State Gradient Middle"),
"Call Gradient Top", "Channel Call State Gradient Top"), CHANNEL_STATE_GRADIENT_TOP_FADE(Color.BLACK, NO_TRANSLUCENCY, "Fade Gradient Top", "Channel Fade State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_CALL(Color.BLUE, CHANNEL_STATE_GRADIENT_MIDDLE_FADE(Color.DARK_GRAY, NO_TRANSLUCENCY, "Fade Gradient Middle", "Channel Fade State Gradient Middle"),
"Call Gradient Middle", "Channel Call State Gradient Middle"), CHANNEL_STATE_GRADIENT_TOP_IDLE(Color.BLACK, NO_TRANSLUCENCY, "Idle Gradient Top", "Channel Idle State Gradient Top"),
CHANNEL_STATE_GRADIENT_TOP_CONTROL(Color.BLACK, CHANNEL_STATE_GRADIENT_MIDDLE_IDLE(Color.DARK_GRAY, NO_TRANSLUCENCY, "Idle Gradient Middle", "Channel Idle State Gradient Middle"),
"Control Gradient Top", "Channel Control State Gradient Top"), CHANNEL_STATE_GRADIENT_TOP_NO_TUNER(Color.RED, NO_TRANSLUCENCY, "No Tuner Gradient Top", "Channel No Tuner State Gradient Top"),
CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL(new Color(0xC64F00), CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER(new Color(0x990000), NO_TRANSLUCENCY, "No Tuner Gradient Middle", "Channel No Tuner State Gradient Middle"),
"Control Gradient Middle", "Channel Control State Gradient Middle"), CHANNEL_STATE_LABEL_DETAILS(Color.LIGHT_GRAY, NO_TRANSLUCENCY, "Details", "Details Label Color"),
CHANNEL_STATE_GRADIENT_TOP_DATA(Color.BLACK, CHANNEL_STATE_LABEL_DECODER(Color.GREEN, NO_TRANSLUCENCY, "Decoder", "Decoder Label Color"),
"Data Gradient Top", "Channel Data State Gradient Top"), CHANNEL_STATE_LABEL_AUX_DECODER(Color.YELLOW, NO_TRANSLUCENCY, "Aux Decoder", "Aux Decoder Label Color"),
CHANNEL_STATE_GRADIENT_MIDDLE_DATA(new Color(0xCC00CC), CHANNEL_STATE_SELECTED_CHANNEL(Color.YELLOW, NO_TRANSLUCENCY, "Selected Channel Indicator", "Selected Channel Indicator Color"),
"Data Gradient Middle", "Channel Data State Gradient Middle"), SPECTRUM_BACKGROUND(Color.BLACK, NO_TRANSLUCENCY, "Background", "Spectrum Background Color"),
CHANNEL_STATE_GRADIENT_TOP_FADE(Color.BLACK, SPECTRUM_CURSOR(Color.ORANGE, NO_TRANSLUCENCY, "Cursor", "Spectrum Cursor Color"),
"Fade Gradient Top", "Channel Fade State Gradient Top"), SPECTRUM_GRADIENT_BOTTOM(Color.GREEN, SPECTRUM_TRANSLUCENCY, "Gradient Bottom", "Spectrum Gradient Bottom Color"),
CHANNEL_STATE_GRADIENT_MIDDLE_FADE(Color.DARK_GRAY, SPECTRUM_GRADIENT_TOP(Color.WHITE, SPECTRUM_TRANSLUCENCY, "Gradient Top", "Spectrum Gradient Top Color"),
"Fade Gradient Middle", "Channel Fade State Gradient Middle"), SPECTRUM_LINE(Color.LIGHT_GRAY, SPECTRUM_TRANSLUCENCY, "Line", "Spectrum Lines and Text Color"),
CHANNEL_STATE_GRADIENT_TOP_IDLE(Color.BLACK, UNKNOWN(Color.RED, NO_TRANSLUCENCY,"Unknown", "Unknown Setting Color");
"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");
private Color mDefaultColor; private Color mDefaultColor;
private int mTranslucency;
private String mLabel; private String mLabel;
private String mDialogTitle; private String mDialogTitle;
private ColorSettingName(Color defaultColor, /**
String label, * Enumeration of color names used in user interface, primarily in the spectral displays.
String dialogTitle) * @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; mDefaultColor = defaultColor;
mTranslucency = translucency;
mLabel = label; mLabel = label;
mDialogTitle = dialogTitle; mDialogTitle = dialogTitle;
} }
/**
* Default color to use when resetting to default.
*/
public Color getDefaultColor() public Color getDefaultColor()
{ {
return mDefaultColor; return mDefaultColor;
} }
/**
* Translucency to apply to the color
*/
public int getTranslucency()
{
return mTranslucency;
}
/**
* Label for the setting
*/
public String getLabel() public String getLabel()
{ {
return mLabel; return mLabel;
} }
/**
* Title to use in dialogs
* @return title
*/
public String getDialogTitle() public String getDialogTitle()
{ {
return mDialogTitle; return mDialogTitle;

View File

@@ -197,7 +197,7 @@ public class SourceEvent
/** /**
* Creates a new locked state change event * 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); 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 * 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); 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;
import io.github.dsheirer.source.tuner.frequency.FrequencyController.Tunable; import io.github.dsheirer.source.tuner.frequency.FrequencyController.Tunable;
import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager; import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.SortedSet; 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, public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener,
INativeBufferProvider, Listener<INativeBuffer>, ITunerErrorListener INativeBufferProvider, Listener<INativeBuffer>, ITunerErrorListener
@@ -70,6 +70,17 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
mFrequencyErrorCorrectionManager = new FrequencyErrorCorrectionManager(this); 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. * 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 * Indicates if the sample rate control is locked by another process. User interface controls should monitor source
* should monitor source events and check the locked state via this method to correctly render the enabled state * events and check the locked state via this method to correctly render the enabled state of the sample rate control.
* of the frequency and sample rate controls.
* *
* @return true if the tuner controller is locked. * @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 * Sets the sample rate locked state to the locked argument value. This should only be changed by a downstream
* by a downstream consumer of samples to prevent users or other processes from modifying the center frequency * consumer of samples to prevent users or other processes from modifying the sample rate of the tuner while
* and/or sample rate of the tuner while processing samples. * processing samples.
* *
* @param locked true to indicate the tuner controller is in a locked state, otherwise false. * @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 try
{ {
mFrequencyController.setLocked(locked); //Note: this access is not protected by the mFrequencyControllerLock
mFrequencyController.setSampleRateLocked(locked);
} }
catch(SourceException se) catch(SourceException se)
{ {
@@ -214,6 +226,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
public int getBandwidth() public int getBandwidth()
{ {
//Note: this access is not protected by the mFrequencyControllerLock
return mFrequencyController.getBandwidth(); return mFrequencyController.getBandwidth();
} }
@@ -225,7 +238,15 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/ */
public void setFrequency(long frequency) throws SourceException 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() public long getFrequency()
{ {
return mFrequencyController.getFrequency(); long frequency;
try
{
getFrequencyControllerLock().lock();
frequency = mFrequencyController.getFrequency();
}
finally
{
getFrequencyControllerLock().unlock();
}
return frequency;
} }
@Override @Override
public boolean canTune(long frequency) 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() public double getSampleRate()
{ {
//Note: this access is not protected by the mFrequencyControllerLock
return mFrequencyController.getSampleRate(); return mFrequencyController.getSampleRate();
} }
public double getFrequencyCorrection() 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 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() 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) 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() 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) public void setMaximumFrequency(long maximum)
{ {
mFrequencyController.setMaximumFrequency(maximum); try
{
getFrequencyControllerLock().lock();
mFrequencyController.setMaximumFrequency(maximum);
}
finally
{
getFrequencyControllerLock().unlock();
}
} }
public long getMinTunedFrequency() throws SourceException 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 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 ) 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 ) 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.TunerManager;
import io.github.dsheirer.source.tuner.manager.TunerStatus; import io.github.dsheirer.source.tuner.manager.TunerStatus;
import io.github.dsheirer.source.tuner.ui.TunerEditor; 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 net.miginfocom.swing.MigLayout;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -40,9 +43,6 @@ import javax.swing.JSlider;
import javax.swing.SpinnerNumberModel; import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
/** /**
* Airspy tuner editor/controller * Airspy tuner editor/controller
@@ -102,7 +102,7 @@ public class AirspyTunerEditor extends TunerEditor<AirspyTuner, AirspyTunerConfi
getTunerStatusLabel().setText(status); getTunerStatusLabel().setText(status);
getButtonPanel().updateControls(); getButtonPanel().updateControls();
getFrequencyPanel().updateControls(); getFrequencyPanel().updateControls();
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked()); getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
getTunerInfoButton().setEnabled(hasTuner()); getTunerInfoButton().setEnabled(hasTuner());
updateGainComponents((hasTuner() && hasConfiguration()) ? getConfiguration().getGain() : null); updateGainComponents((hasTuner() && hasConfiguration()) ? getConfiguration().getGain() : null);
@@ -626,7 +626,7 @@ public class AirspyTunerEditor extends TunerEditor<AirspyTuner, AirspyTunerConfi
*/ */
private void updateSampleRateToolTip() private void updateSampleRateToolTip()
{ {
if(hasTuner() && getTuner().getController().isLocked()) if(hasTuner() && getTuner().getController().isLockedSampleRate())
{ {
mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); 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.InvalidFrequencyException;
import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.SourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FrequencyController public class FrequencyController
{ {
@@ -40,8 +39,12 @@ public class FrequencyController
private long mMaximumFrequency = 0; private long mMaximumFrequency = 0;
private double mFrequencyCorrection = 0.0d; private double mFrequencyCorrection = 0.0d;
private double mSampleRate = 0.0d; private double mSampleRate = 0.0d;
private boolean mLocked = false; private boolean mSampleRateLocked = false;
private List<ISourceEventProcessor> mProcessors = new ArrayList<>(); 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(); private ReentrantLock mLock = new ReentrantLock();
/** /**
@@ -53,6 +56,15 @@ public class FrequencyController
mTunable = tunable; 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. * 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 * Indicates if the sample rate for this frequency controller is locked by a remote process. A locked state
* the frequency nor the sample rate should be changed. The remote process will typically be a downstream * 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 * 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. * @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 * 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. * 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 else
{ {
broadcast(SourceEvent.unlockedState()); broadcast(SourceEvent.unlockedSampleRateState());
} }
} }
@@ -111,7 +123,7 @@ public class FrequencyController
{ {
if(sampleRate != mSampleRate) if(sampleRate != mSampleRate)
{ {
if(!mLocked) if(!mSampleRateLocked)
{ {
mSampleRate = sampleRate; mSampleRate = sampleRate;
@@ -284,7 +296,7 @@ public class FrequencyController
/** /**
* Adds listener to receive frequency change events * Adds listener to receive frequency change events
*/ */
public void addListener(ISourceEventProcessor processor) public void addSourceEventProcessor(ISourceEventProcessor processor)
{ {
mLock.lock(); mLock.lock();
@@ -304,7 +316,7 @@ public class FrequencyController
/** /**
* Removes listener from receiving frequency change events * Removes listener from receiving frequency change events
*/ */
public void removeFrequencyChangeProcessor(ISourceEventProcessor processor) public void removeSourceEventProcessor(ISourceEventProcessor processor)
{ {
mLock.lock(); mLock.lock();

View File

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

View File

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

View File

@@ -82,18 +82,30 @@ public class PassThroughSourceManager extends ChannelSourceManager
@Override @Override
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification) public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
{ {
if(!mRunning) if(!mRunning)
{ {
return null; return null;
} }
PassThroughChannelSource channelSource = new PassThroughChannelSource(new SourceEventProxy(), TunerChannelSource source = null;
mTunerController, tunerChannel);
mTunerChannels.add(tunerChannel); try
mTunerChannelSources.add(channelSource); {
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 @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.ChannelSpecification;
import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource; import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import java.util.SortedSet;
import org.apache.commons.math3.util.FastMath; import org.apache.commons.math3.util.FastMath;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.SortedSet;
/** /**
* PolyphaseChannelSourceManager is responsible for managing the tuner's center tuned frequency and providing access to * 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 * 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 @Override
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification) public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
{ {
if(isTunable(tunerChannel)) TunerChannelSource tunerChannelSource = null;
try
{ {
//Get a new set of currently tuned channels mTunerController.getFrequencyControllerLock().lock();
SortedSet<TunerChannel> tunerChannels = getTunerChannels(); if(isTunable(tunerChannel))
//Add the requested channel to the list
tunerChannels.add(tunerChannel);
if(canTune(tunerChannels))
{ {
long currentCenterFrequency = mTunerController.getFrequency(); //Get a new set of currently tuned channels
long updatedCenterFrequency = 0; SortedSet<TunerChannel> tunerChannels = getTunerChannels();
//Attempt to adjust the center frequency before we allocate the channel //Add the requested channel to the list
try 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 if(updatedCenterFrequency != currentCenterFrequency && updatedCenterFrequency != 0)
return mPolyphaseChannelManager.getChannel(tunerChannel); {
} mTunerController.setFrequency(updatedCenterFrequency);
catch(SourceException se) }
{
//Tuner controller threw an error trying to tune to the updated frequency //If we're successful to here, allocate the channel
mLog.error("Error while updating tuner controller with new center frequency [" + tunerChannelSource = mPolyphaseChannelManager.getChannel(tunerChannel);
updatedCenterFrequency + "] - unable to allocate new tuner channel", se); }
} catch(SourceException se)
catch(IllegalArgumentException iae) {
{ //Tuner controller threw an error trying to tune to the updated frequency
//Center frequency calculation failed mLog.error("Error while updating tuner controller with new center frequency [" +
// mLog.debug("Couldn't calculate new tuner center frequency for new tuner channel [" + updatedCenterFrequency + "] - unable to allocate new tuner channel", se);
// tunerChannel + "] unable to allocate new tuner channel"); }
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); broadcast(sourceEvent);
//Lock the frequency and sample rate controls on the tuner controller so users can't change them //Lock the frequency and sample rate controls on the tuner controller so users can't change them
//when the polyphase manager has channels allocated //when the polyphase manager has channels allocated
mTunerController.setLocked(getTunerChannelCount() > 0); mTunerController.setLockedSampleRate(getTunerChannelCount() > 0);
break; break;
case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED: case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED:
//Rebroadcast these frequency measurement errors to the tuner and tuner controller //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.E4KLNAGain;
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain; import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain;
import io.github.dsheirer.source.tuner.ui.TunerEditor; import io.github.dsheirer.source.tuner.ui.TunerEditor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -41,8 +43,6 @@ import javax.swing.JOptionPane;
import javax.swing.JSeparator; import javax.swing.JSeparator;
import javax.swing.SpinnerNumberModel; import javax.swing.SpinnerNumberModel;
import javax.usb.UsbException; import javax.usb.UsbException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/** /**
* E4000 tuner editor * E4000 tuner editor
@@ -383,7 +383,7 @@ public class E4KTunerEditor extends TunerEditor<RTL2832Tuner, E4KTunerConfigurat
*/ */
private void updateSampleRateToolTip() private void updateSampleRateToolTip()
{ {
if(hasTuner() && getTuner().getTunerController().isLocked()) if(hasTuner() && getTuner().getTunerController().isLockedSampleRate())
{ {
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); 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() private void updateSampleRateToolTip()
{ {
if(hasTuner() && getTuner().getTunerController().isLocked()) if(hasTuner() && getTuner().getTunerController().isLockedSampleRate())
{ {
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); 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.DiscoveredUSBTuner;
import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener; import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener;
import io.github.dsheirer.source.tuner.manager.TunerStatus; 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.awt.EventQueue;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
@@ -36,6 +32,10 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
/** /**
* Model for discovered tuners * Model for discovered tuners
@@ -449,7 +449,7 @@ public class DiscoveredTunerModel extends AbstractTableModel implements Listener
if(discoveredTuner.hasTuner()) if(discoveredTuner.hasTuner())
{ {
int channelCount = discoveredTuner.getTuner().getChannelSourceManager().getTunerChannelCount(); int channelCount = discoveredTuner.getTuner().getChannelSourceManager().getTunerChannelCount();
return channelCount + " (" + (discoveredTuner.getTuner().getTunerController().isLocked() ? "LOCKED)" : "UNLOCKED)"); return channelCount + " (" + (discoveredTuner.getTuner().getTunerController().isLockedSampleRate() ? "LOCKED)" : "UNLOCKED)");
} }
else else
{ {

View File

@@ -619,8 +619,8 @@ public abstract class TunerEditor<T extends Tuner,C extends TunerConfiguration>
{ {
getFrequencyControl().clearListeners(); getFrequencyControl().clearListeners();
getFrequencyControl().addListener(mFrequencyAndCorrectionChangeListener); getFrequencyControl().addListener(mFrequencyAndCorrectionChangeListener);
getFrequencyControl().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked()); getFrequencyControl().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
getTunerLockedStatusLabel().setVisible(hasTuner() && getTuner().getTunerController().isLocked()); getTunerLockedStatusLabel().setVisible(hasTuner() && getTuner().getTunerController().isLockedSampleRate());
getFrequencyCorrectionSpinner().setEnabled(hasTuner()); getFrequencyCorrectionSpinner().setEnabled(hasTuner());
getAutoPPMCheckBox().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()) 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-2022 Dennis Sheirer
~ Copyright (C) 2014-2018 Dennis Sheirer
~ ~
~ This program is free software: you can redistribute it and/or modify ~ 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 ~ 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 ~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/> ~ along with this program. If not, see <http://www.gnu.org/licenses/>
~ ****************************************************************************** ~ *****************************************************************************
--> -->
<configuration> <configuration>
<conversionRule conversionWord="memory_usage" converterClass="io.github.dsheirer.util.MemoryUsageLogger" /> <conversionRule conversionWord="memory_usage" converterClass="io.github.dsheirer.util.MemoryUsageLogger" />
@@ -24,7 +23,7 @@
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <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> </encoder>
</appender> </appender>