Compare commits

..

22 Commits

Author SHA1 Message Date
Denny Sheirer
02c62d012f #2557 DMR improved error detection in voice frames and voice events. (#2608)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-09 03:49:58 -05:00
Denny Sheirer
a4ec04a499 Version 1.0.0 beta 1 (#2607)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-08 10:04:34 -05:00
Denny Sheirer
006e382b34 #2604 jLibUSB library integration (#2605)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-08 09:56:09 -05:00
tadscottsmith
beb2593dd9 Allow Streaming Configuration Text Inputs to Expand (#2591) 2026-11-08 06:07:56 -05:00
Denny Sheirer
657cbf5c55 #2600 LimeSDR support (#2601)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-08 06:03:13 -05:00
Denny Sheirer
aff34fa5eb #2566 P25P1 Adjacent Status Broadcast message update. (#2602)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-08 05:50:30 -05:00
Denny Sheirer
22cd8968cb #1248 Frequency control doesn't allow mouse wheel to change value when locked (#2603)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-08 05:44:53 -05:00
Denny Sheirer
ec7efa4d17 #536 Add Bias-T support for Airspy, Airspy HF and Funcube Dongles. (#2599)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-08 04:05:58 -05:00
Denny Sheirer
07fd8c2923 #697 HackRF Bias T control. (#2598)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-07 07:09:54 -05:00
Denny Sheirer
a7301e93d9 #2596 Use jHackRF native wrapper library and remove unused USBTunerController code. (#2597)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-07 06:57:08 -05:00
Denny Sheirer
5912a958b3 #2594 Use jsdrplay library for RSP tuners and remove legacy implementation from the code base. (#2595)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-07 05:57:34 -05:00
Denny Sheirer
9ca4ae4d8f #2592 Use jAirspyHF library for AirspyHF tuners. (#2593)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-07 03:34:37 -05:00
Denny Sheirer
6cd2cea30b #2588 use jRTL driver library for RTL-SDR tuners and remove legacy custom java driver code (#2590)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-06 15:01:08 -05:00
Denny Sheirer
ba16c8b377 #2586 jHydraSDR v1.1.4 library update (#2587)
Co-authored-by: denny <dennis.sheirer@gmail.com>
2026-11-06 07:30:08 -05:00
Denny Sheirer
d72c5d87be #2277 Enhances playlist manager with accessibility features to better support assistive technology like screen readers on Windows and OSX. (#2583)
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
2026-11-05 07:38:17 -05:00
Denny Sheirer
dea3a3a41e #2580 Playlist Editor aliases tab identifier view now sorts identifiers by protocol and then numeric identifier value (#2581)
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
2026-11-05 06:11:59 -05:00
Denny Sheirer
48296990e9 #2578 GitHub workflows use latest component versions. Adds support for automatic releases on tag. Enhances the what's changed logic when selecting the previous version for the scope of the changes. (#2579)
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
2026-11-05 05:35:18 -05:00
Denny Sheirer
289150d415 #2577 NXDN Talkgroup Range accepts zero value and the display text for talkgroup zero no longer includes the 'NULL TALKGROUP' appendage. (#2577)
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
2026-11-05 04:54:32 -05:00
Denny Sheirer
69d49d9dd0 #2571 Use jAirspy library instead of legacy custom driver code (#2573)
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
2026-11-04 07:00:44 -05:00
Denny Sheirer
e8e813142b #2529 NXDN conventional repeater calls now consider talkgroup zero a valid TO identifier (#2572)
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
2026-11-04 03:29:55 -05:00
Denny Sheirer
968705a589 #2569 Support SDRSharp I/Q Baseband Recordings as Recording Tuners and as a user preference for tuner and channel baseband recordings. (#2570)
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
2026-11-03 06:48:20 -05:00
Denny Sheirer
1c097491d9 #2567 Reduces memory overhead by extending the JTransforms FFT package to reuse its float array when calculaing the inverse FFT in the channelizer for a subset of the FFT size operations. (#2568)
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
2026-11-03 05:49:40 -05:00
534 changed files with 10316 additions and 65990 deletions

View File

@@ -21,14 +21,14 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up JDK 27
uses: actions/setup-java@v3
uses: actions/setup-java@v6
with:
java-version: '27'
distribution: 'liberica'
java-package: 'jdk+fx'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6
- name: Build with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: clean build
run: ./gradlew clean build

View File

@@ -13,32 +13,31 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up JDK 27
uses: actions/setup-java@v3
uses: actions/setup-java@v6
with:
# Must match the JDK version downloaded in build.gradle (jdk_download_base) - jlink requires an exact version match
java-version: '27.0.0'
distribution: 'liberica'
java-package: 'jdk+fx'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6
- name: Build Linux and Mac releases with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: runtimeZipOthers -PprojectVersion=nightly
run: ./gradlew runtimeZipOthers -PprojectVersion=nightly
- name: Build Windows release with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: runtimeZipWindows -PprojectVersion=nightly
run: ./gradlew runtimeZipWindows -PprojectVersion=nightly
- name: Create Build Info file
run: |
echo -e "ref: $GITHUB_REF \ncommit: $GITHUB_SHA\nbuild: $(date +"%Y-%m-%dT%H:%M:%SZ")" > build/image/build_info.txt
- name: Add build artifacts to nightly release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: nightly
prerelease: true

130
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,130 @@
# Publishes a release when a version tag is pushed. The tag has to match
# projectVersion in gradle.properties with a leading 'v': v0.6.2 for a final
# release, or v0.6.2-beta-1 / v0.6.2-alpha-1 for a pre-release.
name: Release
on:
push:
tags: ['v[0-9]+.[0-9]+.[0-9]+*']
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
previous_tag: ${{ steps.previous.outputs.tag }}
steps:
- uses: actions/checkout@v7
with:
# Full history and tags, to find the previous release tag for the release notes
fetch-depth: 0
- id: version
run: |
expected="v$(sed -n 's/^projectVersion=//p' gradle.properties | tr -d '[:space:]')"
if [ "$GITHUB_REF_NAME" != "$expected" ]; then
echo "::error::Tag $GITHUB_REF_NAME does not match projectVersion in gradle.properties ($expected)"
exit 1
fi
echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
# Release notes list the PRs since the previous version tag (never 'nightly'). A final release
# starts from the previous final release, so it covers every alpha and beta in between.
- id: previous
run: |
exclude=(--exclude "$GITHUB_REF_NAME")
if [[ "$GITHUB_REF_NAME" != *-* ]]; then
exclude+=(--exclude '*-*')
fi
tag="$(git describe --tags --abbrev=0 --match 'v[0-9]*' "${exclude[@]}" HEAD 2>/dev/null || true)"
echo "Release notes start from: ${tag:-(GitHub default)}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Set up JDK 27
uses: actions/setup-java@v6
with:
# Must match the JDK version downloaded in build.gradle (jdk_download_base) - jlink requires an exact version match
java-version: '27.0.0'
distribution: 'liberica'
java-package: 'jdk+fx'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6
- name: Build and test
run: ./gradlew clean build --console=plain
# The runtime plugin can't build Windows and the other platforms in the same invocation.
- name: Build Linux and Mac releases
run: ./gradlew runtimeZipOthers --console=plain
- name: Build Windows release
run: ./gradlew runtimeZipWindows --console=plain
- name: Check release zips
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
missing=0
for platform in linux-aarch64 linux-x86_64 osx-aarch64 osx-x86_64 windows-aarch64 windows-x86_64; do
zip="build/image/sdr-trunk-${platform}-v${VERSION}.zip"
if [ ! -f "$zip" ]; then
echo "::error::Missing $zip"
missing=1
fi
done
ls -l build/image/*.zip
exit $missing
- uses: actions/upload-artifact@v7
with:
name: release-zips
path: build/image/*.zip
if-no-files-found: error
retention-days: 7
publish:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v8
with:
name: release-zips
path: zips
# Publishing is split into steps that can all be repeated, so a failed
# upload or a re-run finishes the draft left behind rather than failing on
# it: gh retries uploads, and a retry of an upload that had in fact
# succeeded fails with "ReleaseAsset.name already exists" unless clobbering.
- name: Create release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
VERSION: ${{ needs.build.outputs.version }}
PREVIOUS_TAG: ${{ needs.build.outputs.previous_tag }}
run: |
(cd zips && sha256sum *.zip > SHA256SUMS)
prerelease=()
if [[ "$GITHUB_REF_NAME" == *-* ]]; then
prerelease=(--prerelease)
fi
notes_start=()
if [ -n "$PREVIOUS_TAG" ]; then
notes_start=(--notes-start-tag "$PREVIOUS_TAG")
fi
if gh release view "$GITHUB_REF_NAME" --json isDraft --jq .isDraft 2>/dev/null | grep -qx false; then
echo "::error::Release $GITHUB_REF_NAME is already published"
exit 1
elif gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
echo "Reusing the draft release $GITHUB_REF_NAME"
else
gh release create "$GITHUB_REF_NAME" --draft "${prerelease[@]}" \
--verify-tag --generate-notes "${notes_start[@]}" --title "Version $VERSION"
fi
gh release upload "$GITHUB_REF_NAME" zips/* --clobber
gh release edit "$GITHUB_REF_NAME" --draft=false

View File

@@ -49,3 +49,28 @@ committed to the code base, so it's not really 'nightly' as much as it is 'curre
* **Operating System:** Windows (~~32 or~~ 64-bit), Linux (~~32 or~~ 64-bit) or Mac (64-bit, 12.x or higher)
* **CPU:** 4-core
* **RAM:** 8GB or more (preferred). Depending on usage, 4GB may be sufficient.
## Creating a Release (Maintainers)
Releases are built and published automatically by the [Release](.github/workflows/release.yml) GitHub workflow
whenever a version tag is pushed to the repository.
1. Update `projectVersion` in `gradle.properties` to the new version, following the naming convention described in
that file (e.g. `0.6.2-alpha-1`, `0.6.2-beta-1` or `0.6.2`).
2. Commit and push the change to `master`.
3. Create and push a tag that matches the version with a leading `v`:
```
git tag v0.6.2-beta-1
git push origin v0.6.2-beta-1
```
The workflow then:
* Verifies that the tag matches `projectVersion` in `gradle.properties` and fails if it does not.
* Builds and tests the code, then creates the release zips for Linux, Mac and Windows (x86-64 and aarch64).
* Creates a GitHub release titled `Version <version>` with auto-generated release notes (a "What's Changed" list of
the PRs merged since the previous version tag), and attaches the six zip
files plus a `SHA256SUMS` checksum file.
* Marks tags containing a dash (alpha and beta versions) as pre-releases. Tags without a dash become full releases.
If the workflow fails part way through, fix the problem and re-run it from the Actions tab. It resumes from the
draft release left behind. It won't modify a release that has already been published. To rebuild a published
release, delete the release and the tag on GitHub, then push the tag again.

View File

@@ -1,229 +0,0 @@
#ifndef SDRPLAY_API_H
#define SDRPLAY_API_H
#include "sdrplay_api_dev.h"
#include "sdrplay_api_rx_channel.h"
#include "sdrplay_api_callback.h"
//#if defined(_M_X64) || defined(_M_IX86)
//#include "windows.h"
//#elif defined (__GNUC__)
typedef void *HANDLE;
//#endif
//#ifndef _SDRPLAY_DLL_QUALIFIER
//#if !defined(STATIC_LIB) && (defined(_M_X64) || defined(_M_IX86))
//#define _SDRPLAY_DLL_QUALIFIER __declspec(dllimport)
//#elif defined(STATIC_LIB) || defined(__GNUC__)
//#define _SDRPLAY_DLL_QUALIFIER
//#endif
//#endif // _SDRPLAY_DLL_QUALIFIER
// Application code should check that it is compiled against the same API version
// sdrplay_api_ApiVersion() returns the API version
#define SDRPLAY_API_VERSION (float)(3.07)
// API Constants
#define SDRPLAY_MAX_DEVICES (16)
#define SDRPLAY_MAX_TUNERS_PER_DEVICE (2)
#define SDRPLAY_MAX_SER_NO_LEN (64)
#define SDRPLAY_MAX_ROOT_NM_LEN (32)
#define SDRPLAY_RSP1_ID (1)
#define SDRPLAY_RSP1A_ID (255)
#define SDRPLAY_RSP2_ID (2)
#define SDRPLAY_RSPduo_ID (3)
#define SDRPLAY_RSPdx_ID (4)
// Enum types
typedef enum
{
sdrplay_api_Success = 0,
sdrplay_api_Fail = 1,
sdrplay_api_InvalidParam = 2,
sdrplay_api_OutOfRange = 3,
sdrplay_api_GainUpdateError = 4,
sdrplay_api_RfUpdateError = 5,
sdrplay_api_FsUpdateError = 6,
sdrplay_api_HwError = 7,
sdrplay_api_AliasingError = 8,
sdrplay_api_AlreadyInitialised = 9,
sdrplay_api_NotInitialised = 10,
sdrplay_api_NotEnabled = 11,
sdrplay_api_HwVerError = 12,
sdrplay_api_OutOfMemError = 13,
sdrplay_api_ServiceNotResponding = 14,
sdrplay_api_StartPending = 15,
sdrplay_api_StopPending = 16,
sdrplay_api_InvalidMode = 17,
sdrplay_api_FailedVerification1 = 18,
sdrplay_api_FailedVerification2 = 19,
sdrplay_api_FailedVerification3 = 20,
sdrplay_api_FailedVerification4 = 21,
sdrplay_api_FailedVerification5 = 22,
sdrplay_api_FailedVerification6 = 23,
sdrplay_api_InvalidServiceVersion = 24
} sdrplay_api_ErrT;
typedef enum
{
sdrplay_api_Update_None = 0x00000000,
// Reasons for master only mode
sdrplay_api_Update_Dev_Fs = 0x00000001,
sdrplay_api_Update_Dev_Ppm = 0x00000002,
sdrplay_api_Update_Dev_SyncUpdate = 0x00000004,
sdrplay_api_Update_Dev_ResetFlags = 0x00000008,
sdrplay_api_Update_Rsp1a_BiasTControl = 0x00000010,
sdrplay_api_Update_Rsp1a_RfNotchControl = 0x00000020,
sdrplay_api_Update_Rsp1a_RfDabNotchControl = 0x00000040,
sdrplay_api_Update_Rsp2_BiasTControl = 0x00000080,
sdrplay_api_Update_Rsp2_AmPortSelect = 0x00000100,
sdrplay_api_Update_Rsp2_AntennaControl = 0x00000200,
sdrplay_api_Update_Rsp2_RfNotchControl = 0x00000400,
sdrplay_api_Update_Rsp2_ExtRefControl = 0x00000800,
sdrplay_api_Update_RspDuo_ExtRefControl = 0x00001000,
sdrplay_api_Update_Master_Spare_1 = 0x00002000,
sdrplay_api_Update_Master_Spare_2 = 0x00004000,
// Reasons for master and slave mode
// Note: sdrplay_api_Update_Tuner_Gr MUST be the first value defined in this section!
sdrplay_api_Update_Tuner_Gr = 0x00008000,
sdrplay_api_Update_Tuner_GrLimits = 0x00010000,
sdrplay_api_Update_Tuner_Frf = 0x00020000,
sdrplay_api_Update_Tuner_BwType = 0x00040000,
sdrplay_api_Update_Tuner_IfType = 0x00080000,
sdrplay_api_Update_Tuner_DcOffset = 0x00100000,
sdrplay_api_Update_Tuner_LoMode = 0x00200000,
sdrplay_api_Update_Ctrl_DCoffsetIQimbalance = 0x00400000,
sdrplay_api_Update_Ctrl_Decimation = 0x00800000,
sdrplay_api_Update_Ctrl_Agc = 0x01000000,
sdrplay_api_Update_Ctrl_AdsbMode = 0x02000000,
sdrplay_api_Update_Ctrl_OverloadMsgAck = 0x04000000,
sdrplay_api_Update_RspDuo_BiasTControl = 0x08000000,
sdrplay_api_Update_RspDuo_AmPortSelect = 0x10000000,
sdrplay_api_Update_RspDuo_Tuner1AmNotchControl = 0x20000000,
sdrplay_api_Update_RspDuo_RfNotchControl = 0x40000000,
sdrplay_api_Update_RspDuo_RfDabNotchControl = 0x80000000,
} sdrplay_api_ReasonForUpdateT;
typedef enum
{
sdrplay_api_Update_Ext1_None = 0x00000000,
// Reasons for master only mode
sdrplay_api_Update_RspDx_HdrEnable = 0x00000001,
sdrplay_api_Update_RspDx_BiasTControl = 0x00000002,
sdrplay_api_Update_RspDx_AntennaControl = 0x00000004,
sdrplay_api_Update_RspDx_RfNotchControl = 0x00000008,
sdrplay_api_Update_RspDx_RfDabNotchControl = 0x00000010,
sdrplay_api_Update_RspDx_HdrBw = 0x00000020,
// Reasons for master and slave mode
} sdrplay_api_ReasonForUpdateExtension1T;
typedef enum
{
sdrplay_api_DbgLvl_Disable = 0,
sdrplay_api_DbgLvl_Verbose = 1,
sdrplay_api_DbgLvl_Warning = 2,
sdrplay_api_DbgLvl_Error = 3,
sdrplay_api_DbgLvl_Message = 4,
} sdrplay_api_DbgLvl_t;
// Device structure
typedef struct
{
char SerNo[SDRPLAY_MAX_SER_NO_LEN];
unsigned char hwVer;
sdrplay_api_TunerSelectT tuner;
sdrplay_api_RspDuoModeT rspDuoMode;
double rspDuoSampleFreq;
HANDLE dev;
} sdrplay_api_DeviceT;
// Device parameter structure
typedef struct
{
sdrplay_api_DevParamsT *devParams;
sdrplay_api_RxChannelParamsT *rxChannelA;
sdrplay_api_RxChannelParamsT *rxChannelB;
} sdrplay_api_DeviceParamsT;
// Extended error message structure
typedef struct
{
char file[256];
char function[256];
int line;
char message[1024];
} sdrplay_api_ErrorInfoT;
// Comman API function types
typedef sdrplay_api_ErrT (*sdrplay_api_Open_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_Close_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_ApiVersion_t)(float *apiVer);
typedef sdrplay_api_ErrT (*sdrplay_api_LockDeviceApi_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_UnlockDeviceApi_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_GetDevices_t)(sdrplay_api_DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs);
typedef sdrplay_api_ErrT (*sdrplay_api_SelectDevice_t)(sdrplay_api_DeviceT *device);
typedef sdrplay_api_ErrT (*sdrplay_api_ReleaseDevice_t)(sdrplay_api_DeviceT *device);
typedef const char* (*sdrplay_api_GetErrorString_t)(sdrplay_api_ErrT err);
typedef sdrplay_api_ErrorInfoT* (*sdrplay_api_GetLastError_t)(sdrplay_api_DeviceT *device);
typedef sdrplay_api_ErrT (*sdrplay_api_DisableHeartbeat_t)(void);
// Device API function types
typedef sdrplay_api_ErrT (*sdrplay_api_DebugEnable_t)(HANDLE dev, sdrplay_api_DbgLvl_t dbgLvl);
typedef sdrplay_api_ErrT (*sdrplay_api_GetDeviceParams_t)(HANDLE dev, sdrplay_api_DeviceParamsT **deviceParams);
typedef sdrplay_api_ErrT (*sdrplay_api_Init_t)(HANDLE dev, sdrplay_api_CallbackFnsT *callbackFns, void *cbContext);
typedef sdrplay_api_ErrT (*sdrplay_api_Uninit_t)(HANDLE dev);
typedef sdrplay_api_ErrT (*sdrplay_api_Update_t)(HANDLE dev, sdrplay_api_TunerSelectT tuner, sdrplay_api_ReasonForUpdateT reasonForUpdate, sdrplay_api_ReasonForUpdateExtension1T reasonForUpdateExt1);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoActiveTuner_t)(HANDLE dev, sdrplay_api_TunerSelectT *tuner, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoDualTunerModeSampleRate_t)(double *currentSampleRate);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoMode_t)(sdrplay_api_DeviceT *currDevice, sdrplay_api_DeviceParamsT **deviceParams,
sdrplay_api_RspDuoModeT rspDuoMode, double sampleRate, sdrplay_api_TunerSelectT tuner,
sdrplay_api_Bw_MHzT bwType, sdrplay_api_If_kHzT ifType, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
// API function definitions
#ifdef __cplusplus
extern "C"
{
#endif
// Comman API function definitions
sdrplay_api_ErrT sdrplay_api_Open(void);
sdrplay_api_ErrT sdrplay_api_Close(void);
sdrplay_api_ErrT sdrplay_api_ApiVersion(float *apiVer);
sdrplay_api_ErrT sdrplay_api_LockDeviceApi(void);
sdrplay_api_ErrT sdrplay_api_UnlockDeviceApi(void);
sdrplay_api_ErrT sdrplay_api_GetDevices(sdrplay_api_DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs);
sdrplay_api_ErrT sdrplay_api_SelectDevice(sdrplay_api_DeviceT *device);
sdrplay_api_ErrT sdrplay_api_ReleaseDevice(sdrplay_api_DeviceT *device);
const char* sdrplay_api_GetErrorString(sdrplay_api_ErrT err);
sdrplay_api_ErrorInfoT* sdrplay_api_GetLastError(sdrplay_api_DeviceT *device);
sdrplay_api_ErrT sdrplay_api_DisableHeartbeat(void); // Must be called before sdrplay_api_SelectDevice()
// Device API function definitions
sdrplay_api_ErrT sdrplay_api_DebugEnable(HANDLE dev, sdrplay_api_DbgLvl_t enable);
sdrplay_api_ErrT sdrplay_api_GetDeviceParams(HANDLE dev, sdrplay_api_DeviceParamsT **deviceParams);
sdrplay_api_ErrT sdrplay_api_Init(HANDLE dev, sdrplay_api_CallbackFnsT *callbackFns, void *cbContext);
sdrplay_api_ErrT sdrplay_api_Uninit(HANDLE dev);
sdrplay_api_ErrT sdrplay_api_Update(HANDLE dev, sdrplay_api_TunerSelectT tuner, sdrplay_api_ReasonForUpdateT reasonForUpdate, sdrplay_api_ReasonForUpdateExtension1T reasonForUpdateExt1);
sdrplay_api_ErrT sdrplay_api_SwapRspDuoActiveTuner(HANDLE dev, sdrplay_api_TunerSelectT *currentTuner, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
sdrplay_api_ErrT sdrplay_api_SwapRspDuoDualTunerModeSampleRate(HANDLE dev, double *currentSampleRate);
sdrplay_api_ErrT sdrplay_api_SwapRspDuoMode(sdrplay_api_DeviceT *currDevice, sdrplay_api_DeviceParamsT **deviceParams,
sdrplay_api_RspDuoModeT rspDuoMode, double sampleRate, sdrplay_api_TunerSelectT tuner,
sdrplay_api_Bw_MHzT bwType, sdrplay_api_If_kHzT ifType, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
#ifdef __cplusplus
}
#endif
#endif //SDRPLAY_API_H

View File

@@ -1,78 +0,0 @@
#ifndef SDRPLAY_API_CALLBACK_H
#define SDRPLAY_API_CALLBACK_H
// Event callback enums
typedef enum
{
sdrplay_api_Overload_Detected = 0,
sdrplay_api_Overload_Corrected = 1,
} sdrplay_api_PowerOverloadCbEventIdT;
typedef enum
{
sdrplay_api_MasterInitialised = 0,
sdrplay_api_SlaveAttached = 1,
sdrplay_api_SlaveDetached = 2,
sdrplay_api_SlaveInitialised = 3,
sdrplay_api_SlaveUninitialised = 4,
sdrplay_api_MasterDllDisappeared = 5,
sdrplay_api_SlaveDllDisappeared = 6,
} sdrplay_api_RspDuoModeCbEventIdT;
typedef enum
{
sdrplay_api_GainChange = 0,
sdrplay_api_PowerOverloadChange = 1,
sdrplay_api_DeviceRemoved = 2,
sdrplay_api_RspDuoModeChange = 3,
} sdrplay_api_EventT;
// Event callback parameter structs
typedef struct
{
unsigned int gRdB;
unsigned int lnaGRdB;
double currGain;
} sdrplay_api_GainCbParamT;
typedef struct
{
sdrplay_api_PowerOverloadCbEventIdT powerOverloadChangeType;
} sdrplay_api_PowerOverloadCbParamT;
typedef struct
{
sdrplay_api_RspDuoModeCbEventIdT modeChangeType;
} sdrplay_api_RspDuoModeCbParamT;
// Event parameters overlay
typedef union
{
sdrplay_api_GainCbParamT gainParams;
sdrplay_api_PowerOverloadCbParamT powerOverloadParams;
sdrplay_api_RspDuoModeCbParamT rspDuoModeParams;
} sdrplay_api_EventParamsT;
// Stream callback parameter structs
typedef struct
{
unsigned int firstSampleNum;
int grChanged;
int rfChanged;
int fsChanged;
unsigned int numSamples;
} sdrplay_api_StreamCbParamsT;
// Callback function prototypes
typedef void (*sdrplay_api_StreamCallback_t)(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext);
typedef void (*sdrplay_api_EventCallback_t)(sdrplay_api_EventT eventId, sdrplay_api_TunerSelectT tuner, sdrplay_api_EventParamsT *params, void *cbContext);
// Callback function struct
typedef struct
{
sdrplay_api_StreamCallback_t StreamACbFn;
sdrplay_api_StreamCallback_t StreamBCbFn;
sdrplay_api_EventCallback_t EventCbFn;
} sdrplay_api_CallbackFnsT;
#endif //SDRPLAY_API_CALLBACK_H

View File

@@ -1,55 +0,0 @@
#ifndef SDRPLAY_API_CONTROL_H
#define SDRPLAY_API_CONTROL_H
// Control parameter enums
typedef enum
{
sdrplay_api_AGC_DISABLE = 0,
sdrplay_api_AGC_100HZ = 1,
sdrplay_api_AGC_50HZ = 2,
sdrplay_api_AGC_5HZ = 3,
sdrplay_api_AGC_CTRL_EN = 4
} sdrplay_api_AgcControlT;
typedef enum
{
sdrplay_api_ADSB_DECIMATION = 0,
sdrplay_api_ADSB_NO_DECIMATION_LOWPASS = 1,
sdrplay_api_ADSB_NO_DECIMATION_BANDPASS_2MHZ = 2,
sdrplay_api_ADSB_NO_DECIMATION_BANDPASS_3MHZ = 3
} sdrplay_api_AdsbModeT;
// Control parameter structs
typedef struct
{
unsigned char DCenable; // default: 1
unsigned char IQenable; // default: 1
} sdrplay_api_DcOffsetT;
typedef struct
{
unsigned char enable; // default: 0
unsigned char decimationFactor; // default: 1
unsigned char wideBandSignal; // default: 0
} sdrplay_api_DecimationT;
typedef struct
{
sdrplay_api_AgcControlT enable; // default: sdrplay_api_AGC_50HZ
int setPoint_dBfs; // default: -60
unsigned short attack_ms; // default: 0
unsigned short decay_ms; // default: 0
unsigned short decay_delay_ms; // default: 0
unsigned short decay_threshold_dB; // default: 0
int syncUpdate; // default: 0
} sdrplay_api_AgcT;
typedef struct
{
sdrplay_api_DcOffsetT dcOffset;
sdrplay_api_DecimationT decimation;
sdrplay_api_AgcT agc;
sdrplay_api_AdsbModeT adsbMode; //default: sdrplay_api_ADSB_DECIMATION
} sdrplay_api_ControlParamsT;
#endif //SDRPLAY_API_CONTROL_H

View File

@@ -1,51 +0,0 @@
#ifndef SDRPLAY_API_DEV_H
#define SDRPLAY_API_DEV_H
#include "sdrplay_api_rsp1a.h"
#include "sdrplay_api_rsp2.h"
#include "sdrplay_api_rspDuo.h"
#include "sdrplay_api_rspDx.h"
// Dev parameter enums
typedef enum
{
sdrplay_api_ISOCH = 0,
sdrplay_api_BULK = 1
} sdrplay_api_TransferModeT;
// Dev parameter structs
typedef struct
{
double fsHz; // default: 2000000.0
unsigned char syncUpdate; // default: 0
unsigned char reCal; // default: 0
} sdrplay_api_FsFreqT;
typedef struct
{
unsigned int sampleNum; // default: 0
unsigned int period; // default: 0
} sdrplay_api_SyncUpdateT;
typedef struct
{
unsigned char resetGainUpdate; // default: 0
unsigned char resetRfUpdate; // default: 0
unsigned char resetFsUpdate; // default: 0
} sdrplay_api_ResetFlagsT;
typedef struct
{
double ppm; // default: 0.0
sdrplay_api_FsFreqT fsFreq;
sdrplay_api_SyncUpdateT syncUpdate;
sdrplay_api_ResetFlagsT resetFlags;
sdrplay_api_TransferModeT mode; // default: sdrplay_api_ISOCH
unsigned int samplesPerPkt; // default: 0 (output param)
sdrplay_api_Rsp1aParamsT rsp1aParams;
sdrplay_api_Rsp2ParamsT rsp2Params;
sdrplay_api_RspDuoParamsT rspDuoParams;
sdrplay_api_RspDxParamsT rspDxParams;
} sdrplay_api_DevParamsT;
#endif //SDRPLAY_API_DEV_H

View File

@@ -1,22 +0,0 @@
#ifndef SDRPLAY_API_RSP1A_H
#define SDRPLAY_API_RSP1A_H
#define RSPIA_NUM_LNA_STATES 10
#define RSPIA_NUM_LNA_STATES_AM 7
#define RSPIA_NUM_LNA_STATES_LBAND 9
// RSP1A parameter enums
// RSP1A parameter structs
typedef struct
{
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
} sdrplay_api_Rsp1aParamsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
} sdrplay_api_Rsp1aTunerParamsT;
#endif //SDRPLAY_API_RSP1A_H

View File

@@ -1,35 +0,0 @@
#ifndef SDRPLAY_API_RSP2_H
#define SDRPLAY_API_RSP2_H
#define RSPII_NUM_LNA_STATES 9
#define RSPII_NUM_LNA_STATES_AMPORT 5
#define RSPII_NUM_LNA_STATES_420MHZ 6
// RSP2 parameter enums
typedef enum
{
sdrplay_api_Rsp2_ANTENNA_A = 5,
sdrplay_api_Rsp2_ANTENNA_B = 6,
} sdrplay_api_Rsp2_AntennaSelectT;
typedef enum
{
sdrplay_api_Rsp2_AMPORT_1 = 1,
sdrplay_api_Rsp2_AMPORT_2 = 0,
} sdrplay_api_Rsp2_AmPortSelectT;
// RSP2 parameter structs
typedef struct
{
unsigned char extRefOutputEn; // default: 0
} sdrplay_api_Rsp2ParamsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
sdrplay_api_Rsp2_AmPortSelectT amPortSel; // default: sdrplay_api_Rsp2_AMPORT_2
sdrplay_api_Rsp2_AntennaSelectT antennaSel; // default: sdrplay_api_Rsp2_ANTENNA_A
unsigned char rfNotchEnable; // default: 0
} sdrplay_api_Rsp2TunerParamsT;
#endif //SDRPLAY_API_RSP2_H

View File

@@ -1,40 +0,0 @@
#ifndef SDRPLAY_API_RSPduo_H
#define SDRPLAY_API_RSPduo_H
#define RSPDUO_NUM_LNA_STATES 10
#define RSPDUO_NUM_LNA_STATES_AMPORT 5
#define RSPDUO_NUM_LNA_STATES_AM 7
#define RSPDUO_NUM_LNA_STATES_LBAND 9
// RSPduo parameter enums
typedef enum
{
sdrplay_api_RspDuoMode_Unknown = 0,
sdrplay_api_RspDuoMode_Single_Tuner = 1,
sdrplay_api_RspDuoMode_Dual_Tuner = 2,
sdrplay_api_RspDuoMode_Master = 4,
sdrplay_api_RspDuoMode_Slave = 8,
} sdrplay_api_RspDuoModeT;
typedef enum
{
sdrplay_api_RspDuo_AMPORT_1 = 1,
sdrplay_api_RspDuo_AMPORT_2 = 0,
} sdrplay_api_RspDuo_AmPortSelectT;
// RSPduo parameter structs
typedef struct
{
int extRefOutputEn; // default: 0
} sdrplay_api_RspDuoParamsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel; // default: sdrplay_api_RspDuo_AMPORT_2
unsigned char tuner1AmNotchEnable; // default: 0
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
} sdrplay_api_RspDuoTunerParamsT;
#endif //SDRPLAY_API_RSPduo_H

View File

@@ -1,45 +0,0 @@
#ifndef SDRPLAY_API_RSPDX_H
#define SDRPLAY_API_RSPDX_H
#include "sdrplay_api_tuner.h"
#define RSPDX_NUM_LNA_STATES 28 // Number of LNA states in all bands (except where defined differently below)
#define RSPDX_NUM_LNA_STATES_AMPORT2_0_12 19 // Number of LNA states when using AM Port 2 between 0 and 12MHz
#define RSPDX_NUM_LNA_STATES_AMPORT2_12_60 20 // Number of LNA states when using AM Port 2 between 12 and 60MHz
#define RSPDX_NUM_LNA_STATES_VHF_BAND3 27 // Number of LNA states in VHF and Band3
#define RSPDX_NUM_LNA_STATES_420MHZ 21 // Number of LNA states in 420MHz band
#define RSPDX_NUM_LNA_STATES_LBAND 19 // Number of LNA states in L-band
#define RSPDX_NUM_LNA_STATES_DX 26 // Number of LNA states in DX path
// RSPdx parameter enums
typedef enum
{
sdrplay_api_RspDx_ANTENNA_A = 0,
sdrplay_api_RspDx_ANTENNA_B = 1,
sdrplay_api_RspDx_ANTENNA_C = 2,
} sdrplay_api_RspDx_AntennaSelectT;
typedef enum
{
sdrplay_api_RspDx_HDRMODE_BW_0_200 = 0,
sdrplay_api_RspDx_HDRMODE_BW_0_500 = 1,
sdrplay_api_RspDx_HDRMODE_BW_1_200 = 2,
sdrplay_api_RspDx_HDRMODE_BW_1_700 = 3,
} sdrplay_api_RspDx_HdrModeBwT;
// RSPdx parameter structs
typedef struct
{
unsigned char hdrEnable; // default: 0
unsigned char biasTEnable; // default: 0
sdrplay_api_RspDx_AntennaSelectT antennaSel; // default: sdrplay_api_RspDx_ANTENNA_A
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
} sdrplay_api_RspDxParamsT;
typedef struct
{
sdrplay_api_RspDx_HdrModeBwT hdrBw; // default: sdrplay_api_RspDx_HDRMODE_BW_1_700
} sdrplay_api_RspDxTunerParamsT;
#endif //SDRPLAY_API_RSPDX_H

View File

@@ -1,21 +0,0 @@
#ifndef SDRPLAY_API_RX_CHANNEL_H
#define SDRPLAY_API_RX_CHANNEL_H
#include "sdrplay_api_tuner.h"
#include "sdrplay_api_control.h"
#include "sdrplay_api_rsp1a.h"
#include "sdrplay_api_rsp2.h"
#include "sdrplay_api_rspDuo.h"
#include "sdrplay_api_rspDx.h"
typedef struct
{
sdrplay_api_TunerParamsT tunerParams;
sdrplay_api_ControlParamsT ctrlParams;
sdrplay_api_Rsp1aTunerParamsT rsp1aTunerParams;
sdrplay_api_Rsp2TunerParamsT rsp2TunerParams;
sdrplay_api_RspDuoTunerParamsT rspDuoTunerParams;
sdrplay_api_RspDxTunerParamsT rspDxTunerParams;
} sdrplay_api_RxChannelParamsT;
#endif //SDRPLAY_API_RX_CHANNEL_H

View File

@@ -1,93 +0,0 @@
#ifndef SDRPLAY_API_TUNER_H
#define SDRPLAY_API_TUNER_H
#define MAX_BB_GR (59)
// Tuner parameter enums
typedef enum
{
sdrplay_api_BW_Undefined = 0,
sdrplay_api_BW_0_200 = 200,
sdrplay_api_BW_0_300 = 300,
sdrplay_api_BW_0_600 = 600,
sdrplay_api_BW_1_536 = 1536,
sdrplay_api_BW_5_000 = 5000,
sdrplay_api_BW_6_000 = 6000,
sdrplay_api_BW_7_000 = 7000,
sdrplay_api_BW_8_000 = 8000
} sdrplay_api_Bw_MHzT;
typedef enum
{
sdrplay_api_IF_Undefined = -1,
sdrplay_api_IF_Zero = 0,
sdrplay_api_IF_0_450 = 450,
sdrplay_api_IF_1_620 = 1620,
sdrplay_api_IF_2_048 = 2048
} sdrplay_api_If_kHzT;
typedef enum
{
sdrplay_api_LO_Undefined = 0,
sdrplay_api_LO_Auto = 1,
sdrplay_api_LO_120MHz = 2,
sdrplay_api_LO_144MHz = 3,
sdrplay_api_LO_168MHz = 4
} sdrplay_api_LoModeT;
typedef enum
{
sdrplay_api_EXTENDED_MIN_GR = 0,
sdrplay_api_NORMAL_MIN_GR = 20
} sdrplay_api_MinGainReductionT;
typedef enum
{
sdrplay_api_Tuner_Neither = 0,
sdrplay_api_Tuner_A = 1,
sdrplay_api_Tuner_B = 2,
sdrplay_api_Tuner_Both = 3,
} sdrplay_api_TunerSelectT;
// Tuner parameter structs
typedef struct
{
float curr;
float max;
float min;
} sdrplay_api_GainValuesT;
typedef struct
{
int gRdB; // default: 50
unsigned char LNAstate; // default: 0
unsigned char syncUpdate; // default: 0
sdrplay_api_MinGainReductionT minGr; // default: sdrplay_api_NORMAL_MIN_GR
sdrplay_api_GainValuesT gainVals; // output parameter
} sdrplay_api_GainT;
typedef struct
{
double rfHz; // default: 200000000.0
unsigned char syncUpdate; // default: 0
} sdrplay_api_RfFreqT;
typedef struct
{
unsigned char dcCal; // default: 3 (Periodic mode)
unsigned char speedUp; // default: 0 (No speedup)
int trackTime; // default: 1 (=> time in uSec = (72 * 3 * trackTime) / 24e6 = 9uSec)
int refreshRateTime; // default: 2048 (=> time in uSec = (72 * 3 * refreshRateTime) / 24e6 = 18432uSec)
} sdrplay_api_DcOffsetTunerT;
typedef struct
{
sdrplay_api_Bw_MHzT bwType; // default: sdrplay_api_BW_0_200
sdrplay_api_If_kHzT ifType; // default: sdrplay_api_IF_Zero
sdrplay_api_LoModeT loMode; // default: sdrplay_api_LO_Auto
sdrplay_api_GainT gain;
sdrplay_api_RfFreqT rfFreq;
sdrplay_api_DcOffsetTunerT dcOffsetTuner;
} sdrplay_api_TunerParamsT;
#endif //SDRPLAY_API_TUNER_H

View File

@@ -1,230 +0,0 @@
#ifndef SDRPLAY_API_H
#define SDRPLAY_API_H
#include "sdrplay_api_dev.h"
#include "sdrplay_api_rx_channel.h"
#include "sdrplay_api_callback.h"
//#if defined(_M_X64) || defined(_M_IX86)
//#include "windows.h"
//#elif defined (__GNUC__)
typedef void *HANDLE;
//#endif
//
//#ifndef _SDRPLAY_DLL_QUALIFIER
//#if !defined(STATIC_LIB) && (defined(_M_X64) || defined(_M_IX86))
//#define _SDRPLAY_DLL_QUALIFIER __declspec(dllimport)
//#elif defined(STATIC_LIB) || defined(__GNUC__)
//#define _SDRPLAY_DLL_QUALIFIER
//#endif
//#endif // _SDRPLAY_DLL_QUALIFIER
// Application code should check that it is compiled against the same API version
// sdrplay_api_ApiVersion() returns the API version
#define SDRPLAY_API_VERSION (float)(3.08)
// API Constants
#define SDRPLAY_MAX_DEVICES (16)
#define SDRPLAY_MAX_TUNERS_PER_DEVICE (2)
#define SDRPLAY_MAX_SER_NO_LEN (64)
#define SDRPLAY_MAX_ROOT_NM_LEN (32)
#define SDRPLAY_RSP1_ID (1)
#define SDRPLAY_RSP1A_ID (255)
#define SDRPLAY_RSP2_ID (2)
#define SDRPLAY_RSPduo_ID (3)
#define SDRPLAY_RSPdx_ID (4)
// Enum types
typedef enum
{
sdrplay_api_Success = 0,
sdrplay_api_Fail = 1,
sdrplay_api_InvalidParam = 2,
sdrplay_api_OutOfRange = 3,
sdrplay_api_GainUpdateError = 4,
sdrplay_api_RfUpdateError = 5,
sdrplay_api_FsUpdateError = 6,
sdrplay_api_HwError = 7,
sdrplay_api_AliasingError = 8,
sdrplay_api_AlreadyInitialised = 9,
sdrplay_api_NotInitialised = 10,
sdrplay_api_NotEnabled = 11,
sdrplay_api_HwVerError = 12,
sdrplay_api_OutOfMemError = 13,
sdrplay_api_ServiceNotResponding = 14,
sdrplay_api_StartPending = 15,
sdrplay_api_StopPending = 16,
sdrplay_api_InvalidMode = 17,
sdrplay_api_FailedVerification1 = 18,
sdrplay_api_FailedVerification2 = 19,
sdrplay_api_FailedVerification3 = 20,
sdrplay_api_FailedVerification4 = 21,
sdrplay_api_FailedVerification5 = 22,
sdrplay_api_FailedVerification6 = 23,
sdrplay_api_InvalidServiceVersion = 24
} sdrplay_api_ErrT;
typedef enum
{
sdrplay_api_Update_None = 0x00000000,
// Reasons for master only mode
sdrplay_api_Update_Dev_Fs = 0x00000001,
sdrplay_api_Update_Dev_Ppm = 0x00000002,
sdrplay_api_Update_Dev_SyncUpdate = 0x00000004,
sdrplay_api_Update_Dev_ResetFlags = 0x00000008,
sdrplay_api_Update_Rsp1a_BiasTControl = 0x00000010,
sdrplay_api_Update_Rsp1a_RfNotchControl = 0x00000020,
sdrplay_api_Update_Rsp1a_RfDabNotchControl = 0x00000040,
sdrplay_api_Update_Rsp2_BiasTControl = 0x00000080,
sdrplay_api_Update_Rsp2_AmPortSelect = 0x00000100,
sdrplay_api_Update_Rsp2_AntennaControl = 0x00000200,
sdrplay_api_Update_Rsp2_RfNotchControl = 0x00000400,
sdrplay_api_Update_Rsp2_ExtRefControl = 0x00000800,
sdrplay_api_Update_RspDuo_ExtRefControl = 0x00001000,
sdrplay_api_Update_Master_Spare_1 = 0x00002000,
sdrplay_api_Update_Master_Spare_2 = 0x00004000,
// Reasons for master and slave mode
// Note: sdrplay_api_Update_Tuner_Gr MUST be the first value defined in this section!
sdrplay_api_Update_Tuner_Gr = 0x00008000,
sdrplay_api_Update_Tuner_GrLimits = 0x00010000,
sdrplay_api_Update_Tuner_Frf = 0x00020000,
sdrplay_api_Update_Tuner_BwType = 0x00040000,
sdrplay_api_Update_Tuner_IfType = 0x00080000,
sdrplay_api_Update_Tuner_DcOffset = 0x00100000,
sdrplay_api_Update_Tuner_LoMode = 0x00200000,
sdrplay_api_Update_Ctrl_DCoffsetIQimbalance = 0x00400000,
sdrplay_api_Update_Ctrl_Decimation = 0x00800000,
sdrplay_api_Update_Ctrl_Agc = 0x01000000,
sdrplay_api_Update_Ctrl_AdsbMode = 0x02000000,
sdrplay_api_Update_Ctrl_OverloadMsgAck = 0x04000000,
sdrplay_api_Update_RspDuo_BiasTControl = 0x08000000,
sdrplay_api_Update_RspDuo_AmPortSelect = 0x10000000,
sdrplay_api_Update_RspDuo_Tuner1AmNotchControl = 0x20000000,
sdrplay_api_Update_RspDuo_RfNotchControl = 0x40000000,
sdrplay_api_Update_RspDuo_RfDabNotchControl = 0x80000000,
} sdrplay_api_ReasonForUpdateT;
typedef enum
{
sdrplay_api_Update_Ext1_None = 0x00000000,
// Reasons for master only mode
sdrplay_api_Update_RspDx_HdrEnable = 0x00000001,
sdrplay_api_Update_RspDx_BiasTControl = 0x00000002,
sdrplay_api_Update_RspDx_AntennaControl = 0x00000004,
sdrplay_api_Update_RspDx_RfNotchControl = 0x00000008,
sdrplay_api_Update_RspDx_RfDabNotchControl = 0x00000010,
sdrplay_api_Update_RspDx_HdrBw = 0x00000020,
// Reasons for master and slave mode
} sdrplay_api_ReasonForUpdateExtension1T;
typedef enum
{
sdrplay_api_DbgLvl_Disable = 0,
sdrplay_api_DbgLvl_Verbose = 1,
sdrplay_api_DbgLvl_Warning = 2,
sdrplay_api_DbgLvl_Error = 3,
sdrplay_api_DbgLvl_Message = 4,
} sdrplay_api_DbgLvl_t;
// Device structure
typedef struct
{
char SerNo[SDRPLAY_MAX_SER_NO_LEN];
unsigned char hwVer;
sdrplay_api_TunerSelectT tuner;
sdrplay_api_RspDuoModeT rspDuoMode;
unsigned char valid;
double rspDuoSampleFreq;
HANDLE dev;
} sdrplay_api_DeviceT;
// Device parameter structure
typedef struct
{
sdrplay_api_DevParamsT *devParams;
sdrplay_api_RxChannelParamsT *rxChannelA;
sdrplay_api_RxChannelParamsT *rxChannelB;
} sdrplay_api_DeviceParamsT;
// Extended error message structure
typedef struct
{
char file[256];
char function[256];
int line;
char message[1024];
} sdrplay_api_ErrorInfoT;
// Comman API function types
typedef sdrplay_api_ErrT (*sdrplay_api_Open_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_Close_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_ApiVersion_t)(float *apiVer);
typedef sdrplay_api_ErrT (*sdrplay_api_LockDeviceApi_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_UnlockDeviceApi_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_GetDevices_t)(sdrplay_api_DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs);
typedef sdrplay_api_ErrT (*sdrplay_api_SelectDevice_t)(sdrplay_api_DeviceT *device);
typedef sdrplay_api_ErrT (*sdrplay_api_ReleaseDevice_t)(sdrplay_api_DeviceT *device);
typedef const char* (*sdrplay_api_GetErrorString_t)(sdrplay_api_ErrT err);
typedef sdrplay_api_ErrorInfoT* (*sdrplay_api_GetLastError_t)(sdrplay_api_DeviceT *device);
typedef sdrplay_api_ErrT (*sdrplay_api_DisableHeartbeat_t)(void);
// Device API function types
typedef sdrplay_api_ErrT (*sdrplay_api_DebugEnable_t)(HANDLE dev, sdrplay_api_DbgLvl_t dbgLvl);
typedef sdrplay_api_ErrT (*sdrplay_api_GetDeviceParams_t)(HANDLE dev, sdrplay_api_DeviceParamsT **deviceParams);
typedef sdrplay_api_ErrT (*sdrplay_api_Init_t)(HANDLE dev, sdrplay_api_CallbackFnsT *callbackFns, void *cbContext);
typedef sdrplay_api_ErrT (*sdrplay_api_Uninit_t)(HANDLE dev);
typedef sdrplay_api_ErrT (*sdrplay_api_Update_t)(HANDLE dev, sdrplay_api_TunerSelectT tuner, sdrplay_api_ReasonForUpdateT reasonForUpdate, sdrplay_api_ReasonForUpdateExtension1T reasonForUpdateExt1);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoActiveTuner_t)(HANDLE dev, sdrplay_api_TunerSelectT *tuner, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoDualTunerModeSampleRate_t)(double *currentSampleRate, double newSampleRate);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoMode_t)(sdrplay_api_DeviceT *currDevice, sdrplay_api_DeviceParamsT **deviceParams,
sdrplay_api_RspDuoModeT rspDuoMode, double sampleRate, sdrplay_api_TunerSelectT tuner,
sdrplay_api_Bw_MHzT bwType, sdrplay_api_If_kHzT ifType, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
// API function definitions
#ifdef __cplusplus
extern "C"
{
#endif
// // Comman API function definitions
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Open(void);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Close(void);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_ApiVersion(float *apiVer);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_LockDeviceApi(void);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_UnlockDeviceApi(void);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_GetDevices(sdrplay_api_DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_SelectDevice(sdrplay_api_DeviceT *device);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_ReleaseDevice(sdrplay_api_DeviceT *device);
// _SDRPLAY_DLL_QUALIFIER const char* sdrplay_api_GetErrorString(sdrplay_api_ErrT err);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrorInfoT* sdrplay_api_GetLastError(sdrplay_api_DeviceT *device);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_DisableHeartbeat(void); // Must be called before sdrplay_api_SelectDevice()
//
// // Device API function definitions
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_DebugEnable(HANDLE dev, sdrplay_api_DbgLvl_t enable);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_GetDeviceParams(HANDLE dev, sdrplay_api_DeviceParamsT **deviceParams);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Init(HANDLE dev, sdrplay_api_CallbackFnsT *callbackFns, void *cbContext);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Uninit(HANDLE dev);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Update(HANDLE dev, sdrplay_api_TunerSelectT tuner, sdrplay_api_ReasonForUpdateT reasonForUpdate, sdrplay_api_ReasonForUpdateExtension1T reasonForUpdateExt1);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_SwapRspDuoActiveTuner(HANDLE dev, sdrplay_api_TunerSelectT *currentTuner, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_SwapRspDuoDualTunerModeSampleRate(HANDLE dev, double *currentSampleRate, double newSampleRate);
// _SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_SwapRspDuoMode(sdrplay_api_DeviceT *currDevice, sdrplay_api_DeviceParamsT **deviceParams,
// sdrplay_api_RspDuoModeT rspDuoMode, double sampleRate, sdrplay_api_TunerSelectT tuner,
// sdrplay_api_Bw_MHzT bwType, sdrplay_api_If_kHzT ifType, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
#ifdef __cplusplus
}
#endif
#endif //SDRPLAY_API_H

View File

@@ -1,78 +0,0 @@
#ifndef SDRPLAY_API_CALLBACK_H
#define SDRPLAY_API_CALLBACK_H
// Event callback enums
typedef enum
{
sdrplay_api_Overload_Detected = 0,
sdrplay_api_Overload_Corrected = 1,
} sdrplay_api_PowerOverloadCbEventIdT;
typedef enum
{
sdrplay_api_MasterInitialised = 0,
sdrplay_api_SlaveAttached = 1,
sdrplay_api_SlaveDetached = 2,
sdrplay_api_SlaveInitialised = 3,
sdrplay_api_SlaveUninitialised = 4,
sdrplay_api_MasterDllDisappeared = 5,
sdrplay_api_SlaveDllDisappeared = 6,
} sdrplay_api_RspDuoModeCbEventIdT;
typedef enum
{
sdrplay_api_GainChange = 0,
sdrplay_api_PowerOverloadChange = 1,
sdrplay_api_DeviceRemoved = 2,
sdrplay_api_RspDuoModeChange = 3,
} sdrplay_api_EventT;
// Event callback parameter structs
typedef struct
{
unsigned int gRdB;
unsigned int lnaGRdB;
double currGain;
} sdrplay_api_GainCbParamT;
typedef struct
{
sdrplay_api_PowerOverloadCbEventIdT powerOverloadChangeType;
} sdrplay_api_PowerOverloadCbParamT;
typedef struct
{
sdrplay_api_RspDuoModeCbEventIdT modeChangeType;
} sdrplay_api_RspDuoModeCbParamT;
// Event parameters overlay
typedef union
{
sdrplay_api_GainCbParamT gainParams;
sdrplay_api_PowerOverloadCbParamT powerOverloadParams;
sdrplay_api_RspDuoModeCbParamT rspDuoModeParams;
} sdrplay_api_EventParamsT;
// Stream callback parameter structs
typedef struct
{
unsigned int firstSampleNum;
int grChanged;
int rfChanged;
int fsChanged;
unsigned int numSamples;
} sdrplay_api_StreamCbParamsT;
// Callback function prototypes
typedef void (*sdrplay_api_StreamCallback_t)(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext);
typedef void (*sdrplay_api_EventCallback_t)(sdrplay_api_EventT eventId, sdrplay_api_TunerSelectT tuner, sdrplay_api_EventParamsT *params, void *cbContext);
// Callback function struct
typedef struct
{
sdrplay_api_StreamCallback_t StreamACbFn;
sdrplay_api_StreamCallback_t StreamBCbFn;
sdrplay_api_EventCallback_t EventCbFn;
} sdrplay_api_CallbackFnsT;
#endif //SDRPLAY_API_CALLBACK_H

View File

@@ -1,55 +0,0 @@
#ifndef SDRPLAY_API_CONTROL_H
#define SDRPLAY_API_CONTROL_H
// Control parameter enums
typedef enum
{
sdrplay_api_AGC_DISABLE = 0,
sdrplay_api_AGC_100HZ = 1,
sdrplay_api_AGC_50HZ = 2,
sdrplay_api_AGC_5HZ = 3,
sdrplay_api_AGC_CTRL_EN = 4
} sdrplay_api_AgcControlT;
typedef enum
{
sdrplay_api_ADSB_DECIMATION = 0,
sdrplay_api_ADSB_NO_DECIMATION_LOWPASS = 1,
sdrplay_api_ADSB_NO_DECIMATION_BANDPASS_2MHZ = 2,
sdrplay_api_ADSB_NO_DECIMATION_BANDPASS_3MHZ = 3
} sdrplay_api_AdsbModeT;
// Control parameter structs
typedef struct
{
unsigned char DCenable; // default: 1
unsigned char IQenable; // default: 1
} sdrplay_api_DcOffsetT;
typedef struct
{
unsigned char enable; // default: 0
unsigned char decimationFactor; // default: 1
unsigned char wideBandSignal; // default: 0
} sdrplay_api_DecimationT;
typedef struct
{
sdrplay_api_AgcControlT enable; // default: sdrplay_api_AGC_50HZ
int setPoint_dBfs; // default: -60
unsigned short attack_ms; // default: 0
unsigned short decay_ms; // default: 0
unsigned short decay_delay_ms; // default: 0
unsigned short decay_threshold_dB; // default: 0
int syncUpdate; // default: 0
} sdrplay_api_AgcT;
typedef struct
{
sdrplay_api_DcOffsetT dcOffset;
sdrplay_api_DecimationT decimation;
sdrplay_api_AgcT agc;
sdrplay_api_AdsbModeT adsbMode; //default: sdrplay_api_ADSB_DECIMATION
} sdrplay_api_ControlParamsT;
#endif //SDRPLAY_API_CONTROL_H

View File

@@ -1,51 +0,0 @@
#ifndef SDRPLAY_API_DEV_H
#define SDRPLAY_API_DEV_H
#include "sdrplay_api_rsp1a.h"
#include "sdrplay_api_rsp2.h"
#include "sdrplay_api_rspDuo.h"
#include "sdrplay_api_rspDx.h"
// Dev parameter enums
typedef enum
{
sdrplay_api_ISOCH = 0,
sdrplay_api_BULK = 1
} sdrplay_api_TransferModeT;
// Dev parameter structs
typedef struct
{
double fsHz; // default: 2000000.0
unsigned char syncUpdate; // default: 0
unsigned char reCal; // default: 0
} sdrplay_api_FsFreqT;
typedef struct
{
unsigned int sampleNum; // default: 0
unsigned int period; // default: 0
} sdrplay_api_SyncUpdateT;
typedef struct
{
unsigned char resetGainUpdate; // default: 0
unsigned char resetRfUpdate; // default: 0
unsigned char resetFsUpdate; // default: 0
} sdrplay_api_ResetFlagsT;
typedef struct
{
double ppm; // default: 0.0
sdrplay_api_FsFreqT fsFreq;
sdrplay_api_SyncUpdateT syncUpdate;
sdrplay_api_ResetFlagsT resetFlags;
sdrplay_api_TransferModeT mode; // default: sdrplay_api_ISOCH
unsigned int samplesPerPkt; // default: 0 (output param)
sdrplay_api_Rsp1aParamsT rsp1aParams;
sdrplay_api_Rsp2ParamsT rsp2Params;
sdrplay_api_RspDuoParamsT rspDuoParams;
sdrplay_api_RspDxParamsT rspDxParams;
} sdrplay_api_DevParamsT;
#endif //SDRPLAY_API_DEV_H

View File

@@ -1,22 +0,0 @@
#ifndef SDRPLAY_API_RSP1A_H
#define SDRPLAY_API_RSP1A_H
#define RSPIA_NUM_LNA_STATES 10
#define RSPIA_NUM_LNA_STATES_AM 7
#define RSPIA_NUM_LNA_STATES_LBAND 9
// RSP1A parameter enums
// RSP1A parameter structs
typedef struct
{
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
} sdrplay_api_Rsp1aParamsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
} sdrplay_api_Rsp1aTunerParamsT;
#endif //SDRPLAY_API_RSP1A_H

View File

@@ -1,35 +0,0 @@
#ifndef SDRPLAY_API_RSP2_H
#define SDRPLAY_API_RSP2_H
#define RSPII_NUM_LNA_STATES 9
#define RSPII_NUM_LNA_STATES_AMPORT 5
#define RSPII_NUM_LNA_STATES_420MHZ 6
// RSP2 parameter enums
typedef enum
{
sdrplay_api_Rsp2_ANTENNA_A = 5,
sdrplay_api_Rsp2_ANTENNA_B = 6,
} sdrplay_api_Rsp2_AntennaSelectT;
typedef enum
{
sdrplay_api_Rsp2_AMPORT_1 = 1,
sdrplay_api_Rsp2_AMPORT_2 = 0,
} sdrplay_api_Rsp2_AmPortSelectT;
// RSP2 parameter structs
typedef struct
{
unsigned char extRefOutputEn; // default: 0
} sdrplay_api_Rsp2ParamsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
sdrplay_api_Rsp2_AmPortSelectT amPortSel; // default: sdrplay_api_Rsp2_AMPORT_2
sdrplay_api_Rsp2_AntennaSelectT antennaSel; // default: sdrplay_api_Rsp2_ANTENNA_A
unsigned char rfNotchEnable; // default: 0
} sdrplay_api_Rsp2TunerParamsT;
#endif //SDRPLAY_API_RSP2_H

View File

@@ -1,40 +0,0 @@
#ifndef SDRPLAY_API_RSPduo_H
#define SDRPLAY_API_RSPduo_H
#define RSPDUO_NUM_LNA_STATES 10
#define RSPDUO_NUM_LNA_STATES_AMPORT 5
#define RSPDUO_NUM_LNA_STATES_AM 7
#define RSPDUO_NUM_LNA_STATES_LBAND 9
// RSPduo parameter enums
typedef enum
{
sdrplay_api_RspDuoMode_Unknown = 0,
sdrplay_api_RspDuoMode_Single_Tuner = 1,
sdrplay_api_RspDuoMode_Dual_Tuner = 2,
sdrplay_api_RspDuoMode_Master = 4,
sdrplay_api_RspDuoMode_Slave = 8,
} sdrplay_api_RspDuoModeT;
typedef enum
{
sdrplay_api_RspDuo_AMPORT_1 = 1,
sdrplay_api_RspDuo_AMPORT_2 = 0,
} sdrplay_api_RspDuo_AmPortSelectT;
// RSPduo parameter structs
typedef struct
{
int extRefOutputEn; // default: 0
} sdrplay_api_RspDuoParamsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel; // default: sdrplay_api_RspDuo_AMPORT_2
unsigned char tuner1AmNotchEnable; // default: 0
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
} sdrplay_api_RspDuoTunerParamsT;
#endif //SDRPLAY_API_RSPduo_H

View File

@@ -1,45 +0,0 @@
#ifndef SDRPLAY_API_RSPDX_H
#define SDRPLAY_API_RSPDX_H
#include "sdrplay_api_tuner.h"
#define RSPDX_NUM_LNA_STATES 28 // Number of LNA states in all bands (except where defined differently below)
#define RSPDX_NUM_LNA_STATES_AMPORT2_0_12 19 // Number of LNA states when using AM Port 2 between 0 and 12MHz
#define RSPDX_NUM_LNA_STATES_AMPORT2_12_60 20 // Number of LNA states when using AM Port 2 between 12 and 60MHz
#define RSPDX_NUM_LNA_STATES_VHF_BAND3 27 // Number of LNA states in VHF and Band3
#define RSPDX_NUM_LNA_STATES_420MHZ 21 // Number of LNA states in 420MHz band
#define RSPDX_NUM_LNA_STATES_LBAND 19 // Number of LNA states in L-band
#define RSPDX_NUM_LNA_STATES_DX 26 // Number of LNA states in DX path
// RSPdx parameter enums
typedef enum
{
sdrplay_api_RspDx_ANTENNA_A = 0,
sdrplay_api_RspDx_ANTENNA_B = 1,
sdrplay_api_RspDx_ANTENNA_C = 2,
} sdrplay_api_RspDx_AntennaSelectT;
typedef enum
{
sdrplay_api_RspDx_HDRMODE_BW_0_200 = 0,
sdrplay_api_RspDx_HDRMODE_BW_0_500 = 1,
sdrplay_api_RspDx_HDRMODE_BW_1_200 = 2,
sdrplay_api_RspDx_HDRMODE_BW_1_700 = 3,
} sdrplay_api_RspDx_HdrModeBwT;
// RSPdx parameter structs
typedef struct
{
unsigned char hdrEnable; // default: 0
unsigned char biasTEnable; // default: 0
sdrplay_api_RspDx_AntennaSelectT antennaSel; // default: sdrplay_api_RspDx_ANTENNA_A
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
} sdrplay_api_RspDxParamsT;
typedef struct
{
sdrplay_api_RspDx_HdrModeBwT hdrBw; // default: sdrplay_api_RspDx_HDRMODE_BW_1_700
} sdrplay_api_RspDxTunerParamsT;
#endif //SDRPLAY_API_RSPDX_H

View File

@@ -1,21 +0,0 @@
#ifndef SDRPLAY_API_RX_CHANNEL_H
#define SDRPLAY_API_RX_CHANNEL_H
#include "sdrplay_api_tuner.h"
#include "sdrplay_api_control.h"
#include "sdrplay_api_rsp1a.h"
#include "sdrplay_api_rsp2.h"
#include "sdrplay_api_rspDuo.h"
#include "sdrplay_api_rspDx.h"
typedef struct
{
sdrplay_api_TunerParamsT tunerParams;
sdrplay_api_ControlParamsT ctrlParams;
sdrplay_api_Rsp1aTunerParamsT rsp1aTunerParams;
sdrplay_api_Rsp2TunerParamsT rsp2TunerParams;
sdrplay_api_RspDuoTunerParamsT rspDuoTunerParams;
sdrplay_api_RspDxTunerParamsT rspDxTunerParams;
} sdrplay_api_RxChannelParamsT;
#endif //SDRPLAY_API_RX_CHANNEL_H

View File

@@ -1,93 +0,0 @@
#ifndef SDRPLAY_API_TUNER_H
#define SDRPLAY_API_TUNER_H
#define MAX_BB_GR (59)
// Tuner parameter enums
typedef enum
{
sdrplay_api_BW_Undefined = 0,
sdrplay_api_BW_0_200 = 200,
sdrplay_api_BW_0_300 = 300,
sdrplay_api_BW_0_600 = 600,
sdrplay_api_BW_1_536 = 1536,
sdrplay_api_BW_5_000 = 5000,
sdrplay_api_BW_6_000 = 6000,
sdrplay_api_BW_7_000 = 7000,
sdrplay_api_BW_8_000 = 8000
} sdrplay_api_Bw_MHzT;
typedef enum
{
sdrplay_api_IF_Undefined = -1,
sdrplay_api_IF_Zero = 0,
sdrplay_api_IF_0_450 = 450,
sdrplay_api_IF_1_620 = 1620,
sdrplay_api_IF_2_048 = 2048
} sdrplay_api_If_kHzT;
typedef enum
{
sdrplay_api_LO_Undefined = 0,
sdrplay_api_LO_Auto = 1,
sdrplay_api_LO_120MHz = 2,
sdrplay_api_LO_144MHz = 3,
sdrplay_api_LO_168MHz = 4
} sdrplay_api_LoModeT;
typedef enum
{
sdrplay_api_EXTENDED_MIN_GR = 0,
sdrplay_api_NORMAL_MIN_GR = 20
} sdrplay_api_MinGainReductionT;
typedef enum
{
sdrplay_api_Tuner_Neither = 0,
sdrplay_api_Tuner_A = 1,
sdrplay_api_Tuner_B = 2,
sdrplay_api_Tuner_Both = 3,
} sdrplay_api_TunerSelectT;
// Tuner parameter structs
typedef struct
{
float curr;
float max;
float min;
} sdrplay_api_GainValuesT;
typedef struct
{
int gRdB; // default: 50
unsigned char LNAstate; // default: 0
unsigned char syncUpdate; // default: 0
sdrplay_api_MinGainReductionT minGr; // default: sdrplay_api_NORMAL_MIN_GR
sdrplay_api_GainValuesT gainVals; // output parameter
} sdrplay_api_GainT;
typedef struct
{
double rfHz; // default: 200000000.0
unsigned char syncUpdate; // default: 0
} sdrplay_api_RfFreqT;
typedef struct
{
unsigned char dcCal; // default: 3 (Periodic mode)
unsigned char speedUp; // default: 0 (No speedup)
int trackTime; // default: 1 (=> time in uSec = (72 * 3 * trackTime) / 24e6 = 9uSec)
int refreshRateTime; // default: 2048 (=> time in uSec = (72 * 3 * refreshRateTime) / 24e6 = 18432uSec)
} sdrplay_api_DcOffsetTunerT;
typedef struct
{
sdrplay_api_Bw_MHzT bwType; // default: sdrplay_api_BW_0_200
sdrplay_api_If_kHzT ifType; // default: sdrplay_api_IF_Zero
sdrplay_api_LoModeT loMode; // default: sdrplay_api_LO_Auto
sdrplay_api_GainT gain;
sdrplay_api_RfFreqT rfFreq;
sdrplay_api_DcOffsetTunerT dcOffsetTuner;
} sdrplay_api_TunerParamsT;
#endif //SDRPLAY_API_TUNER_H

View File

@@ -1,235 +0,0 @@
#ifndef SDRPLAY_API_H
#define SDRPLAY_API_H
#include "sdrplay_api_dev.h"
#include "sdrplay_api_rx_channel.h"
#include "sdrplay_api_callback.h"
#if defined(_M_X64) || defined(_M_IX86) || defined(_M_ARM64)
#include "windows.h"
#elif defined (__GNUC__)
typedef void *HANDLE;
#endif
#ifndef _SDRPLAY_DLL_QUALIFIER
#if !defined(STATIC_LIB) && (defined(_M_X64) || defined(_M_IX86) || defined(_M_ARM64))
#define _SDRPLAY_DLL_QUALIFIER __declspec(dllimport)
#elif defined(STATIC_LIB) || defined(__GNUC__)
#define _SDRPLAY_DLL_QUALIFIER
#endif
#endif // _SDRPLAY_DLL_QUALIFIER
// Application code should check that it is compiled against the same API version
// sdrplay_api_ApiVersion() returns the API version
#define SDRPLAY_API_VERSION (float)(3.15)
// API Constants
#define SDRPLAY_MAX_DEVICES (16)
#define SDRPLAY_MAX_TUNERS_PER_DEVICE (2)
#define SDRPLAY_MAX_SER_NO_LEN (64)
#define SDRPLAY_MAX_ROOT_NM_LEN (32)
#define SDRPLAY_RSP1_ID (1)
#define SDRPLAY_RSP1A_ID (255)
#define SDRPLAY_RSP2_ID (2)
#define SDRPLAY_RSPduo_ID (3)
#define SDRPLAY_RSPdx_ID (4)
#define SDRPLAY_RSP1B_ID (6)
#define SDRPLAY_RSPdxR2_ID (7)
// Enum types
typedef enum
{
sdrplay_api_Success = 0,
sdrplay_api_Fail = 1,
sdrplay_api_InvalidParam = 2,
sdrplay_api_OutOfRange = 3,
sdrplay_api_GainUpdateError = 4,
sdrplay_api_RfUpdateError = 5,
sdrplay_api_FsUpdateError = 6,
sdrplay_api_HwError = 7,
sdrplay_api_AliasingError = 8,
sdrplay_api_AlreadyInitialised = 9,
sdrplay_api_NotInitialised = 10,
sdrplay_api_NotEnabled = 11,
sdrplay_api_HwVerError = 12,
sdrplay_api_OutOfMemError = 13,
sdrplay_api_ServiceNotResponding = 14,
sdrplay_api_StartPending = 15,
sdrplay_api_StopPending = 16,
sdrplay_api_InvalidMode = 17,
sdrplay_api_FailedVerification1 = 18,
sdrplay_api_FailedVerification2 = 19,
sdrplay_api_FailedVerification3 = 20,
sdrplay_api_FailedVerification4 = 21,
sdrplay_api_FailedVerification5 = 22,
sdrplay_api_FailedVerification6 = 23,
sdrplay_api_InvalidServiceVersion = 24
} sdrplay_api_ErrT;
typedef enum
{
sdrplay_api_Update_None = 0x00000000,
// Reasons for master only mode
sdrplay_api_Update_Dev_Fs = 0x00000001,
sdrplay_api_Update_Dev_Ppm = 0x00000002,
sdrplay_api_Update_Dev_SyncUpdate = 0x00000004,
sdrplay_api_Update_Dev_ResetFlags = 0x00000008,
sdrplay_api_Update_Rsp1a_BiasTControl = 0x00000010,
sdrplay_api_Update_Rsp1a_RfNotchControl = 0x00000020,
sdrplay_api_Update_Rsp1a_RfDabNotchControl = 0x00000040,
sdrplay_api_Update_Rsp2_BiasTControl = 0x00000080,
sdrplay_api_Update_Rsp2_AmPortSelect = 0x00000100,
sdrplay_api_Update_Rsp2_AntennaControl = 0x00000200,
sdrplay_api_Update_Rsp2_RfNotchControl = 0x00000400,
sdrplay_api_Update_Rsp2_ExtRefControl = 0x00000800,
sdrplay_api_Update_RspDuo_ExtRefControl = 0x00001000,
sdrplay_api_Update_Master_Spare_1 = 0x00002000,
sdrplay_api_Update_Master_Spare_2 = 0x00004000,
// Reasons for master and slave mode
// Note: sdrplay_api_Update_Tuner_Gr MUST be the first value defined in this section!
sdrplay_api_Update_Tuner_Gr = 0x00008000,
sdrplay_api_Update_Tuner_GrLimits = 0x00010000,
sdrplay_api_Update_Tuner_Frf = 0x00020000,
sdrplay_api_Update_Tuner_BwType = 0x00040000,
sdrplay_api_Update_Tuner_IfType = 0x00080000,
sdrplay_api_Update_Tuner_DcOffset = 0x00100000,
sdrplay_api_Update_Tuner_LoMode = 0x00200000,
sdrplay_api_Update_Ctrl_DCoffsetIQimbalance = 0x00400000,
sdrplay_api_Update_Ctrl_Decimation = 0x00800000,
sdrplay_api_Update_Ctrl_Agc = 0x01000000,
sdrplay_api_Update_Ctrl_AdsbMode = 0x02000000,
sdrplay_api_Update_Ctrl_OverloadMsgAck = 0x04000000,
sdrplay_api_Update_RspDuo_BiasTControl = 0x08000000,
sdrplay_api_Update_RspDuo_AmPortSelect = 0x10000000,
sdrplay_api_Update_RspDuo_Tuner1AmNotchControl = 0x20000000,
sdrplay_api_Update_RspDuo_RfNotchControl = 0x40000000,
sdrplay_api_Update_RspDuo_RfDabNotchControl = 0x80000000,
} sdrplay_api_ReasonForUpdateT;
typedef enum
{
sdrplay_api_Update_Ext1_None = 0x00000000,
// Reasons for master only mode
sdrplay_api_Update_RspDx_HdrEnable = 0x00000001,
sdrplay_api_Update_RspDx_BiasTControl = 0x00000002,
sdrplay_api_Update_RspDx_AntennaControl = 0x00000004,
sdrplay_api_Update_RspDx_RfNotchControl = 0x00000008,
sdrplay_api_Update_RspDx_RfDabNotchControl = 0x00000010,
sdrplay_api_Update_RspDx_HdrBw = 0x00000020,
sdrplay_api_Update_RspDuo_ResetSlaveFlags = 0x00000040,
// Reasons for master and slave mode
} sdrplay_api_ReasonForUpdateExtension1T;
typedef enum
{
sdrplay_api_DbgLvl_Disable = 0,
sdrplay_api_DbgLvl_Verbose = 1,
sdrplay_api_DbgLvl_Warning = 2,
sdrplay_api_DbgLvl_Error = 3,
sdrplay_api_DbgLvl_Message = 4,
} sdrplay_api_DbgLvl_t;
// Device structure
typedef struct
{
char SerNo[SDRPLAY_MAX_SER_NO_LEN];
unsigned char hwVer;
sdrplay_api_TunerSelectT tuner;
sdrplay_api_RspDuoModeT rspDuoMode;
unsigned char valid;
double rspDuoSampleFreq;
HANDLE dev;
} sdrplay_api_DeviceT;
// Device parameter structure
typedef struct
{
sdrplay_api_DevParamsT *devParams;
sdrplay_api_RxChannelParamsT *rxChannelA;
sdrplay_api_RxChannelParamsT *rxChannelB;
} sdrplay_api_DeviceParamsT;
// Extended error message structure
typedef struct
{
char file[256];
char function[256];
int line;
char message[1024];
} sdrplay_api_ErrorInfoT;
// Comman API function types
typedef sdrplay_api_ErrT (*sdrplay_api_Open_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_Close_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_ApiVersion_t)(float *apiVer);
typedef sdrplay_api_ErrT (*sdrplay_api_LockDeviceApi_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_UnlockDeviceApi_t)(void);
typedef sdrplay_api_ErrT (*sdrplay_api_GetDevices_t)(sdrplay_api_DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs);
typedef sdrplay_api_ErrT (*sdrplay_api_SelectDevice_t)(sdrplay_api_DeviceT *device);
typedef sdrplay_api_ErrT (*sdrplay_api_ReleaseDevice_t)(sdrplay_api_DeviceT *device);
typedef const char* (*sdrplay_api_GetErrorString_t)(sdrplay_api_ErrT err);
typedef sdrplay_api_ErrorInfoT* (*sdrplay_api_GetLastError_t)(sdrplay_api_DeviceT *device);
typedef sdrplay_api_ErrorInfoT* (*sdrplay_api_GetLastErrorByType_t)(sdrplay_api_DeviceT *device, int type, unsigned long long *time);
typedef sdrplay_api_ErrT (*sdrplay_api_DisableHeartbeat_t)(void);
// Device API function types
typedef sdrplay_api_ErrT (*sdrplay_api_DebugEnable_t)(HANDLE dev, sdrplay_api_DbgLvl_t dbgLvl);
typedef sdrplay_api_ErrT (*sdrplay_api_GetDeviceParams_t)(HANDLE dev, sdrplay_api_DeviceParamsT **deviceParams);
typedef sdrplay_api_ErrT (*sdrplay_api_Init_t)(HANDLE dev, sdrplay_api_CallbackFnsT *callbackFns, void *cbContext);
typedef sdrplay_api_ErrT (*sdrplay_api_Uninit_t)(HANDLE dev);
typedef sdrplay_api_ErrT (*sdrplay_api_Update_t)(HANDLE dev, sdrplay_api_TunerSelectT tuner, sdrplay_api_ReasonForUpdateT reasonForUpdate, sdrplay_api_ReasonForUpdateExtension1T reasonForUpdateExt1);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoActiveTuner_t)(HANDLE dev, sdrplay_api_TunerSelectT *tuner, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoDualTunerModeSampleRate_t)(double *currentSampleRate, double newSampleRate);
typedef sdrplay_api_ErrT (*sdrplay_api_SwapRspDuoMode_t)(sdrplay_api_DeviceT *currDevice, sdrplay_api_DeviceParamsT **deviceParams,
sdrplay_api_RspDuoModeT rspDuoMode, double sampleRate, sdrplay_api_TunerSelectT tuner,
sdrplay_api_Bw_MHzT bwType, sdrplay_api_If_kHzT ifType, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
// API function definitions
#ifdef __cplusplus
extern "C"
{
#endif
// Comman API function definitions
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Open(void);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Close(void);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_ApiVersion(float *apiVer);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_LockDeviceApi(void);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_UnlockDeviceApi(void);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_GetDevices(sdrplay_api_DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_SelectDevice(sdrplay_api_DeviceT *device);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_ReleaseDevice(sdrplay_api_DeviceT *device);
_SDRPLAY_DLL_QUALIFIER const char* sdrplay_api_GetErrorString(sdrplay_api_ErrT err);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrorInfoT* sdrplay_api_GetLastError(sdrplay_api_DeviceT *device);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrorInfoT* sdrplay_api_GetLastErrorByType(sdrplay_api_DeviceT *device, int type, unsigned long long *time);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_DisableHeartbeat(void); // Must be called before sdrplay_api_SelectDevice()
// Device API function definitions
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_DebugEnable(HANDLE dev, sdrplay_api_DbgLvl_t enable);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_GetDeviceParams(HANDLE dev, sdrplay_api_DeviceParamsT **deviceParams);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Init(HANDLE dev, sdrplay_api_CallbackFnsT *callbackFns, void *cbContext);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Uninit(HANDLE dev);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_Update(HANDLE dev, sdrplay_api_TunerSelectT tuner, sdrplay_api_ReasonForUpdateT reasonForUpdate, sdrplay_api_ReasonForUpdateExtension1T reasonForUpdateExt1);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_SwapRspDuoActiveTuner(HANDLE dev, sdrplay_api_TunerSelectT *currentTuner, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_SwapRspDuoDualTunerModeSampleRate(HANDLE dev, double *currentSampleRate, double newSampleRate);
_SDRPLAY_DLL_QUALIFIER sdrplay_api_ErrT sdrplay_api_SwapRspDuoMode(sdrplay_api_DeviceT *currDevice, sdrplay_api_DeviceParamsT **deviceParams,
sdrplay_api_RspDuoModeT rspDuoMode, double sampleRate, sdrplay_api_TunerSelectT tuner,
sdrplay_api_Bw_MHzT bwType, sdrplay_api_If_kHzT ifType, sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel);
#ifdef __cplusplus
}
#endif
#endif //SDRPLAY_API_H

View File

@@ -1,79 +0,0 @@
#ifndef SDRPLAY_API_CALLBACK_H
#define SDRPLAY_API_CALLBACK_H
// Event callback enums
typedef enum
{
sdrplay_api_Overload_Detected = 0,
sdrplay_api_Overload_Corrected = 1,
} sdrplay_api_PowerOverloadCbEventIdT;
typedef enum
{
sdrplay_api_MasterInitialised = 0,
sdrplay_api_SlaveAttached = 1,
sdrplay_api_SlaveDetached = 2,
sdrplay_api_SlaveInitialised = 3,
sdrplay_api_SlaveUninitialised = 4,
sdrplay_api_MasterDllDisappeared = 5,
sdrplay_api_SlaveDllDisappeared = 6,
} sdrplay_api_RspDuoModeCbEventIdT;
typedef enum
{
sdrplay_api_GainChange = 0,
sdrplay_api_PowerOverloadChange = 1,
sdrplay_api_DeviceRemoved = 2,
sdrplay_api_RspDuoModeChange = 3,
sdrplay_api_DeviceFailure = 4,
} sdrplay_api_EventT;
// Event callback parameter structs
typedef struct
{
unsigned int gRdB;
unsigned int lnaGRdB;
double currGain;
} sdrplay_api_GainCbParamT;
typedef struct
{
sdrplay_api_PowerOverloadCbEventIdT powerOverloadChangeType;
} sdrplay_api_PowerOverloadCbParamT;
typedef struct
{
sdrplay_api_RspDuoModeCbEventIdT modeChangeType;
} sdrplay_api_RspDuoModeCbParamT;
// Event parameters overlay
typedef union
{
sdrplay_api_GainCbParamT gainParams;
sdrplay_api_PowerOverloadCbParamT powerOverloadParams;
sdrplay_api_RspDuoModeCbParamT rspDuoModeParams;
} sdrplay_api_EventParamsT;
// Stream callback parameter structs
typedef struct
{
unsigned int firstSampleNum;
int grChanged;
int rfChanged;
int fsChanged;
unsigned int numSamples;
} sdrplay_api_StreamCbParamsT;
// Callback function prototypes
typedef void (*sdrplay_api_StreamCallback_t)(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext);
typedef void (*sdrplay_api_EventCallback_t)(sdrplay_api_EventT eventId, sdrplay_api_TunerSelectT tuner, sdrplay_api_EventParamsT *params, void *cbContext);
// Callback function struct
typedef struct
{
sdrplay_api_StreamCallback_t StreamACbFn;
sdrplay_api_StreamCallback_t StreamBCbFn;
sdrplay_api_EventCallback_t EventCbFn;
} sdrplay_api_CallbackFnsT;
#endif //SDRPLAY_API_CALLBACK_H

View File

@@ -1,55 +0,0 @@
#ifndef SDRPLAY_API_CONTROL_H
#define SDRPLAY_API_CONTROL_H
// Control parameter enums
typedef enum
{
sdrplay_api_AGC_DISABLE = 0,
sdrplay_api_AGC_100HZ = 1,
sdrplay_api_AGC_50HZ = 2,
sdrplay_api_AGC_5HZ = 3,
sdrplay_api_AGC_CTRL_EN = 4
} sdrplay_api_AgcControlT;
typedef enum
{
sdrplay_api_ADSB_DECIMATION = 0,
sdrplay_api_ADSB_NO_DECIMATION_LOWPASS = 1,
sdrplay_api_ADSB_NO_DECIMATION_BANDPASS_2MHZ = 2,
sdrplay_api_ADSB_NO_DECIMATION_BANDPASS_3MHZ = 3
} sdrplay_api_AdsbModeT;
// Control parameter structs
typedef struct
{
unsigned char DCenable; // default: 1
unsigned char IQenable; // default: 1
} sdrplay_api_DcOffsetT;
typedef struct
{
unsigned char enable; // default: 0
unsigned char decimationFactor; // default: 1
unsigned char wideBandSignal; // default: 0
} sdrplay_api_DecimationT;
typedef struct
{
sdrplay_api_AgcControlT enable; // default: sdrplay_api_AGC_50HZ
int setPoint_dBfs; // default: -60
unsigned short attack_ms; // default: 0
unsigned short decay_ms; // default: 0
unsigned short decay_delay_ms; // default: 0
unsigned short decay_threshold_dB; // default: 0
int syncUpdate; // default: 0
} sdrplay_api_AgcT;
typedef struct
{
sdrplay_api_DcOffsetT dcOffset;
sdrplay_api_DecimationT decimation;
sdrplay_api_AgcT agc;
sdrplay_api_AdsbModeT adsbMode; //default: sdrplay_api_ADSB_DECIMATION
} sdrplay_api_ControlParamsT;
#endif //SDRPLAY_API_CONTROL_H

View File

@@ -1,51 +0,0 @@
#ifndef SDRPLAY_API_DEV_H
#define SDRPLAY_API_DEV_H
#include "sdrplay_api_rsp1a.h"
#include "sdrplay_api_rsp2.h"
#include "sdrplay_api_rspDuo.h"
#include "sdrplay_api_rspDx.h"
// Dev parameter enums
typedef enum
{
sdrplay_api_ISOCH = 0,
sdrplay_api_BULK = 1
} sdrplay_api_TransferModeT;
// Dev parameter structs
typedef struct
{
double fsHz; // default: 2000000.0
unsigned char syncUpdate; // default: 0
unsigned char reCal; // default: 0
} sdrplay_api_FsFreqT;
typedef struct
{
unsigned int sampleNum; // default: 0
unsigned int period; // default: 0
} sdrplay_api_SyncUpdateT;
typedef struct
{
unsigned char resetGainUpdate; // default: 0
unsigned char resetRfUpdate; // default: 0
unsigned char resetFsUpdate; // default: 0
} sdrplay_api_ResetFlagsT;
typedef struct
{
double ppm; // default: 0.0
sdrplay_api_FsFreqT fsFreq;
sdrplay_api_SyncUpdateT syncUpdate;
sdrplay_api_ResetFlagsT resetFlags;
sdrplay_api_TransferModeT mode; // default: sdrplay_api_ISOCH
unsigned int samplesPerPkt; // default: 0 (output param)
sdrplay_api_Rsp1aParamsT rsp1aParams;
sdrplay_api_Rsp2ParamsT rsp2Params;
sdrplay_api_RspDuoParamsT rspDuoParams;
sdrplay_api_RspDxParamsT rspDxParams;
} sdrplay_api_DevParamsT;
#endif //SDRPLAY_API_DEV_H

View File

@@ -1,22 +0,0 @@
#ifndef SDRPLAY_API_RSP1A_H
#define SDRPLAY_API_RSP1A_H
#define RSPIA_NUM_LNA_STATES 10
#define RSPIA_NUM_LNA_STATES_AM 7
#define RSPIA_NUM_LNA_STATES_LBAND 9
// RSP1A parameter enums
// RSP1A parameter structs
typedef struct
{
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
} sdrplay_api_Rsp1aParamsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
} sdrplay_api_Rsp1aTunerParamsT;
#endif //SDRPLAY_API_RSP1A_H

View File

@@ -1,35 +0,0 @@
#ifndef SDRPLAY_API_RSP2_H
#define SDRPLAY_API_RSP2_H
#define RSPII_NUM_LNA_STATES 9
#define RSPII_NUM_LNA_STATES_AMPORT 5
#define RSPII_NUM_LNA_STATES_420MHZ 6
// RSP2 parameter enums
typedef enum
{
sdrplay_api_Rsp2_ANTENNA_A = 5,
sdrplay_api_Rsp2_ANTENNA_B = 6,
} sdrplay_api_Rsp2_AntennaSelectT;
typedef enum
{
sdrplay_api_Rsp2_AMPORT_1 = 1,
sdrplay_api_Rsp2_AMPORT_2 = 0,
} sdrplay_api_Rsp2_AmPortSelectT;
// RSP2 parameter structs
typedef struct
{
unsigned char extRefOutputEn; // default: 0
} sdrplay_api_Rsp2ParamsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
sdrplay_api_Rsp2_AmPortSelectT amPortSel; // default: sdrplay_api_Rsp2_AMPORT_2
sdrplay_api_Rsp2_AntennaSelectT antennaSel; // default: sdrplay_api_Rsp2_ANTENNA_A
unsigned char rfNotchEnable; // default: 0
} sdrplay_api_Rsp2TunerParamsT;
#endif //SDRPLAY_API_RSP2_H

View File

@@ -1,47 +0,0 @@
#ifndef SDRPLAY_API_RSPduo_H
#define SDRPLAY_API_RSPduo_H
#define RSPDUO_NUM_LNA_STATES 10
#define RSPDUO_NUM_LNA_STATES_AMPORT 5
#define RSPDUO_NUM_LNA_STATES_AM 7
#define RSPDUO_NUM_LNA_STATES_LBAND 9
// RSPduo parameter enums
typedef enum
{
sdrplay_api_RspDuoMode_Unknown = 0,
sdrplay_api_RspDuoMode_Single_Tuner = 1,
sdrplay_api_RspDuoMode_Dual_Tuner = 2,
sdrplay_api_RspDuoMode_Master = 4,
sdrplay_api_RspDuoMode_Slave = 8,
} sdrplay_api_RspDuoModeT;
typedef enum
{
sdrplay_api_RspDuo_AMPORT_1 = 1,
sdrplay_api_RspDuo_AMPORT_2 = 0,
} sdrplay_api_RspDuo_AmPortSelectT;
// RSPduo parameter structs
typedef struct
{
int extRefOutputEn; // default: 0
} sdrplay_api_RspDuoParamsT;
typedef struct
{
unsigned char resetGainUpdate; // default: 0
unsigned char resetRfUpdate; // default: 0
} sdrplay_api_RspDuo_ResetSlaveFlagsT;
typedef struct
{
unsigned char biasTEnable; // default: 0
sdrplay_api_RspDuo_AmPortSelectT tuner1AmPortSel; // default: sdrplay_api_RspDuo_AMPORT_2
unsigned char tuner1AmNotchEnable; // default: 0
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
sdrplay_api_RspDuo_ResetSlaveFlagsT resetSlaveFlags;
} sdrplay_api_RspDuoTunerParamsT;
#endif //SDRPLAY_API_RSPduo_H

View File

@@ -1,46 +0,0 @@
#ifndef SDRPLAY_API_RSPDX_H
#define SDRPLAY_API_RSPDX_H
#include "sdrplay_api_tuner.h"
#define RSPDX_NUM_LNA_STATES 28 // Number of LNA states in all bands (except where defined differently below)
#define RSPDX_NUM_LNA_STATES_AMPORT2_0_12 19 // Number of LNA states when using AM Port 2 between 0 and 12MHz
#define RSPDX_NUM_LNA_STATES_AMPORT2_12_50 20 // Number of LNA states when using AM Port 2 between 12 and 50MHz
#define RSPDX_NUM_LNA_STATES_AMPORT2_50_60 25 // Number of LNA states when using AM Port 2 between 50 and 60MHz
#define RSPDX_NUM_LNA_STATES_VHF_BAND3 27 // Number of LNA states in VHF and Band3
#define RSPDX_NUM_LNA_STATES_420MHZ 21 // Number of LNA states in 420MHz band
#define RSPDX_NUM_LNA_STATES_LBAND 19 // Number of LNA states in L-band
#define RSPDX_NUM_LNA_STATES_DX 22 // Number of LNA states in DX path
// RSPdx parameter enums
typedef enum
{
sdrplay_api_RspDx_ANTENNA_A = 0,
sdrplay_api_RspDx_ANTENNA_B = 1,
sdrplay_api_RspDx_ANTENNA_C = 2,
} sdrplay_api_RspDx_AntennaSelectT;
typedef enum
{
sdrplay_api_RspDx_HDRMODE_BW_0_200 = 0,
sdrplay_api_RspDx_HDRMODE_BW_0_500 = 1,
sdrplay_api_RspDx_HDRMODE_BW_1_200 = 2,
sdrplay_api_RspDx_HDRMODE_BW_1_700 = 3,
} sdrplay_api_RspDx_HdrModeBwT;
// RSPdx parameter structs
typedef struct
{
unsigned char hdrEnable; // default: 0
unsigned char biasTEnable; // default: 0
sdrplay_api_RspDx_AntennaSelectT antennaSel; // default: sdrplay_api_RspDx_ANTENNA_A
unsigned char rfNotchEnable; // default: 0
unsigned char rfDabNotchEnable; // default: 0
} sdrplay_api_RspDxParamsT;
typedef struct
{
sdrplay_api_RspDx_HdrModeBwT hdrBw; // default: sdrplay_api_RspDx_HDRMODE_BW_1_700
} sdrplay_api_RspDxTunerParamsT;
#endif //SDRPLAY_API_RSPDX_H

View File

@@ -1,21 +0,0 @@
#ifndef SDRPLAY_API_RX_CHANNEL_H
#define SDRPLAY_API_RX_CHANNEL_H
#include "sdrplay_api_tuner.h"
#include "sdrplay_api_control.h"
#include "sdrplay_api_rsp1a.h"
#include "sdrplay_api_rsp2.h"
#include "sdrplay_api_rspDuo.h"
#include "sdrplay_api_rspDx.h"
typedef struct
{
sdrplay_api_TunerParamsT tunerParams;
sdrplay_api_ControlParamsT ctrlParams;
sdrplay_api_Rsp1aTunerParamsT rsp1aTunerParams;
sdrplay_api_Rsp2TunerParamsT rsp2TunerParams;
sdrplay_api_RspDuoTunerParamsT rspDuoTunerParams;
sdrplay_api_RspDxTunerParamsT rspDxTunerParams;
} sdrplay_api_RxChannelParamsT;
#endif //SDRPLAY_API_RX_CHANNEL_H

View File

@@ -1,93 +0,0 @@
#ifndef SDRPLAY_API_TUNER_H
#define SDRPLAY_API_TUNER_H
#define MAX_BB_GR (59)
// Tuner parameter enums
typedef enum
{
sdrplay_api_BW_Undefined = 0,
sdrplay_api_BW_0_200 = 200,
sdrplay_api_BW_0_300 = 300,
sdrplay_api_BW_0_600 = 600,
sdrplay_api_BW_1_536 = 1536,
sdrplay_api_BW_5_000 = 5000,
sdrplay_api_BW_6_000 = 6000,
sdrplay_api_BW_7_000 = 7000,
sdrplay_api_BW_8_000 = 8000
} sdrplay_api_Bw_MHzT;
typedef enum
{
sdrplay_api_IF_Undefined = -1,
sdrplay_api_IF_Zero = 0,
sdrplay_api_IF_0_450 = 450,
sdrplay_api_IF_1_620 = 1620,
sdrplay_api_IF_2_048 = 2048
} sdrplay_api_If_kHzT;
typedef enum
{
sdrplay_api_LO_Undefined = 0,
sdrplay_api_LO_Auto = 1,
sdrplay_api_LO_120MHz = 2,
sdrplay_api_LO_144MHz = 3,
sdrplay_api_LO_168MHz = 4
} sdrplay_api_LoModeT;
typedef enum
{
sdrplay_api_EXTENDED_MIN_GR = 0,
sdrplay_api_NORMAL_MIN_GR = 20
} sdrplay_api_MinGainReductionT;
typedef enum
{
sdrplay_api_Tuner_Neither = 0,
sdrplay_api_Tuner_A = 1,
sdrplay_api_Tuner_B = 2,
sdrplay_api_Tuner_Both = 3,
} sdrplay_api_TunerSelectT;
// Tuner parameter structs
typedef struct
{
float curr;
float max;
float min;
} sdrplay_api_GainValuesT;
typedef struct
{
int gRdB; // default: 50
unsigned char LNAstate; // default: 0
unsigned char syncUpdate; // default: 0
sdrplay_api_MinGainReductionT minGr; // default: sdrplay_api_NORMAL_MIN_GR
sdrplay_api_GainValuesT gainVals; // output parameter
} sdrplay_api_GainT;
typedef struct
{
double rfHz; // default: 200000000.0
unsigned char syncUpdate; // default: 0
} sdrplay_api_RfFreqT;
typedef struct
{
unsigned char dcCal; // default: 3 (Periodic mode)
unsigned char speedUp; // default: 0 (No speedup)
int trackTime; // default: 1 (=> time in uSec = (72 * 3 * trackTime) / 24e6 = 9uSec)
int refreshRateTime; // default: 2048 (=> time in uSec = (72 * 3 * refreshRateTime) / 24e6 = 18432uSec)
} sdrplay_api_DcOffsetTunerT;
typedef struct
{
sdrplay_api_Bw_MHzT bwType; // default: sdrplay_api_BW_0_200
sdrplay_api_If_kHzT ifType; // default: sdrplay_api_IF_Zero
sdrplay_api_LoModeT loMode; // default: sdrplay_api_LO_Auto
sdrplay_api_GainT gain;
sdrplay_api_RfFreqT rfFreq;
sdrplay_api_DcOffsetTunerT dcOffsetTuner;
} sdrplay_api_TunerParamsT;
#endif //SDRPLAY_API_TUNER_H

View File

@@ -124,12 +124,16 @@ dependencies {
implementation 'com.mpatric:mp3agic:0.9.1'
implementation 'commons-io:commons-io:2.22.0'
implementation 'eu.hansolo:charts:1.0.5'
implementation 'io.github.dsheirer:jbladerf:2.6.0' //bladeRF tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jhydrasdr:1.1.3' //HydraSDR tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jairspy:1.0.12.1' //Airspy tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jairspyhf:1.8.1.1' //Airspy HF+ tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jbladerf:2.6.0.1' //bladeRF tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jhackrf:2026.01.3.1' //HackRF tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jlibusb:1.0.30.1' //libusb and USB hotplug for the tuner libraries - GitHub release; not pulled in transitively
implementation 'io.github.dsheirer:jhydrasdr:1.1.4.1' //HydraSDR tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jlimesdr:25.1.0.2' //LimeSDR tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jrtl:1.4.0.2' //RTL-SDR tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:jsdrplay:3.15.3' //SDRplay RSP tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
implementation 'io.github.dsheirer:usb4java-native-libraries:1.3.3' //OSX & Windows aarch64 native libs
implementation 'io.github.dsheirer:usb4java:1.3.5' //Fork with Loader support for OSX Multi-Version
implementation 'javax.usb:usb-api:1.0.2'
implementation 'net.coderazzi:tablefilter-swing:5.5.4'
implementation 'org.apache.commons:commons-compress:1.28.0'
implementation 'org.apache.commons:commons-lang3:3.20.0'
@@ -140,8 +144,6 @@ dependencies {
implementation 'org.controlsfx:controlsfx:11.2.3'
implementation 'org.rauschig:jarchivelib:1.2.0'
implementation 'org.slf4j:slf4j-api:2.0.17'
implementation 'org.usb4java:libusb4java:1.3.0'
implementation 'org.usb4java:usb4java-javax:1.3.0'
implementation 'pl.edu.icm:JLargeArrays:1.6'
}
@@ -165,8 +167,7 @@ def jvmArgsWindows =
'--enable-preview', //Project Panama Vector API is still in incubator
'--enable-native-access=ALL-UNNAMED', //Suppress unsafe warnings for FFM API Foreign Memory calls
'--enable-native-access=javafx.graphics', //JDK24 - suppress restricted java.lang.System calls
'--sun-misc-unsafe-memory-access=allow', //JDK24 - suppress sun.misc.Unsafe::allocateMemory warning
'-Djava.library.path=c:\\Program Files\\SDRplay\\API\\x64'] //SDRPlay API library path
'--sun-misc-unsafe-memory-access=allow'] //JDK24 - suppress sun.misc.Unsafe::allocateMemory warning
/**
* JVM Arguments for running the application on Linux.
@@ -177,7 +178,7 @@ def jvmArgsLinux =
'--enable-preview', //Project Panama Vector & Foreign Function APIs remain in incubator
'--sun-misc-unsafe-memory-access=allow', //JDK24 - suppress sun.misc.Unsafe::allocateMemory warning
'--enable-native-access=javafx.graphics', //JDK24 - suppress restricted java.lang.System calls
'--enable-native-access=ALL-UNNAMED'] //SDRPlay API library path
'--enable-native-access=ALL-UNNAMED'] //Suppress warnings for FFM API native calls (tuner libraries)
application {
mainClass = "io.github.dsheirer.gui.SDRTrunk"

View File

@@ -28,4 +28,4 @@
# tag name, specifically the dashes inserted before and after the alpha/beta labels.
# See: https://github.com/DSheirer/sdrtrunk/issues/1651
projectVersion=0.6.2-beta-1
projectVersion=1.0.0-beta-1

View File

@@ -9,6 +9,47 @@ dependencyResolutionManagement {
mavenCentral()
maven { url 'https://jitpack.io' }
//jAirspy is published as a GitHub release, with the native libraries for all platforms in the jar
ivy {
url = 'https://github.com/DSheirer/jAirspy/releases/download'
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
metadataSources { artifact() }
content { includeModule 'io.github.dsheirer', 'jairspy' }
}
//jAirspyHF is published as a GitHub release, with the native libraries for all platforms in the jar
ivy {
url = 'https://github.com/DSheirer/jAirspyHF/releases/download'
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
metadataSources { artifact() }
content { includeModule 'io.github.dsheirer', 'jairspyhf' }
}
//jRTL is published as a GitHub release, with the native libraries for all platforms in the jar
ivy {
url = 'https://github.com/DSheirer/jRTL/releases/download'
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
metadataSources { artifact() }
content { includeModule 'io.github.dsheirer', 'jrtl' }
}
//jlibusb is published as a GitHub release, with libusb for all platforms in the jar. Tuner libraries built on
//jlibusb (jAirspy, jAirspyHF, jBladeRF, jHackRF, jHydraSDR, jLimeSDR, jRTL) load their native library next to its libusb, so the process shares one copy.
ivy {
url = 'https://github.com/DSheirer/jlibusb/releases/download'
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
metadataSources { artifact() }
content { includeModule 'io.github.dsheirer', 'jlibusb' }
}
//jHackRF is published as a GitHub release, with the native libraries for all platforms in the jar
ivy {
url = 'https://github.com/DSheirer/jHackRF/releases/download'
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
metadataSources { artifact() }
content { includeModule 'io.github.dsheirer', 'jhackrf' }
}
//jHydraSDR is published as a GitHub release, with the native libraries for all platforms in the jar
ivy {
url = 'https://github.com/DSheirer/jHydraSDR/releases/download'
@@ -17,6 +58,23 @@ dependencyResolutionManagement {
content { includeModule 'io.github.dsheirer', 'jhydrasdr' }
}
//jLimeSDR is published as a GitHub release, with the native libraries for all platforms in the jar
ivy {
url = 'https://github.com/DSheirer/jLimeSDR/releases/download'
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
metadataSources { artifact() }
content { includeModule 'io.github.dsheirer', 'jlimesdr' }
}
//jsdrplay is published as a GitHub release. The jar does not contain the SDRplay API native library, which
//users install separately from sdrplay.com
ivy {
url = 'https://github.com/DSheirer/jsdrplay/releases/download'
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
metadataSources { artifact() }
content { includeModule 'io.github.dsheirer', 'jsdrplay' }
}
//jBladeRF is published as a GitHub release, with the native libraries for all platforms in the jar
ivy {
url = 'https://github.com/DSheirer/jBladeRF/releases/download'

View File

@@ -1,132 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.airspy;
import io.github.dsheirer.buffer.AbstractNativeBuffer;
import io.github.dsheirer.sample.complex.ComplexSamples;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import java.util.Iterator;
/**
* Native buffer implementation for Airspy HF+ & Discovery tuners.
*/
public class AirspyHfNativeBuffer extends AbstractNativeBuffer
{
public static final float SCALE = 1.0f / 32768.0f;
private short[] mInterleavedSamples;
private float mAverageDc;
/**
* Constructs an instance.
* @param timestamp of the first sample of the buffer
* @param interleavedSamples pairs of 16-bit complex samples.
*/
public AirspyHfNativeBuffer(long timestamp, float samplesPerMillisecond, float averageDc, short[] interleavedSamples)
{
super(timestamp, samplesPerMillisecond);
mInterleavedSamples = interleavedSamples;
mAverageDc = averageDc;
}
@Override
public Iterator<ComplexSamples> iterator()
{
return new IteratorComplexSamples();
}
@Override
public Iterator<InterleavedComplexSamples> iteratorInterleaved()
{
return new IteratorInterleaved();
}
@Override
public int sampleCount()
{
return mInterleavedSamples.length / 2;
}
/**
* Scalar implementation of complex samples buffer iterator
*/
public class IteratorComplexSamples implements Iterator<ComplexSamples>
{
private boolean mHasNext = true;
@Override
public boolean hasNext()
{
return mHasNext;
}
@Override
public ComplexSamples next()
{
int length = mInterleavedSamples.length / 2;
float[] i = new float[length];
float[] q = new float[length];
int index = 0;
for(int x = 0; x < mInterleavedSamples.length; x += 2)
{
index = x / 2;
//Native ordering is in Q, I, Q, I ... reverse it to I, Q, I, Q
i[index] = (mInterleavedSamples[x + 1] * SCALE) - mAverageDc;
q[index] = (mInterleavedSamples[x] * SCALE) - mAverageDc;
}
mHasNext = false;
return new ComplexSamples(i, q, getTimestamp());
}
}
/**
* Scalar implementation of interleaved sample buffer iterator.
*/
public class IteratorInterleaved implements Iterator<InterleavedComplexSamples>
{
private boolean mHasNext = true;
@Override
public boolean hasNext()
{
return mHasNext;
}
@Override
public InterleavedComplexSamples next()
{
float[] samples = new float[mInterleavedSamples.length];
for(int x = 0; x < mInterleavedSamples.length; x += 2)
{
//Native ordering is in Q, I, Q, I ... reverse it to I, Q, I, Q
samples[x] = (mInterleavedSamples[x + 1] * SCALE) - mAverageDc;
samples[x + 1] = (mInterleavedSamples[x] * SCALE) - mAverageDc;
}
mHasNext = false;
return new InterleavedComplexSamples(samples, getTimestamp());
}
}
}

View File

@@ -1,83 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.airspy;
import io.github.dsheirer.buffer.AbstractNativeBufferFactory;
import io.github.dsheirer.buffer.DcCorrectionManager;
import io.github.dsheirer.buffer.INativeBuffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
/**
* Airspy HF+ native buffer factory.
*
* Note: the tuner provides 1024 samples in each transfer where samples are 16-bit interleaved complex.
*/
public class AirspyHfNativeBufferFactory extends AbstractNativeBufferFactory
{
private DcCorrectionManager mDcCorrectionManager = new DcCorrectionManager();
/**
* Constructs an instance
*/
public AirspyHfNativeBufferFactory()
{
}
/**
* Converts the samples byte buffer into a native buffer and calculates DC offset
* @param samples byte array copied from native memory
* @param timestamp of the samples
* @return constructed native buffer
*/
@Override
public INativeBuffer getBuffer(ByteBuffer samples, long timestamp)
{
ShortBuffer shortBuffer = samples.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] converted = new short[shortBuffer.capacity()];
shortBuffer.get(converted);
if(mDcCorrectionManager.shouldCalculateDc())
{
calculateDc(converted);
}
return new AirspyHfNativeBuffer(timestamp, getSamplesPerMillisecond(), mDcCorrectionManager.getAverageDc(), converted);
}
/**
* Calculates the average DC in the sample stream so that it can be subtracted from the samples when the
* native buffer is used.
* @param samples containing DC offset
*/
private void calculateDc(short[] samples)
{
float dcAccumulator = 0;
for(short sample: samples)
{
dcAccumulator += sample;
}
dcAccumulator /= samples.length;
mDcCorrectionManager.adjust(dcAccumulator * AirspyHfNativeBuffer.SCALE);
}
}

View File

@@ -1,119 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.dsp.filter.hilbert.HilbertTransform;
import java.util.Iterator;
/**
* Base complex samples iterator for raw sample buffers. Incorporates a Hilbert transform filter
* for converting the real sample array to complex samples.
* @param <T> either ComplexSamples or InterleavedComplexSamples
*/
public abstract class SampleBufferIterator<T> implements Iterator<T>
{
protected static final float SCALE_SIGNED_12_BIT_TO_FLOAT = 1.0f / 2048.0f;
protected static final int FRAGMENT_SIZE = 4096;
protected static final float[] COEFFICIENTS =
HilbertTransform.convertHalfBandToHilbert(HilbertTransform.HALF_BAND_FILTER_47_TAP);
public static final int I_OVERLAP = 11;
public static final int Q_OVERLAP = 23;
protected float[] mIBuffer = new float[FRAGMENT_SIZE + I_OVERLAP];
protected float[] mQBuffer = new float[FRAGMENT_SIZE + Q_OVERLAP];
protected short[] mSamples;
protected int mSamplesPointer = 0;
protected float mAverageDc;
private long mTimestamp;
private float mSamplesPerMillisecond;
/**
* Constructs an instance
* @param samples from the tuner.
* @param residualI samples from previous buffer
* @param residualQ samples from previous buffer
* @param averageDc as measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleBufferIterator(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp,
float samplesPerMillisecond)
{
if(residualI.length != I_OVERLAP || residualQ.length != Q_OVERLAP)
{
throw new IllegalArgumentException("Residual I length [" + residualI.length +
"] must be " + I_OVERLAP + " and Residual Q length [" + residualQ.length +
"] must be " + Q_OVERLAP);
}
int requiredInterval = FRAGMENT_SIZE * 2; //requires 4 bytes (2 samples) per fragment
if(samples.length % requiredInterval != 0)
{
throw new IllegalArgumentException("Samples short array length [" + mSamples.length +
"]must be an integer multiple of " + requiredInterval);
}
mAverageDc = averageDc;
mSamples = samples;
mTimestamp = timestamp;
mSamplesPerMillisecond = samplesPerMillisecond;
//Transfer and scale the residual I & Q samples from the previous buffer
for(int i = 0; i < residualI.length; i++)
{
mIBuffer[i] = scale(residualI[i], averageDc);
}
for(int q = 0; q < residualQ.length; q++)
{
mQBuffer[q] = scale(residualQ[q], averageDc);
}
}
/**
* Calculates the timestamp for a samples buffer fragment based on where it is extracted from relative to the
* native buffer starting timestamp.
* @param samplesPointer for the start of the fragment. Note: this value will be divided by 2 to account for I/Q sample pairs.
* @return timestamp adjusted to the fragment or sub-buffer start sample.
*/
protected long getFragmentTimestamp(int samplesPointer)
{
return mTimestamp + (long)(samplesPointer / 2 / mSamplesPerMillisecond);
}
@Override
public boolean hasNext()
{
return mSamplesPointer < mSamples.length;
}
/**
* Scales the short sample value to a 12-bit floating point and subtracts the average DC offset
* @param value to scale
* @param averageDc detected
* @return scaled and normalized sample
*/
public static float scale(short value, float averageDc)
{
return ((float)value - 2048) * SCALE_SIGNED_12_BIT_TO_FLOAT - averageDc;
}
}

View File

@@ -1,98 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.ComplexSamples;
import java.util.Arrays;
/**
* Scalar implementation for non-packed native buffers
*/
public class SampleBufferIteratorScalar extends SampleBufferIterator<ComplexSamples>
{
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
}
@Override
public ComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
mIBuffer[x + I_OVERLAP] = scale(mSamples[offset++], mAverageDc);
mQBuffer[x + Q_OVERLAP] = scale(mSamples[offset++], mAverageDc);
}
mSamplesPointer = offset;
//The i line is a simple delay line, so we'll just copy those as a new array here.
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
float[] q = new float[FRAGMENT_SIZE];
float accumulator;
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = 0;
for(int tap = 0; tap < COEFFICIENTS.length; tap++)
{
accumulator += COEFFICIENTS[tap] * mQBuffer[x + tap];
}
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
q[x] = accumulator;
}
else
{
i[x] = -i[x];
q[x] = -accumulator;
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new ComplexSamples(i, q, timestamp);
}
}

View File

@@ -1,128 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.ComplexSamples;
import java.util.Arrays;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Vector SIMD implementation for non-packed native buffers
*/
public class SampleBufferIteratorVector128Bits extends SampleBufferIterator<ComplexSamples>
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
}
@Override
public ComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
int fragmentPointer = 0;
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
while(fragmentPointer < FRAGMENT_SIZE)
{
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
{
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
scaledSamples[y] = mSamples[offset++];
}
//SIMD scaling operation
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
.sub(2048.0f)
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
.sub(mAverageDc)
.intoArray(scaledSamples, 0);
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
{
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
}
}
mSamplesPointer = offset;
//The i line is a simple delay line, so we'll just copy those as a new array here.
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
float[] q = new float[FRAGMENT_SIZE];
FloatVector accumulator;
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 4);
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
FloatVector f4 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 12);
FloatVector f5 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
FloatVector f6 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 20);
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = FloatVector.zero(VECTOR_SPECIES);
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 4), accumulator);
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
accumulator = f4.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 12), accumulator);
accumulator = f5.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
accumulator = f6.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 20), accumulator);
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
q[x] = accumulator.reduceLanes(VectorOperators.ADD);
}
else
{
i[x] = -i[x];
q[x] = -accumulator.reduceLanes(VectorOperators.ADD);
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new ComplexSamples(i, q, timestamp);
}
}

View File

@@ -1,122 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.ComplexSamples;
import java.util.Arrays;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Vector SIMD implementation for non-packed native buffers
*/
public class SampleBufferIteratorVector256Bits extends SampleBufferIterator<ComplexSamples>
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
}
@Override
public ComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
int fragmentPointer = 0;
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
while(fragmentPointer < FRAGMENT_SIZE)
{
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
{
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
scaledSamples[y] = mSamples[offset++];
}
//SIMD scaling operation
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
.sub(2048.0f)
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
.sub(mAverageDc)
.intoArray(scaledSamples, 0);
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
{
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
}
}
mSamplesPointer = offset;
//The i line is a simple delay line, so we'll just copy those as a new array here.
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
float[] q = new float[FRAGMENT_SIZE];
FloatVector accumulator;
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = FloatVector.zero(VECTOR_SPECIES);
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
q[x] = accumulator.reduceLanes(VectorOperators.ADD);
}
else
{
i[x] = -i[x];
q[x] = -accumulator.reduceLanes(VectorOperators.ADD);
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new ComplexSamples(i, q, timestamp);
}
}

View File

@@ -1,125 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.ComplexSamples;
import java.util.Arrays;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Vector SIMD implementation for non-packed native buffers
*/
public class SampleBufferIteratorVector512Bits extends SampleBufferIterator<ComplexSamples>
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
private float[] mFilterPart2;
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
//Arrange the last 8 taps of the filter into the higher indices of an array to align for SIMD
mFilterPart2 = new float[16];
System.arraycopy(COEFFICIENTS, 16, mFilterPart2, 8, 8);
}
@Override
public ComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
int fragmentPointer = 0;
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
while(fragmentPointer < FRAGMENT_SIZE)
{
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
{
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
scaledSamples[y] = mSamples[offset++];
}
//SIMD scaling operation
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
.sub(2048.0f)
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
.sub(mAverageDc)
.intoArray(scaledSamples, 0);
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
{
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
}
}
mSamplesPointer = offset;
//The i line is a simple delay line, so we'll just copy those as a new array here.
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
float[] q = new float[FRAGMENT_SIZE];
FloatVector accumulator;
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, mFilterPart2, 0);
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = FloatVector.zero(VECTOR_SPECIES);
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
q[x] = accumulator.reduceLanes(VectorOperators.ADD);
}
else
{
i[x] = -i[x];
q[x] = -accumulator.reduceLanes(VectorOperators.ADD);
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new ComplexSamples(i, q, timestamp);
}
}

View File

@@ -1,141 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.ComplexSamples;
import java.util.Arrays;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Vector SIMD implementation for non-packed native buffers
*/
public class SampleBufferIteratorVector64Bits extends SampleBufferIterator<ComplexSamples>
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
}
@Override
public ComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
int fragmentPointer = 0;
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
while(fragmentPointer < FRAGMENT_SIZE)
{
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
{
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
scaledSamples[y] = mSamples[offset++];
}
//SIMD scaling operation
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
.sub(2048.0f)
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
.sub(mAverageDc)
.intoArray(scaledSamples, 0);
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
{
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
}
}
mSamplesPointer = offset;
//The i line is a simple delay line, so we'll just copy those as a new array here.
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
float[] q = new float[FRAGMENT_SIZE];
FloatVector accumulator;
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 2);
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 4);
FloatVector f4 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 6);
FloatVector f5 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
FloatVector f6 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 10);
FloatVector f7 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 12);
FloatVector f8 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 14);
FloatVector f9 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
FloatVector f10 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 18);
FloatVector f11 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 20);
FloatVector f12 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 22);
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = FloatVector.zero(VECTOR_SPECIES);
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 2), accumulator);
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 4), accumulator);
accumulator = f4.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 6), accumulator);
accumulator = f5.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
accumulator = f6.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 10), accumulator);
accumulator = f7.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 12), accumulator);
accumulator = f8.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 14), accumulator);
accumulator = f9.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
accumulator = f10.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 18), accumulator);
accumulator = f11.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 20), accumulator);
accumulator = f12.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 22), accumulator);
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
q[x] = accumulator.reduceLanes(VectorOperators.ADD);
}
else
{
i[x] = -i[x];
q[x] = -accumulator.reduceLanes(VectorOperators.ADD);
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new ComplexSamples(i, q, timestamp);
}
}

View File

@@ -1,96 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
/**
* Scalar implementation for non-packed native buffers
*/
public class SampleInterleavedBufferIteratorScalar extends SampleBufferIterator<InterleavedComplexSamples>
{
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleInterleavedBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
}
@Override
public InterleavedComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
mIBuffer[x + I_OVERLAP] = scale(mSamples[offset++], mAverageDc);
mQBuffer[x + Q_OVERLAP] = scale(mSamples[offset++], mAverageDc);
}
mSamplesPointer = offset;
float[] samples = new float[FRAGMENT_SIZE * 2];
float accumulator;
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = 0;
for(int tap = 0; tap < COEFFICIENTS.length; tap++)
{
accumulator += COEFFICIENTS[tap] * mQBuffer[x + tap];
}
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
samples[2 * x] = mIBuffer[x];
samples[2 * x + 1] = accumulator;
}
else
{
samples[2 * x] = -mIBuffer[x];
samples[2 * x + 1] = -accumulator;
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new InterleavedComplexSamples(samples, timestamp);
}
}

View File

@@ -1,125 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Vector SIMD implementation for non-packed native buffers
*/
public class SampleInterleavedBufferIteratorVector128Bits extends SampleBufferIterator<InterleavedComplexSamples>
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleInterleavedBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
}
@Override
public InterleavedComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
int fragmentPointer = 0;
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
while(fragmentPointer < FRAGMENT_SIZE)
{
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
{
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
scaledSamples[y] = mSamples[offset++];
}
//SIMD scaling operation
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
.sub(2048.0f)
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
.sub(mAverageDc)
.intoArray(scaledSamples, 0);
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
{
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
}
}
mSamplesPointer = offset;
float[] samples = new float[FRAGMENT_SIZE * 2];
FloatVector accumulator;
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 4);
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
FloatVector f4 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 12);
FloatVector f5 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
FloatVector f6 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 20);
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = FloatVector.zero(VECTOR_SPECIES);
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 4), accumulator);
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
accumulator = f4.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 12), accumulator);
accumulator = f5.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
accumulator = f6.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 20), accumulator);
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
samples[2 * x] = mIBuffer[x];
samples[2 * x + 1] = accumulator.reduceLanes(VectorOperators.ADD);
}
else
{
samples[2 * x] = -mIBuffer[x];
samples[2 * x + 1] = -accumulator.reduceLanes(VectorOperators.ADD);
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new InterleavedComplexSamples(samples, timestamp);
}
}

View File

@@ -1,119 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Vector SIMD implementation for non-packed native buffers
*/
public class SampleInterleavedBufferIteratorVector256Bits extends SampleBufferIterator<InterleavedComplexSamples>
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleInterleavedBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
}
@Override
public InterleavedComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
int fragmentPointer = 0;
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
while(fragmentPointer < FRAGMENT_SIZE)
{
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
{
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
scaledSamples[y] = mSamples[offset++];
}
//SIMD scaling operation
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
.sub(2048.0f)
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
.sub(mAverageDc)
.intoArray(scaledSamples, 0);
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
{
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
}
}
mSamplesPointer = offset;
float[] samples = new float[FRAGMENT_SIZE * 2];
FloatVector accumulator;
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = FloatVector.zero(VECTOR_SPECIES);
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
samples[2 * x] = mIBuffer[x];
samples[2 * x + 1] = accumulator.reduceLanes(VectorOperators.ADD);
}
else
{
samples[2 * x] = -mIBuffer[x];
samples[2 * x + 1] = -accumulator.reduceLanes(VectorOperators.ADD);
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new InterleavedComplexSamples(samples, timestamp);
}
}

View File

@@ -1,122 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Vector SIMD implementation for non-packed native buffers
*/
public class SampleInterleavedBufferIteratorVector512Bits extends SampleBufferIterator<InterleavedComplexSamples>
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
private float[] mFilterPart2;
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleInterleavedBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
//Arrange the last 8 taps of the filter into the higher indices of an array to align for SIMD
mFilterPart2 = new float[16];
System.arraycopy(COEFFICIENTS, 16, mFilterPart2, 8, 8);
}
@Override
public InterleavedComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
int fragmentPointer = 0;
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
while(fragmentPointer < FRAGMENT_SIZE)
{
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
{
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
scaledSamples[y] = mSamples[offset++];
}
//SIMD scaling operation
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
.sub(2048.0f)
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
.sub(mAverageDc)
.intoArray(scaledSamples, 0);
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
{
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
}
}
mSamplesPointer = offset;
float[] samples = new float[FRAGMENT_SIZE * 2];
FloatVector accumulator;
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, mFilterPart2, 0);
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = FloatVector.zero(VECTOR_SPECIES);
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
samples[2 * x] = mIBuffer[x];
samples[2 * x + 1] = accumulator.reduceLanes(VectorOperators.ADD);
}
else
{
samples[2 * x] = -mIBuffer[x];
samples[2 * x + 1] = -accumulator.reduceLanes(VectorOperators.ADD);
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new InterleavedComplexSamples(samples, timestamp);
}
}

View File

@@ -1,137 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Vector SIMD implementation for non-packed native buffers
*/
public class SampleInterleavedBufferIteratorVector64Bits extends SampleBufferIterator<InterleavedComplexSamples>
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
/**
* Constructs an instance
*
* @param samples from the device, either packed or unpacked.
* @param residualI samples from last buffer
* @param residualQ samples from last buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
*/
public SampleInterleavedBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, float samplesPerMillisecond)
{
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
}
@Override
public InterleavedComplexSamples next()
{
if(mSamplesPointer >= mSamples.length)
{
throw new IllegalStateException("End of buffer exceeded");
}
long timestamp = getFragmentTimestamp(mSamplesPointer);
int offset = mSamplesPointer;
int fragmentPointer = 0;
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
while(fragmentPointer < FRAGMENT_SIZE)
{
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
{
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
scaledSamples[y] = mSamples[offset++];
}
//SIMD scaling operation
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
.sub(2048.0f)
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
.sub(mAverageDc)
.intoArray(scaledSamples, 0);
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
{
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
}
}
mSamplesPointer = offset;
float[] samples = new float[FRAGMENT_SIZE * 2];
FloatVector accumulator;
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 2);
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 4);
FloatVector f4 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 6);
FloatVector f5 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
FloatVector f6 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 10);
FloatVector f7 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 12);
FloatVector f8 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 14);
FloatVector f9 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
FloatVector f10 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 18);
FloatVector f11 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 20);
FloatVector f12 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 22);
for(int x = 0; x < FRAGMENT_SIZE; x++)
{
accumulator = FloatVector.zero(VECTOR_SPECIES);
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 2), accumulator);
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 4), accumulator);
accumulator = f4.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 6), accumulator);
accumulator = f5.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
accumulator = f6.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 10), accumulator);
accumulator = f7.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 12), accumulator);
accumulator = f8.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 14), accumulator);
accumulator = f9.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
accumulator = f10.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 18), accumulator);
accumulator = f11.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 20), accumulator);
accumulator = f12.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 22), accumulator);
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
if(x % 2 == 0)
{
samples[2 * x] = mIBuffer[x];
samples[2 * x + 1] = accumulator.reduceLanes(VectorOperators.ADD);
}
else
{
samples[2 * x] = -mIBuffer[x];
samples[2 * x + 1] = -accumulator.reduceLanes(VectorOperators.ADD);
}
}
//Copy residual end samples to beginning of buffers for the next iteration
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
return new InterleavedComplexSamples(samples, timestamp);
}
}

View File

@@ -1,103 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2026 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.sample;
import io.github.dsheirer.buffer.AbstractNativeBuffer;
import io.github.dsheirer.sample.complex.ComplexSamples;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import io.github.dsheirer.vector.calibrate.Implementation;
import java.util.Iterator;
/**
* Native buffer scalar implementation for non-packed samples.
*/
public class SampleNativeBuffer extends AbstractNativeBuffer
{
private final short[] mSamples;
private final short[] mResidualI;
private final short[] mResidualQ;
private final float mAverageDc;
private final Implementation mInterleavedImplementation;
private final Implementation mNonInterleavedImplementation;
/**
* Constructs an instance
* @param samples (non-packed) from the device
* @param residualI samples from previous buffer
* @param residualQ samples from previous buffer
* @param averageDc measured
* @param timestamp of the buffer
* @param interleavedImplementation optimal, scalar vs vector SIMD
* @param nonInterleavedImplementation optimal, scalar vs vector SIMD
* @param samplesPerMillisecond used to calculate sub-buffer fragment timestamp offsets from the start of this buffer.
*/
public SampleNativeBuffer(short[] samples, short[] residualI, short[] residualQ, float averageDc,
long timestamp, Implementation interleavedImplementation,
Implementation nonInterleavedImplementation, float samplesPerMillisecond)
{
super(timestamp, samplesPerMillisecond);
//Ensure we're an even multiple of the fragment size. Typically, this will be 64k or 128k
if(samples.length % SampleBufferIterator.FRAGMENT_SIZE != 0)
{
throw new IllegalArgumentException("Samples short[] length [" + samples.length +
"] must be an even multiple of " + SampleBufferIterator.FRAGMENT_SIZE);
}
mSamples = samples;
mResidualI = residualI;
mResidualQ = residualQ;
mAverageDc = averageDc;
mInterleavedImplementation = interleavedImplementation;
mNonInterleavedImplementation = nonInterleavedImplementation;
}
@Override
public int sampleCount()
{
return mSamples.length / 2;
}
@Override
public Iterator<ComplexSamples> iterator()
{
return switch(mNonInterleavedImplementation)
{
case VECTOR_SIMD_512 -> new SampleBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
case VECTOR_SIMD_256-> new SampleBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
case VECTOR_SIMD_128-> new SampleBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
case VECTOR_SIMD_64 ->new SampleBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
default -> new SampleBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
};
}
@Override
public Iterator<InterleavedComplexSamples> iteratorInterleaved()
{
return switch(mInterleavedImplementation)
{
case VECTOR_SIMD_512 -> new SampleInterleavedBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
case VECTOR_SIMD_256-> new SampleInterleavedBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
case VECTOR_SIMD_128-> new SampleInterleavedBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
case VECTOR_SIMD_64 ->new SampleInterleavedBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
default -> new SampleInterleavedBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
};
}
}

View File

@@ -1,135 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.buffer.AbstractNativeBufferFactory;
import io.github.dsheirer.buffer.INativeBuffer;
import io.github.dsheirer.vector.calibrate.CalibrationManager;
import io.github.dsheirer.vector.calibrate.CalibrationType;
import io.github.dsheirer.vector.calibrate.Implementation;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* Implements a factory for creating SignedByteNativeBuffer instances
*/
public class SampleNativeBufferFactory extends AbstractNativeBufferFactory
{
private boolean mSamplePacking = false;
private short[] mResidualI = new short[SampleBufferIterator.I_OVERLAP];
private short[] mResidualQ = new short[SampleBufferIterator.Q_OVERLAP];
private ISampleConverter mConverter;
private Implementation mInterleavedIteratorImplementation;
private Implementation mNonInterleavedIteratorImplementation;
/**
* Constructs an instance
*/
public SampleNativeBufferFactory()
{
updateConverter();
mInterleavedIteratorImplementation = CalibrationManager.getInstance()
.getImplementation(CalibrationType.SAMPLE_UNPACKED_INTERLEAVED_ITERATOR);
mNonInterleavedIteratorImplementation = CalibrationManager.getInstance()
.getImplementation(CalibrationType.SAMPLE_UNPACKED_ITERATOR);
}
/**
* Sample packing places two 12-bit samples into 3 bytes when enabled or
* places two 12-bit samples into 4 bytes when disabled.
*
* @param enabled
*/
public void setSamplePacking(boolean enabled)
{
mSamplePacking = enabled;
updateConverter();
}
/**
* Creates an optimal sample converter instance.
*/
private void updateConverter()
{
if(mSamplePacking)
{
if(!(mConverter instanceof ScalarPackedSampleConverter))
{
mConverter = new ScalarPackedSampleConverter();
}
}
else
{
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.SAMPLE_PACKED_CONVERTER);
if(implementation == Implementation.VECTOR_SIMD_PREFERRED)
{
if(!(mConverter instanceof VectorUnpackedSampleConverter))
{
mConverter = new VectorUnpackedSampleConverter();
}
}
else
{
if(!(mConverter instanceof ScalarUnpackedSampleConverter))
{
mConverter = new ScalarUnpackedSampleConverter();
}
}
}
}
@Override
public INativeBuffer getBuffer(ByteBuffer buffer, long timestamp)
{
short[] samples = mConverter.convert(buffer);
INativeBuffer nativeBuffer = new SampleNativeBuffer(samples,
Arrays.copyOf(mResidualI, mResidualI.length),
Arrays.copyOf(mResidualQ, mResidualQ.length), mConverter.getAverageDc(), timestamp,
mInterleavedIteratorImplementation, mNonInterleavedIteratorImplementation, getSamplesPerMillisecond());
extractResidual(samples);
return nativeBuffer;
}
/**
* Extracts the residual overlap samples needed for continuity in the Hilbert transform filter.
* @param samples to extract residual from
*/
private void extractResidual(short[] samples)
{
int offset = samples.length - (SampleBufferIterator.I_OVERLAP * 2);
for(int i = 0; i < SampleBufferIterator.I_OVERLAP; i++)
{
mResidualI[i] = samples[offset + (2 * i)];
}
offset = samples.length - (SampleBufferIterator.Q_OVERLAP * 2) + 1;
for(int q = 0; q < SampleBufferIterator.Q_OVERLAP; q++)
{
mResidualQ[q] = samples[offset + (2 * q)];
}
}
}

View File

@@ -1,120 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.buffer.DcCorrectionManager;
import java.nio.ByteBuffer;
/**
* Scalar implementation of sample converter for un-packed samples.
*/
public class ScalarPackedSampleConverter implements ISampleConverter
{
/**
* Manages DC calculations and processing interval
*/
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
@Override
public short[] convert(ByteBuffer buffer)
{
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
int offset = 0;
short sample;
short[] samples;
byte b1, b2, b3;
samples = new short[buffer.capacity() / 3 * 2];
if(shouldCalculateDc)
{
long dcAccumulator = 0;
for(int x = 0; x < samples.length; x += 2)
{
b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
b3 = buffer.get(offset++);
sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
samples[x] = sample;
dcAccumulator += sample;
sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF));
samples[x] = sample;
dcAccumulator += sample;
}
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= SampleBufferIterator.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++);
sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
samples[x] = sample;
sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF));
samples[x] = sample;
}
}
return samples;
}
@Override
public float getAverageDc()
{
return mDcCalculationManager.getAverageDc();
}
public static void main(String[] args)
{
byte[] raw = new byte[262144];
ByteBuffer buffer = ByteBuffer.wrap(raw);
ScalarPackedSampleConverter scalar = new ScalarPackedSampleConverter();
VectorUnpackedSampleConverter vector = new VectorUnpackedSampleConverter();
long accumulator = 0;
int iterations = 1_000_000;
System.out.println("Starting ...");
long start = System.currentTimeMillis();
for(int count = 0; count < iterations; count++)
{
// short[] samples = scalar.convert(buffer);
short[] samples = vector.convert(buffer);
accumulator += samples[1];
}
long elapsed = System.currentTimeMillis() - start;
System.out.println("Accumulator: " + accumulator);
System.out.println("Elapsed: " + (elapsed / 1000.0f));
System.out.println("Finished");
}
}

View File

@@ -1,83 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.buffer.DcCorrectionManager;
import java.nio.ByteBuffer;
/**
* Scalar implementation of sample converter for un-packed samples.
*/
public class ScalarUnpackedSampleConverter implements ISampleConverter
{
/**
* Manages DC calculations and processing interval
*/
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
@Override
public short[] convert(ByteBuffer buffer)
{
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
int offset = 0;
short sample;
short[] samples;
byte b1, b2;
samples = new short[buffer.capacity() / 2];
if(shouldCalculateDc)
{
long dcAccumulator = 0;
for(int x = 0; x < samples.length; x++)
{
b1 = buffer.get(offset++);
b2 = buffer.get(offset++);
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[x] = sample;
dcAccumulator += sample;
}
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= SampleBufferIterator.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;
}
@Override
public float getAverageDc()
{
return mDcCalculationManager.getAverageDc();
}
}

View File

@@ -1,129 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.sample;
import io.github.dsheirer.buffer.DcCorrectionManager;
import java.nio.ByteBuffer;
import jdk.incubator.vector.ShortVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* Scalar implementation of sample converter for un-packed samples.
*/
public class VectorUnpackedSampleConverter implements ISampleConverter
{
private static final VectorSpecies<Short> VECTOR_SPECIES = ShortVector.SPECIES_PREFERRED;
/**
* Manages DC calculations and processing interval
*/
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
@Override
public short[] convert(ByteBuffer buffer)
{
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
short sample;
short[] samples;
byte b1, b2;
samples = new short[buffer.capacity() / 2];
int bytesOffset;
int rawPointer = 0;
ShortVector vector;
int samplesOffset = 0;
short[] bytes1 = new short[VECTOR_SPECIES.length()];
short[] bytes2 = new short[VECTOR_SPECIES.length()];
if(shouldCalculateDc)
{
long dcAccumulator = 0;
for(; samplesOffset < VECTOR_SPECIES.loopBound(samples.length); samplesOffset += VECTOR_SPECIES.length())
{
bytesOffset = 0;
while(bytesOffset < VECTOR_SPECIES.length())
{
bytes1[bytesOffset] = (short)(buffer.get(rawPointer++) & 0xFF);
bytes2[bytesOffset++] = (short)(buffer.get(rawPointer++) & 0x0F);
}
vector = ShortVector.fromArray(VECTOR_SPECIES, bytes1, 0)
.or(ShortVector.fromArray(VECTOR_SPECIES, bytes2, 0).lanewise(VectorOperators.LSHL, 8));
dcAccumulator += vector.reduceLanes(VectorOperators.ADD);
vector.intoArray(samples, samplesOffset);
}
for(; samplesOffset < samples.length; samplesOffset++)
{
b1 = buffer.get(rawPointer++);
b2 = buffer.get(rawPointer++);
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
samples[samplesOffset] = sample;
dcAccumulator += sample;
}
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
averageDcNow *= SampleBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
mDcCalculationManager.adjust(averageDcNow);
}
else
{
for(; samplesOffset < VECTOR_SPECIES.loopBound(samples.length); samplesOffset += VECTOR_SPECIES.length())
{
bytesOffset = 0;
while(bytesOffset < VECTOR_SPECIES.length())
{
bytes1[bytesOffset] = (short)(buffer.get(rawPointer++) & 0xFF);
bytes2[bytesOffset++] = (short)(buffer.get(rawPointer++) & 0x0F);
}
vector = ShortVector.fromArray(VECTOR_SPECIES, bytes1, 0)
.or(ShortVector.fromArray(VECTOR_SPECIES, bytes2, 0).lanewise(VectorOperators.LSHL, 8));
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;
}
}
return samples;
}
@Override
public float getAverageDc()
{
return mDcCalculationManager.getAverageDc();
}
}

View File

@@ -257,6 +257,20 @@ public abstract class C4FMSymbolProcessor
return true;
}
/**
* Maximum bit errors allowed in a detected sync pattern while acquiring (ie not synchronized), measured from the
* symbol decisions at the optimized symbol timing. Sync detections with more bit errors are rejected. Bit errors
* include symbol magnitude (ie +3 vs +1) errors, so this rejects noise where the symbol signs correlate with the
* sync pattern but the symbol magnitudes are inconsistent. Once synchronized, the limit isn't applied since the
* sync detection timing is known and rejecting a valid sync detection costs the burst. Default implementation
* imposes no limit.
* @return maximum sync pattern bit error count while acquiring.
*/
public int getSyncAcquisitionMaxBitErrors()
{
return Integer.MAX_VALUE;
}
/**
* Number of consecutive sync periods (capped at 3) where a sync was detected within the max message length.
*/
@@ -945,12 +959,14 @@ public abstract class C4FMSymbolProcessor
}
/**
* Indicates if this correction has a valid NID or the optimization (or detection) score is high quality.
* Indicates if the optimization score is high quality and, while acquiring, the sync pattern bit errors don't
* exceed the max.
* @return true if valid.
*/
public boolean isValid()
{
return mOptimizationScore > getSyncThresholdEqualized();
return mOptimizationScore > getSyncThresholdEqualized() &&
(mSynchronized || mCorrectedBitCount <= getSyncAcquisitionMaxBitErrors());
}
}

View File

@@ -56,7 +56,6 @@ import io.github.dsheirer.source.tuner.Tuner;
import io.github.dsheirer.source.tuner.TunerEvent;
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.source.tuner.sdrplay.api.SDRPlayLibraryHelper;
import io.github.dsheirer.source.tuner.ui.TunerSpectralDisplayManager;
import io.github.dsheirer.spectrum.DisableSpectrumWaterfallMenuItem;
import io.github.dsheirer.spectrum.ShowTunerMenuItem;
@@ -154,14 +153,6 @@ public class SDRTrunk implements Listener<TunerEvent>
mApplicationLog = new ApplicationLog(mUserPreferences);
mApplicationLog.start();
//Note: invoke this early in the application lifecycle, before the TunerManager causes the sdrplay classes
//to be loaded since the jextract auto-generated code attempts to load the library by name and that can fail
//when the library was not installed into a normal/default location, particularly on windows OS systems.
if(SDRPlayLibraryHelper.LOADED)
{
mLog.info("SDRPlay API native library preemptively loaded");
}
mResourceMonitor = new ResourceMonitor(mUserPreferences);
ThreadPool.logSettings();

View File

@@ -0,0 +1,84 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2026 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.gui.control;
import javafx.collections.ListChangeListener;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import org.controlsfx.control.SegmentedButton;
/**
* Screen reader (accessibility) utilities for JavaFX controls.
*/
public final class AccessibilityUtil
{
private AccessibilityUtil()
{
//Not instantiable
}
/**
* Provides an accessible name for each toggle button in a segmented button group, combining the group label with
* the button text (e.g. "Channel Bandwidth: 12.5 kHz").
*
* A segmented button is a container, so a label can't identify it via Label.setLabelFor() in a way that screen
* readers announce: each toggle button receives focus individually and would otherwise be announced by its own text
* only, without the context of the group. Buttons added to the group later are also named.
*
* @param groupLabel that visually identifies the segmented button group
* @param segmentedButton containing the toggle buttons
*/
public static void labelGroup(Label groupLabel, SegmentedButton segmentedButton)
{
String groupName = groupLabel.getText() == null ? "" : groupLabel.getText().trim();
//Remove a trailing colon so that the name doesn't announce "colon colon"
if(groupName.endsWith(":"))
{
groupName = groupName.substring(0, groupName.length() - 1).trim();
}
final String name = groupName;
for(ToggleButton button: segmentedButton.getButtons())
{
setGroupName(name, button);
}
segmentedButton.getButtons().addListener((ListChangeListener<ToggleButton>)change -> {
while(change.next())
{
for(ToggleButton added: change.getAddedSubList())
{
setGroupName(name, added);
}
}
});
}
/**
* Sets the accessible text for the button as the group name and button text.
*/
private static void setGroupName(String groupName, ToggleButton button)
{
String text = button.getText() == null ? "" : button.getText();
button.setAccessibleText(groupName.isEmpty() ? text : groupName + ": " + text);
}
}

View File

@@ -0,0 +1,80 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2026 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.gui.control;
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleAttribute;
import javafx.scene.AccessibleRole;
import org.controlsfx.control.ToggleSwitch;
/**
* ControlsFX toggle switch that exposes its role and on/off state to screen readers.
*
* The ControlsFX toggle switch doesn't provide an accessible role or selected state, so assistive technology sees it
* as a generic node with no state. This version reports itself as a toggle button, reports the selected state, and
* supports the accessible fire (toggle) action. The accessible name comes from the switch text, or from a label that
* identifies this switch via Label.setLabelFor().
*/
public class AccessibleToggleSwitch extends ToggleSwitch
{
/**
* Constructs an instance with no text. Use Label.setLabelFor() or setAccessibleText() to provide an accessible name.
*/
public AccessibleToggleSwitch()
{
this(null);
}
/**
* Constructs an instance
* @param text to display alongside the switch, also used as the accessible name.
*/
public AccessibleToggleSwitch(String text)
{
super(text);
setAccessibleRole(AccessibleRole.TOGGLE_BUTTON);
selectedProperty().addListener((observable, oldValue, newValue) ->
notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTED));
}
@Override
public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters)
{
if(attribute == AccessibleAttribute.SELECTED)
{
return isSelected();
}
return super.queryAccessibleAttribute(attribute, parameters);
}
@Override
public void executeAccessibleAction(AccessibleAction action, Object... parameters)
{
if(action == AccessibleAction.FIRE)
{
fire();
}
else
{
super.executeAccessibleAction(action, parameters);
}
}
}

View File

@@ -45,6 +45,7 @@ import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
/**
@@ -575,7 +576,17 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor
@Override
public void mouseWheelMoved(MouseWheelEvent e)
{
set(mValue - e.getWheelRotation());
//Swing delivers mouse wheel events to disabled components, so check enabled state explicitly so that
//the wheel can't bypass the lock when the tuner is providing channels.
if(isEnabled())
{
set(mValue - e.getWheelRotation());
}
else if(getParent() != null)
{
//Pass the event up so that an enclosing scroll pane can still scroll while this control is locked
getParent().dispatchEvent(SwingUtilities.convertMouseEvent(Digit.this, e, getParent()));
}
}
}
}

View File

@@ -21,6 +21,7 @@ package io.github.dsheirer.gui.playlist.alias;
import com.google.common.collect.Ordering;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.playlist.PlaylistManager;
@@ -100,6 +101,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
gridPane.getChildren().add(separator);
Label colorLabel = new Label("Color");
colorLabel.setLabelFor(getColorPicker());
GridPane.setHalignment(colorLabel, HPos.RIGHT);
GridPane.setConstraints(colorLabel, 0, ++row);
gridPane.getChildren().add(colorLabel);
@@ -114,6 +116,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
gridPane.getChildren().add(getResetColorButton());
Label iconLabel = new Label("Icon");
iconLabel.setLabelFor(getIconNodeComboBox());
GridPane.setHalignment(iconLabel, HPos.RIGHT);
GridPane.setConstraints(iconLabel, 0, ++row);
gridPane.getChildren().add(iconLabel);
@@ -125,6 +128,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
gridPane.getChildren().add(getApplyIconButton());
Label listenLabel = new Label("Listen");
listenLabel.setLabelFor(getMonitorAudioToggleSwitch());
GridPane.setHalignment(listenLabel, HPos.RIGHT);
GridPane.setConstraints(listenLabel, 0, ++row);
gridPane.getChildren().add(listenLabel);
@@ -133,6 +137,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
gridPane.getChildren().add(getMonitorAudioToggleSwitch());
Label priorityLabel = new Label("Priority");
priorityLabel.setLabelFor(getMonitorPriorityComboBox());
GridPane.setHalignment(priorityLabel, HPos.RIGHT);
GridPane.setConstraints(priorityLabel, 2, row);
gridPane.getChildren().add(priorityLabel);
@@ -144,6 +149,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
gridPane.getChildren().add(getApplyMonitorButton());
Label recordLabel = new Label("Record");
recordLabel.setLabelFor(getRecordToggleSwitch());
GridPane.setHalignment(recordLabel, HPos.RIGHT);
GridPane.setConstraints(recordLabel, 0, ++row);
gridPane.getChildren().add(recordLabel);
@@ -235,6 +241,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
if(mApplyColorButton == null)
{
mApplyColorButton = new Button("Apply");
mApplyColorButton.setAccessibleText("Apply color to selected aliases");
mApplyColorButton.setOnAction(event ->
{
startChange();
@@ -258,6 +265,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
if(mResetColorButton == null)
{
mResetColorButton = new Button("Reset Color");
mResetColorButton.setAccessibleText("Reset color for selected aliases");
mResetColorButton.setOnAction(event ->
{
startChange();
@@ -297,6 +305,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
if(mApplyIconButton == null)
{
mApplyIconButton = new Button("Apply");
mApplyIconButton.setAccessibleText("Apply icon to selected aliases");
mApplyIconButton.setDisable(true);
mApplyIconButton.setOnAction(new EventHandler<ActionEvent>()
{
@@ -327,7 +336,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
{
if(mMonitorAudioToggleSwitch == null)
{
mMonitorAudioToggleSwitch = new ToggleSwitch();
mMonitorAudioToggleSwitch = new AccessibleToggleSwitch();
mMonitorAudioToggleSwitch.setSelected(true);
mMonitorAudioToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -340,7 +349,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
{
if(mRecordToggleSwitch == null)
{
mRecordToggleSwitch = new ToggleSwitch();
mRecordToggleSwitch = new AccessibleToggleSwitch();
mRecordToggleSwitch.setSelected(false);
mRecordToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -374,6 +383,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
if(mApplyMonitorButton == null)
{
mApplyMonitorButton = new Button("Apply");
mApplyMonitorButton.setAccessibleText("Apply listen and priority to selected aliases");
mApplyMonitorButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
@@ -414,6 +424,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
if(mApplyRecordButton == null)
{
mApplyRecordButton = new Button("Apply");
mApplyRecordButton.setAccessibleText("Apply record to selected aliases");
mApplyRecordButton.setOnAction(event -> {
startChange();
boolean record = getRecordToggleSwitch().isSelected();
@@ -456,12 +467,15 @@ public class AliasBulkEditor extends Editor<List<Alias>>
{
setText(null);
setGraphic(null);
setAccessibleText(null);
}
else
{
textLabel.setText(item.getName());
iconLabel.setGraphic(new ImageView(item.getFxImage()));
setGraphic(gridPane);
//The name is shown in a nested label, so expose it as the cell's accessible text
setAccessibleText(item.getName());
}
}
};

View File

@@ -273,7 +273,9 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
Label listLabel = new Label("Alias List");
listLabel.setLabelFor(getAliasListNameComboBox());
Label searchLabel = new Label("Search");
searchLabel.setLabelFor(getSearchField());
searchLabel.setAlignment(Pos.CENTER_RIGHT);
HBox searchBox = new HBox();
@@ -451,6 +453,7 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
if(mAliasTableView == null)
{
mAliasTableView = new TableView<>();
mAliasTableView.setAccessibleText("Aliases");
TableColumn nameColumn = new TableColumn();
nameColumn.setText("Alias");
@@ -751,15 +754,18 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
UIManager.getColor("TextField.background"));
rectangle.setFill(ColorUtil.fromInteger(readable.equals(color) ? alias.getColor() :
readable.getRGB()));
setAccessibleText(String.format("#%06X", alias.getColor() & 0xFFFFFF));
}
else
{
rectangle.setVisible(false);
setAccessibleText(null);
}
}
else
{
rectangle.setVisible(false);
setAccessibleText(null);
}
}
};
@@ -798,13 +804,15 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
{
super.updateItem(item, empty);
if(!empty && getTableRow() != null)
if(!empty && getTableRow() != null && item != null)
{
iconNode.setVisible(item);
setAccessibleText(item ? "Yes" : "No");
}
else
{
iconNode.setVisible(false);
setAccessibleText(null);
}
}
};
@@ -919,6 +927,7 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
{
super.updateItem(item, empty);
setAlignment(Pos.CENTER);
setAccessibleText(null);
if(empty)
{
@@ -937,6 +946,7 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
if(icon != null && icon.getFxImage() != null)
{
setGraphic(new ImageView(icon.getFxImage()));
setAccessibleText(icon.getName());
}
else
{

View File

@@ -57,6 +57,7 @@ import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
import io.github.dsheirer.alias.id.tone.TonesID;
import io.github.dsheirer.audio.broadcast.ConfiguredBroadcast;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerFormatter;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.gui.playlist.alias.action.ActionEditor;
@@ -574,6 +575,7 @@ public class AliasItemEditor extends Editor<Alias>
{
mActionsList = new ListView<>(FXCollections.observableArrayList(AliasAction.extractor()));
mActionsList.setPrefHeight(75);
mActionsList.setAccessibleText("Alias actions");
mActionsList.setDisable(true);
mActionsList.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> setAction(newValue));
@@ -686,6 +688,7 @@ public class AliasItemEditor extends Editor<Alias>
mIdentifiersList = new ListView<>(FXCollections.observableArrayList(AliasID.extractor()));
mIdentifiersList.setDisable(true);
mIdentifiersList.setPrefHeight(75);
mIdentifiersList.setAccessibleText("Alias identifiers");
mIdentifiersList.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> setIdentifier(newValue));
mIdentifiersList.setCellFactory(param -> new AliasIdentifierCell());
@@ -846,13 +849,17 @@ public class AliasItemEditor extends Editor<Alias>
buttonBox.setSpacing(5);
buttonBox.getChildren().addAll(new Label(" "), getAddStreamButton(), getRemoveStreamButton());
Label availableLabel = new Label("Available");
getAvailableStreamsView().setAccessibleText("Available streams");
VBox availableBox = new VBox();
VBox.setVgrow(getAvailableStreamsView(), Priority.ALWAYS);
availableBox.getChildren().addAll(new Label("Available"), getAvailableStreamsView());
availableBox.getChildren().addAll(availableLabel, getAvailableStreamsView());
Label selectedLabel = new Label("Selected");
getSelectedStreamsView().setAccessibleText("Selected streams");
VBox selectedBox = new VBox();
VBox.setVgrow(getSelectedStreamsView(), Priority.ALWAYS);
selectedBox.getChildren().addAll(new Label("Selected"), getSelectedStreamsView());
selectedBox.getChildren().addAll(selectedLabel, getSelectedStreamsView());
HBox hbox = new HBox();
hbox.setSpacing(10);
@@ -864,6 +871,7 @@ public class AliasItemEditor extends Editor<Alias>
vbox.setSpacing(10);
VBox.setVgrow(hbox, Priority.ALWAYS);
Label label = new Label("Stream this Alias as Talkgroup:");
label.setLabelFor(getStreamAsTalkgroupField());
HBox streamAsHBox = new HBox();
streamAsHBox.setAlignment(Pos.CENTER_LEFT);
streamAsHBox.setSpacing(10);
@@ -964,6 +972,8 @@ public class AliasItemEditor extends Editor<Alias>
.or(Bindings.isNull(getAvailableStreamsView().getSelectionModel().selectedItemProperty())));
mAddStreamButton.setMaxWidth(Double.MAX_VALUE);
mAddStreamButton.setGraphic(new IconNode(FontAwesome.ANGLE_RIGHT));
mAddStreamButton.setAccessibleText("Add selected stream");
mAddStreamButton.setTooltip(new Tooltip("Add selected stream"));
mAddStreamButton.setAlignment(Pos.CENTER);
mAddStreamButton.setOnAction(event -> {
String stream = getAvailableStreamsView().getSelectionModel().getSelectedItem();
@@ -990,6 +1000,8 @@ public class AliasItemEditor extends Editor<Alias>
.or(Bindings.isNull(getSelectedStreamsView().getSelectionModel().selectedItemProperty())));
mRemoveStreamButton.setMaxWidth(Double.MAX_VALUE);
mRemoveStreamButton.setGraphic(new IconNode(FontAwesome.ANGLE_LEFT));
mRemoveStreamButton.setAccessibleText("Remove selected stream");
mRemoveStreamButton.setTooltip(new Tooltip("Remove selected stream"));
mRemoveStreamButton.setAlignment(Pos.CENTER);
mRemoveStreamButton.setOnAction(event -> {
BroadcastChannel broadcastChannel = getSelectedStreamsView().getSelectionModel().getSelectedItem();
@@ -1050,6 +1062,7 @@ public class AliasItemEditor extends Editor<Alias>
int row = 0;
Label nameLabel = new Label("Alias");
nameLabel.setLabelFor(getNameField());
GridPane.setHalignment(nameLabel, HPos.RIGHT);
GridPane.setConstraints(nameLabel, 0, row);
mTextFieldPane.getChildren().add(nameLabel);
@@ -1058,6 +1071,7 @@ public class AliasItemEditor extends Editor<Alias>
mTextFieldPane.getChildren().add(getNameField());
Label monitorAudioLabel = new Label("Listen");
monitorAudioLabel.setLabelFor(getMonitorAudioToggleSwitch());
GridPane.setHalignment(monitorAudioLabel, HPos.RIGHT);
GridPane.setConstraints(monitorAudioLabel, 2, row);
mTextFieldPane.getChildren().add(monitorAudioLabel);
@@ -1065,6 +1079,7 @@ public class AliasItemEditor extends Editor<Alias>
mTextFieldPane.getChildren().add(getMonitorAudioToggleSwitch());
Label monitorPriorityLabel = new Label("Priority");
monitorPriorityLabel.setLabelFor(getMonitorPriorityComboBox());
GridPane.setHalignment(monitorPriorityLabel, HPos.RIGHT);
GridPane.setConstraints(monitorPriorityLabel, 4, row);
mTextFieldPane.getChildren().add(monitorPriorityLabel);
@@ -1072,6 +1087,7 @@ public class AliasItemEditor extends Editor<Alias>
mTextFieldPane.getChildren().add(getMonitorPriorityComboBox());
Label colorLabel = new Label("Color");
colorLabel.setLabelFor(getColorPicker());
GridPane.setHalignment(colorLabel, HPos.RIGHT);
GridPane.setConstraints(colorLabel, 6, row);
mTextFieldPane.getChildren().add(colorLabel);
@@ -1079,6 +1095,7 @@ public class AliasItemEditor extends Editor<Alias>
mTextFieldPane.getChildren().add(getColorPicker());
Label groupLabel = new Label("Group");
groupLabel.setLabelFor(getGroupField());
GridPane.setHalignment(groupLabel, HPos.RIGHT);
GridPane.setConstraints(groupLabel, 0, ++row);
mTextFieldPane.getChildren().add(groupLabel);
@@ -1087,6 +1104,7 @@ public class AliasItemEditor extends Editor<Alias>
mTextFieldPane.getChildren().add(getGroupField());
Label recordAudioLabel = new Label("Record");
recordAudioLabel.setLabelFor(getRecordAudioToggleSwitch());
GridPane.setHalignment(recordAudioLabel, HPos.RIGHT);
GridPane.setConstraints(recordAudioLabel, 2, row);
mTextFieldPane.getChildren().add(recordAudioLabel);
@@ -1094,6 +1112,7 @@ public class AliasItemEditor extends Editor<Alias>
mTextFieldPane.getChildren().add(getRecordAudioToggleSwitch());
Label iconLabel = new Label("Icon");
iconLabel.setLabelFor(getIconNodeComboBox());
GridPane.setHalignment(iconLabel, HPos.RIGHT);
GridPane.setConstraints(iconLabel, 4, row);
mTextFieldPane.getChildren().add(iconLabel);
@@ -1108,7 +1127,7 @@ public class AliasItemEditor extends Editor<Alias>
{
if(mMonitorAudioToggleSwitch == null)
{
mMonitorAudioToggleSwitch = new ToggleSwitch();
mMonitorAudioToggleSwitch = new AccessibleToggleSwitch();
mMonitorAudioToggleSwitch.setDisable(true);
mMonitorAudioToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -1141,7 +1160,7 @@ public class AliasItemEditor extends Editor<Alias>
{
if(mRecordAudioToggleSwitch == null)
{
mRecordAudioToggleSwitch = new ToggleSwitch();
mRecordAudioToggleSwitch = new AccessibleToggleSwitch();
mRecordAudioToggleSwitch.setDisable(true);
mRecordAudioToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -1820,12 +1839,15 @@ public class AliasItemEditor extends Editor<Alias>
{
setText(null);
setGraphic(null);
setAccessibleText(null);
}
else
{
textLabel.setText(item.getName());
iconLabel.setGraphic(new ImageView(item.getFxImage()));
setGraphic(gridPane);
//The name is shown in a nested label, so expose it as the cell's accessible text
setAccessibleText(item.getName());
}
}
};

View File

@@ -23,11 +23,21 @@ import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.alias.id.AliasID;
import io.github.dsheirer.alias.id.AliasIDType;
import io.github.dsheirer.alias.id.radio.P25FullyQualifiedRadio;
import io.github.dsheirer.alias.id.radio.Radio;
import io.github.dsheirer.alias.id.radio.RadioRange;
import io.github.dsheirer.alias.id.status.UnitStatusID;
import io.github.dsheirer.alias.id.status.UserStatusID;
import io.github.dsheirer.alias.id.talkgroup.P25FullyQualifiedTalkgroup;
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.protocol.Protocol;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
@@ -39,6 +49,7 @@ import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
@@ -52,6 +63,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
@@ -62,7 +74,7 @@ public class AliasViewByIdentifierEditor extends VBox
private ComboBox<String> mAliasListNameComboBox;
private ComboBox<AliasIDType> mAliasIDTypeComboBox;
private TableView<AliasAndIdentifier> mAliasAndIdentifierTableView;
private TableColumn<AliasAndIdentifier,String> mIdentifierColumn;
private TableColumn<AliasAndIdentifier,AliasID> mIdentifierColumn;
private Button mViewAliasButton;
private boolean aliasListInvalidated;
@@ -85,6 +97,7 @@ public class AliasViewByIdentifierEditor extends VBox
gridPane.setHgap(10);
Label aliasListLabel = new Label("Alias List");
aliasListLabel.setLabelFor(getAliasListNameComboBox());
GridPane.setHalignment(aliasListLabel, HPos.RIGHT);
GridPane.setConstraints(aliasListLabel, 0, 0);
gridPane.getChildren().add(aliasListLabel);
@@ -94,6 +107,7 @@ public class AliasViewByIdentifierEditor extends VBox
gridPane.getChildren().add(getAliasListNameComboBox());
Label identifierLabel = new Label("Identifier Type");
identifierLabel.setLabelFor(getAliasIDTypeComboBox());
GridPane.setHalignment(identifierLabel, HPos.RIGHT);
GridPane.setConstraints(identifierLabel, 0, 1);
gridPane.getChildren().add(identifierLabel);
@@ -198,9 +212,29 @@ public class AliasViewByIdentifierEditor extends VBox
if(mAliasAndIdentifierTableView == null)
{
mAliasAndIdentifierTableView = new TableView<>(FXCollections.observableArrayList(AliasAndIdentifier.extractor()));
mAliasAndIdentifierTableView.setAccessibleText("Aliases by identifier");
mAliasAndIdentifierTableView.setPlaceholder(new Label("No aliases or identifiers available"));
mIdentifierColumn = new TableColumn<>("Identifier");
mIdentifierColumn.setCellValueFactory(new PropertyValueFactory<>("identifier"));
mIdentifierColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().getAliasIdentifier()));
mIdentifierColumn.setCellFactory(column -> new TableCell<>()
{
@Override
protected void updateItem(AliasID item, boolean empty)
{
super.updateItem(item, empty);
textProperty().unbind();
if(empty || item == null)
{
setText(null);
}
else
{
textProperty().bind(item.valueProperty());
}
}
});
mIdentifierColumn.setComparator(new AliasIdentifierComparator());
mIdentifierColumn.setPrefWidth(350);
TableColumn<AliasAndIdentifier, String> aliasColumn = new TableColumn<>();
@@ -268,6 +302,103 @@ public class AliasViewByIdentifierEditor extends VBox
return mAliasIDTypeComboBox;
}
/**
* Compares alias identifiers by protocol and then by numeric identifier value(s) so that the identifier column
* sorts numerically rather than alphabetically by the formatted display text. Identifiers that don't have a
* numeric value fall back to comparing the display text.
*/
public static class AliasIdentifierComparator implements Comparator<AliasID>
{
private static final Comparator<Protocol> PROTOCOL_COMPARATOR = Comparator.nullsLast(Comparator.naturalOrder());
@Override
public int compare(AliasID a, AliasID b)
{
if(a == b)
{
return 0;
}
if(a == null)
{
return 1;
}
if(b == null)
{
return -1;
}
int comparison = PROTOCOL_COMPARATOR.compare(getProtocol(a), getProtocol(b));
if(comparison == 0)
{
long[] aValues = getValues(a);
long[] bValues = getValues(b);
if(aValues != null && bValues != null)
{
for(int x = 0; x < Math.min(aValues.length, bValues.length) && comparison == 0; x++)
{
comparison = Long.compare(aValues[x], bValues[x]);
}
if(comparison == 0)
{
comparison = Integer.compare(aValues.length, bValues.length);
}
}
else if(aValues != null)
{
comparison = -1;
}
else if(bValues != null)
{
comparison = 1;
}
}
if(comparison == 0)
{
comparison = String.valueOf(a).compareTo(String.valueOf(b));
}
return comparison;
}
/**
* Protocol for the identifier, or null if the identifier doesn't have a protocol.
*/
private static Protocol getProtocol(AliasID id)
{
return switch(id)
{
case Talkgroup talkgroup -> talkgroup.getProtocol();
case TalkgroupRange talkgroupRange -> talkgroupRange.getProtocol();
case Radio radio -> radio.getProtocol();
case RadioRange radioRange -> radioRange.getProtocol();
default -> null;
};
}
/**
* Numeric values for the identifier, in order of sort significance, or null if the identifier is not numeric.
*/
private static long[] getValues(AliasID id)
{
return switch(id)
{
case P25FullyQualifiedTalkgroup fqt -> new long[]{fqt.getWacn(), fqt.getSystem(), fqt.getValue()};
case P25FullyQualifiedRadio fqr -> new long[]{fqr.getWacn(), fqr.getSystem(), fqr.getValue()};
case Talkgroup talkgroup -> new long[]{talkgroup.getValue()};
case TalkgroupRange range -> new long[]{range.getMinTalkgroup(), range.getMaxTalkgroup()};
case Radio radio -> new long[]{radio.getValue()};
case RadioRange range -> new long[]{range.getMinRadio(), range.getMaxRadio()};
case UnitStatusID status -> new long[]{status.getStatus()};
case UserStatusID status -> new long[]{status.getStatus()};
default -> null;
};
}
}
public static class AliasAndIdentifier
{
private Alias mAlias;

View File

@@ -34,6 +34,7 @@ import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
@@ -157,12 +158,14 @@ public class AliasViewByRecordingEditor extends VBox
aliasListBox.setAlignment(Pos.CENTER);
aliasListBox.setSpacing(5);
Label aliasListLabel = new Label("Alias List");
aliasListLabel.setLabelFor(getAliasListNameComboBox());
aliasListBox.getChildren().addAll(aliasListLabel, getAliasListNameComboBox());
HBox searchBox = new HBox();
searchBox.setAlignment(Pos.CENTER);
searchBox.setSpacing(5);
Label searchLabel = new Label("Search");
searchLabel.setLabelFor(getSearchField());
searchBox.getChildren().addAll(searchLabel, getSearchField());
mTopBox.getChildren().addAll(aliasListBox, searchBox);
@@ -228,6 +231,7 @@ public class AliasViewByRecordingEditor extends VBox
if(mNoRecordAliasTableView == null)
{
mNoRecordAliasTableView = new TableView<>();
mNoRecordAliasTableView.setAccessibleText("Don't record audio aliases");
mNoRecordAliasTableView.setMaxHeight(Double.MAX_VALUE);
mNoRecordAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
@@ -266,6 +270,7 @@ public class AliasViewByRecordingEditor extends VBox
if(mRecordAliasTableView == null)
{
mRecordAliasTableView = new TableView<>();
mRecordAliasTableView.setAccessibleText("Record audio aliases");
mRecordAliasTableView.setMaxHeight(Double.MAX_VALUE);
mRecordAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
@@ -307,6 +312,8 @@ public class AliasViewByRecordingEditor extends VBox
mAddButton.setDisable(true);
mAddButton.setMaxWidth(Double.MAX_VALUE);
mAddButton.setGraphic(new IconNode(FontAwesome.ANGLE_RIGHT));
mAddButton.setAccessibleText("Record audio for selected alias");
mAddButton.setTooltip(new Tooltip("Record audio for selected alias"));
mAddButton.setAlignment(Pos.CENTER);
mAddButton.setOnAction(event -> {
Alias selectedAlias = getNoRecordAliasTableView().getSelectionModel().getSelectedItem();
@@ -330,6 +337,8 @@ public class AliasViewByRecordingEditor extends VBox
mAddAllButton.setDisable(true);
mAddAllButton.setMaxWidth(Double.MAX_VALUE);
mAddAllButton.setGraphic(new IconNode(FontAwesome.ANGLE_DOUBLE_RIGHT));
mAddAllButton.setAccessibleText("Record audio for all selected aliases");
mAddAllButton.setTooltip(new Tooltip("Record audio for all selected aliases"));
mAddAllButton.setAlignment(Pos.CENTER);
mAddAllButton.setOnAction(event -> {
List<Alias> selectedAliases =
@@ -358,6 +367,8 @@ public class AliasViewByRecordingEditor extends VBox
mRemoveButton.setDisable(true);
mRemoveButton.setMaxWidth(Double.MAX_VALUE);
mRemoveButton.setGraphic(new IconNode(FontAwesome.ANGLE_LEFT));
mRemoveButton.setAccessibleText("Don't record audio for selected alias");
mRemoveButton.setTooltip(new Tooltip("Don't record audio for selected alias"));
mRemoveButton.setAlignment(Pos.CENTER);
mRemoveButton.setOnAction(event -> {
Alias selectedAlias = getRecordAliasTableView().getSelectionModel().getSelectedItem();
@@ -381,6 +392,8 @@ public class AliasViewByRecordingEditor extends VBox
mRemoveAllButton.setDisable(true);
mRemoveAllButton.setMaxWidth(Double.MAX_VALUE);
mRemoveAllButton.setGraphic(new IconNode(FontAwesome.ANGLE_DOUBLE_LEFT));
mRemoveAllButton.setAccessibleText("Don't record audio for all selected aliases");
mRemoveAllButton.setTooltip(new Tooltip("Don't record audio for all selected aliases"));
mRemoveAllButton.setAlignment(Pos.CENTER);
mRemoveAllButton.setOnAction(event -> {
List<Alias> selectedAliases =

View File

@@ -66,6 +66,7 @@ public class BeepEditor extends ActionEditor<BeepAction>
int row = 0;
Label intervalLabel = new Label(("Beep"));
intervalLabel.setLabelFor(getIntervalComboBox());
GridPane.setHalignment(intervalLabel, HPos.RIGHT);
GridPane.setConstraints(intervalLabel, 0, row);
gridPane.getChildren().add(intervalLabel);
@@ -80,6 +81,7 @@ public class BeepEditor extends ActionEditor<BeepAction>
GridPane.setConstraints(getSecondsLabel(), 3, row);
GridPane.setHgrow(getSecondsLabel(), Priority.ALWAYS);
gridPane.getChildren().add(getSecondsLabel());
getSecondsLabel().setLabelFor(getPeriodTextField());
GridPane.setConstraints(getTestButton(), 4, row);
gridPane.getChildren().add(getTestButton());
@@ -155,6 +157,7 @@ public class BeepEditor extends ActionEditor<BeepAction>
if(mTestButton == null)
{
mTestButton = new Button("Test");
mTestButton.setAccessibleText("Test beep");
mTestButton.setMaxWidth(Double.MAX_VALUE);
mTestButton.setOnAction(event -> test());
}

View File

@@ -71,6 +71,7 @@ public class ClipEditor extends ActionEditor<ClipAction>
int row = 0;
Label fileLabel = new Label("Audio File");
fileLabel.setLabelFor(getFilePath());
GridPane.setHalignment(fileLabel, HPos.RIGHT);
GridPane.setConstraints(fileLabel, 0, row);
gridPane.getChildren().add(fileLabel);
@@ -83,6 +84,7 @@ public class ClipEditor extends ActionEditor<ClipAction>
gridPane.getChildren().add(getFileChooser());
Label intervalLabel = new Label(("Play Audio Clip"));
intervalLabel.setLabelFor(getIntervalComboBox());
GridPane.setHalignment(intervalLabel, HPos.RIGHT);
GridPane.setConstraints(intervalLabel, 0, ++row);
gridPane.getChildren().add(intervalLabel);
@@ -97,6 +99,7 @@ public class ClipEditor extends ActionEditor<ClipAction>
GridPane.setConstraints(getSecondsLabel(), 3, row);
GridPane.setHgrow(getSecondsLabel(), Priority.ALWAYS);
gridPane.getChildren().add(getSecondsLabel());
getSecondsLabel().setLabelFor(getPeriodTextField());
GridPane.setConstraints(getTestButton(), 4, row);
gridPane.getChildren().add(getTestButton());
@@ -223,6 +226,7 @@ public class ClipEditor extends ActionEditor<ClipAction>
if(mTestButton == null)
{
mTestButton = new Button("Test");
mTestButton.setAccessibleText("Test audio clip");
mTestButton.setMaxWidth(Double.MAX_VALUE);
mTestButton.setDisable(true);
mTestButton.setOnAction(event -> test());
@@ -236,6 +240,7 @@ public class ClipEditor extends ActionEditor<ClipAction>
if(mFileChooser == null)
{
mFileChooser = new Button(("Select ..."));
mFileChooser.setAccessibleText("Select audio file");
mFileChooser.setMaxWidth(Double.MAX_VALUE);
mFileChooser.setDisable(true);
mFileChooser.setOnAction(event -> {
@@ -278,7 +283,8 @@ public class ClipEditor extends ActionEditor<ClipAction>
if(mFilePath == null)
{
mFilePath = new TextField(PLEASE_SELECT_A_FILE);
mFilePath.setDisable(true);
//Read-only rather than disabled so that the selected file remains focusable for screen readers
mFilePath.setEditable(false);
mFilePath.setMaxWidth(Double.MAX_VALUE);
mFilePath.textProperty().addListener((observable, oldValue, newValue) -> {
if(newValue != null && !newValue.isEmpty() && !newValue.contentEquals(PLEASE_SELECT_A_FILE))

View File

@@ -71,6 +71,7 @@ public class ScriptEditor extends ActionEditor<ScriptAction>
int row = 0;
Label fileLabel = new Label("Script File");
fileLabel.setLabelFor(getFilePath());
GridPane.setHalignment(fileLabel, HPos.RIGHT);
GridPane.setConstraints(fileLabel, 0, row);
gridPane.getChildren().add(fileLabel);
@@ -83,6 +84,7 @@ public class ScriptEditor extends ActionEditor<ScriptAction>
gridPane.getChildren().add(getFileChooser());
Label intervalLabel = new Label(("Run Script"));
intervalLabel.setLabelFor(getIntervalComboBox());
GridPane.setHalignment(intervalLabel, HPos.RIGHT);
GridPane.setConstraints(intervalLabel, 0, ++row);
gridPane.getChildren().add(intervalLabel);
@@ -97,6 +99,7 @@ public class ScriptEditor extends ActionEditor<ScriptAction>
GridPane.setConstraints(getSecondsLabel(), 3, row);
GridPane.setHgrow(getSecondsLabel(), Priority.ALWAYS);
gridPane.getChildren().add(getSecondsLabel());
getSecondsLabel().setLabelFor(getPeriodTextField());
GridPane.setConstraints(getTestButton(), 4, row);
gridPane.getChildren().add(getTestButton());
@@ -223,6 +226,7 @@ public class ScriptEditor extends ActionEditor<ScriptAction>
if(mTestButton == null)
{
mTestButton = new Button("Test");
mTestButton.setAccessibleText("Test script");
mTestButton.setMaxWidth(Double.MAX_VALUE);
mTestButton.setDisable(true);
mTestButton.setOnAction(event -> test());
@@ -236,6 +240,7 @@ public class ScriptEditor extends ActionEditor<ScriptAction>
if(mFileChooser == null)
{
mFileChooser = new Button(("Select ..."));
mFileChooser.setAccessibleText("Select script file");
mFileChooser.setMaxWidth(Double.MAX_VALUE);
mFileChooser.setDisable(true);
mFileChooser.setOnAction(event -> {
@@ -278,7 +283,8 @@ public class ScriptEditor extends ActionEditor<ScriptAction>
if(mFilePath == null)
{
mFilePath = new TextField(PLEASE_SELECT_A_FILE);
mFilePath.setDisable(true);
//Read-only rather than disabled so that the selected file remains focusable for screen readers
mFilePath.setEditable(false);
mFilePath.setMaxWidth(Double.MAX_VALUE);
mFilePath.textProperty().addListener((observable, oldValue, newValue) -> {
if(newValue != null && !newValue.isEmpty() && !newValue.contentEquals(PLEASE_SELECT_A_FILE))

View File

@@ -44,6 +44,7 @@ public class DcsEditor extends IdentifierEditor<Dcs>
gridPane.setHgap(5);
Label typeLabel = new Label("DCS Code");
typeLabel.setLabelFor(getDCSCodeComboBox());
GridPane.setConstraints(typeLabel, 0, 0);
gridPane.getChildren().add(typeLabel);

View File

@@ -54,6 +54,7 @@ public class EsnEditor extends IdentifierEditor<Esn>
gridPane.setHgap(5);
Label typeLabel = new Label("ESN");
typeLabel.setLabelFor(getEsnField());
GridPane.setConstraints(typeLabel, 0, 0);
gridPane.getChildren().add(typeLabel);

View File

@@ -47,6 +47,7 @@ public class LojackEditor extends IdentifierEditor<LoJackFunctionAndID>
gridPane.setHgap(5);
Label typeLabel = new Label("LoJack Function");
typeLabel.setLabelFor(getFunctionComboBox());
GridPane.setConstraints(typeLabel, 0, 0);
gridPane.getChildren().add(typeLabel);
@@ -57,6 +58,10 @@ public class LojackEditor extends IdentifierEditor<LoJackFunctionAndID>
gridPane.getChildren().add(getIdentifierField());
Label helpLabel = new Label("Format: 5 numbers, characters or (*)wildcard - e.g. AB*12");
//The identifier field has no label of its own, so the help label provides its accessible name and help text
helpLabel.setLabelFor(getIdentifierField());
helpLabel.setAccessibleText("LoJack ID");
getIdentifierField().setAccessibleHelp(helpLabel.getText());
GridPane.setConstraints(helpLabel, 3, 0);
gridPane.getChildren().add(helpLabel);

View File

@@ -76,11 +76,13 @@ public class NXDNTypeDEditor<T extends AliasID & INXDNTypeD> extends IdentifierE
gridPane.add(new Label("NXDN Type-D"), 0, 0);
Label homeRepeaterLabel = new Label("Home Repeater");
homeRepeaterLabel.setLabelFor(mHomeRepeaterField);
GridPane.setHalignment(homeRepeaterLabel, HPos.RIGHT);
gridPane.add(homeRepeaterLabel, 1, 0);
gridPane.add(mHomeRepeaterField, 2, 0);
Label identifierFieldLabel = new Label(identifierLabel);
identifierFieldLabel.setLabelFor(mIdentifierField);
GridPane.setHalignment(identifierFieldLabel, HPos.RIGHT);
gridPane.add(identifierFieldLabel, 3, 0);
gridPane.add(mIdentifierField, 4, 0);

View File

@@ -89,15 +89,21 @@ public class NXDNTypeDRangeEditor<T extends AliasID & INXDNTypeDRange> extends I
gridPane.add(new Label("NXDN Type-D"), 0, 0);
Label homeRepeaterLabel = new Label("Home Repeater");
homeRepeaterLabel.setLabelFor(mHomeRepeaterField);
GridPane.setHalignment(homeRepeaterLabel, HPos.RIGHT);
gridPane.add(homeRepeaterLabel, 1, 0);
gridPane.add(mHomeRepeaterField, 2, 0);
Label rangeFieldLabel = new Label(rangeLabel);
rangeFieldLabel.setLabelFor(mMinField);
rangeFieldLabel.setAccessibleText(rangeLabel + " start");
GridPane.setHalignment(rangeFieldLabel, HPos.RIGHT);
gridPane.add(rangeFieldLabel, 3, 0);
gridPane.add(mMinField, 4, 0);
gridPane.add(new Label("-"), 5, 0);
Label dashLabel = new Label("-");
dashLabel.setLabelFor(mMaxField);
dashLabel.setAccessibleText(rangeLabel + " end");
gridPane.add(dashLabel, 5, 0);
gridPane.add(mMaxField, 6, 0);
getChildren().add(gridPane);

View File

@@ -70,6 +70,7 @@ public class P25FullyQualifiedRadioIdEditor extends IdentifierEditor<P25FullyQua
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("WACN");
valueLabel.setLabelFor(getWacnField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
@@ -78,6 +79,7 @@ public class P25FullyQualifiedRadioIdEditor extends IdentifierEditor<P25FullyQua
gridPane.getChildren().add(getWacnField());
Label systemLabel = new Label("System");
systemLabel.setLabelFor(getSystemField());
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 3, 0);
gridPane.getChildren().add(systemLabel);
@@ -86,6 +88,7 @@ public class P25FullyQualifiedRadioIdEditor extends IdentifierEditor<P25FullyQua
gridPane.getChildren().add(getSystemField());
Label radioLabel = new Label("Radio ID");
radioLabel.setLabelFor(getRadioField());
GridPane.setHalignment(radioLabel, HPos.RIGHT);
GridPane.setConstraints(radioLabel, 5, 0);
gridPane.getChildren().add(radioLabel);

View File

@@ -70,6 +70,7 @@ public class P25FullyQualifiedTalkgroupEditor extends IdentifierEditor<P25FullyQ
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("WACN");
valueLabel.setLabelFor(getWacnField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
@@ -78,6 +79,7 @@ public class P25FullyQualifiedTalkgroupEditor extends IdentifierEditor<P25FullyQ
gridPane.getChildren().add(getWacnField());
Label systemLabel = new Label("System");
systemLabel.setLabelFor(getSystemField());
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 3, 0);
gridPane.getChildren().add(systemLabel);
@@ -86,6 +88,7 @@ public class P25FullyQualifiedTalkgroupEditor extends IdentifierEditor<P25FullyQ
gridPane.getChildren().add(getSystemField());
Label radioLabel = new Label("Talkgroup");
radioLabel.setLabelFor(getTalkgroupField());
GridPane.setHalignment(radioLabel, HPos.RIGHT);
GridPane.setConstraints(radioLabel, 5, 0);
gridPane.getChildren().add(radioLabel);

View File

@@ -71,6 +71,7 @@ public class RadioIdEditor extends IdentifierEditor<Radio>
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("Radio ID");
valueLabel.setLabelFor(getRadioIdField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);

View File

@@ -73,6 +73,8 @@ public class RadioIdRangeEditor extends IdentifierEditor<RadioRange>
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("Radio ID Range");
valueLabel.setLabelFor(getMinRadioIdField());
valueLabel.setAccessibleText("Radio ID Range start");
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
@@ -81,6 +83,8 @@ public class RadioIdRangeEditor extends IdentifierEditor<RadioRange>
gridPane.getChildren().add(getMinRadioIdField());
Label dashLabel = new Label("-");
dashLabel.setLabelFor(getMaxRadioIdField());
dashLabel.setAccessibleText("Radio ID Range end");
GridPane.setConstraints(dashLabel, 3, 0);
gridPane.getChildren().add(dashLabel);
@@ -292,10 +296,10 @@ public class RadioIdRangeEditor extends IdentifierEditor<RadioRange>
new IntegerFormatter(0,0xFFFFFF), "Format: 0 - 16777215"));
details.add(new RadioIdDetail(Protocol.APCO25, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFFFF),
new HexFormatter(0,0xFFFFFF), "Format: 0 - FFFFFF"));
details.add(new RadioIdDetail(Protocol.NXDN, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFF),
new IntegerFormatter(0,0xFFFF),"Format: 1 - 65,535"));
details.add(new RadioIdDetail(Protocol.NXDN, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFF),
new HexFormatter(0,0xFFFF),"Format: 1 - FFFF"));
details.add(new RadioIdDetail(Protocol.NXDN, IntegerFormat.DECIMAL, new IntegerFormatter(1,0xFFFF),
new IntegerFormatter(1,0xFFFF),"Format: 1 - 65,535"));
details.add(new RadioIdDetail(Protocol.NXDN, IntegerFormat.HEXADECIMAL, new HexFormatter(1,0xFFFF),
new HexFormatter(1,0xFFFF),"Format: 1 - FFFF"));
details.add(new RadioIdDetail(Protocol.PASSPORT, IntegerFormat.DECIMAL, new IntegerFormatter(0,0x7FFFFF),
new IntegerFormatter(0,0x7FFFFF), "Format: 0 - 8388607"));
details.add(new RadioIdDetail(Protocol.PASSPORT, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0x7FFFFF),

View File

@@ -73,6 +73,7 @@ public class TalkgroupEditor extends IdentifierEditor<Talkgroup>
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("Talkgroup");
valueLabel.setLabelFor(getTalkgroupField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);

View File

@@ -75,6 +75,8 @@ public class TalkgroupRangeEditor extends IdentifierEditor<TalkgroupRange>
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("Talkgroup Range");
valueLabel.setLabelFor(getMinTalkgroupField());
valueLabel.setAccessibleText("Talkgroup Range start");
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
@@ -83,6 +85,8 @@ public class TalkgroupRangeEditor extends IdentifierEditor<TalkgroupRange>
gridPane.getChildren().add(getMinTalkgroupField());
Label dashLabel = new Label("-");
dashLabel.setLabelFor(getMaxTalkgroupField());
dashLabel.setAccessibleText("Talkgroup Range end");
GridPane.setConstraints(dashLabel, 3, 0);
gridPane.getChildren().add(dashLabel);
@@ -220,7 +224,7 @@ public class TalkgroupRangeEditor extends IdentifierEditor<TalkgroupRange>
if(mMaxTalkgroupField == null)
{
mMaxTalkgroupField = new TextField();
mMaxTalkgroupField.setTextFormatter(mMinIntegerTextFormatter);
mMaxTalkgroupField.setTextFormatter(mMaxIntegerTextFormatter);
}
return mMaxTalkgroupField;
@@ -301,7 +305,7 @@ public class TalkgroupRangeEditor extends IdentifierEditor<TalkgroupRange>
details.add(new TalkgroupDetail(Protocol.DMR, IntegerFormat.DECIMAL, new IntegerFormatter(1,16777215),
new IntegerFormatter(1,16777215), "Format: 1 - 16777215"));
details.add(new TalkgroupDetail(Protocol.DMR, IntegerFormat.HEXADECIMAL, new HexFormatter(1,0XFFFFFF),
new HexFormatter(1, 0xFFFFFF), "Format: 0 - FFFFFF"));
new HexFormatter(1, 0xFFFFFF), "Format: 1 - FFFFFF"));
details.add(new TalkgroupDetail(Protocol.FLEETSYNC, IntegerFormat.FORMATTED,
new PrefixIdentFormatter(0,0xFFFFF), new PrefixIdentFormatter(0,0xFFFFF),
"Format: PPP-IIII = Prefix (0-127), Ident (0-8191)"));
@@ -318,10 +322,10 @@ public class TalkgroupRangeEditor extends IdentifierEditor<TalkgroupRange>
new IntegerFormatter(0,65535), "Format 0 - 65535"));
details.add(new TalkgroupDetail(Protocol.NBFM, IntegerFormat.HEXADECIMAL, new HexFormatter(0,65535),
new HexFormatter(0,65535), "Format 0 - FFFF"));
details.add(new TalkgroupDetail(Protocol.NXDN, IntegerFormat.DECIMAL, new IntegerFormatter(1,0xFFFF),
new IntegerFormatter(1,0xFFFF),"Format: 1 - 65,535"));
details.add(new TalkgroupDetail(Protocol.NXDN, IntegerFormat.HEXADECIMAL, new HexFormatter(1,0xFFFF),
new HexFormatter(1,0xFFFF),"Format: 1 - FFFF"));
details.add(new TalkgroupDetail(Protocol.NXDN, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFF),
new IntegerFormatter(0,0xFFFF),"Format: 0 - 65,535"));
details.add(new TalkgroupDetail(Protocol.NXDN, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFF),
new HexFormatter(0,0xFFFF),"Format: 0 - FFFF"));
details.add(new TalkgroupDetail(Protocol.PASSPORT, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFF),
new IntegerFormatter(0,0xFFFF), "Format: 0 - 65535"));
details.add(new TalkgroupDetail(Protocol.PASSPORT, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFF),

View File

@@ -73,6 +73,7 @@ public class TonesEditor extends IdentifierEditor<TonesID>
editorsPane.setVgap(10);
Label toneLabel = new Label("Tone");
toneLabel.setLabelFor(getAmbeToneComboBox());
GridPane.setConstraints(toneLabel, 0, 0);
GridPane.setHalignment(toneLabel, HPos.RIGHT);
editorsPane.getChildren().add(toneLabel);
@@ -81,6 +82,7 @@ public class TonesEditor extends IdentifierEditor<TonesID>
editorsPane.getChildren().add(getAmbeToneComboBox());
Label durationLabel = new Label("Duration 1-50");
durationLabel.setLabelFor(getDurationTextField());
GridPane.setConstraints(durationLabel, 0, 1);
GridPane.setHalignment(durationLabel, HPos.RIGHT);
editorsPane.getChildren().add(durationLabel);
@@ -222,6 +224,7 @@ public class TonesEditor extends IdentifierEditor<TonesID>
iconNode.setIconSize(12);
iconNode.setFill(getMoveUpButton().getTextFill());
mMoveUpButton.setGraphic(iconNode);
mMoveUpButton.setAccessibleText("Move tone up");
mMoveUpButton.setMaxWidth(Double.MAX_VALUE);
mMoveUpButton.setDisable(true);
mMoveUpButton.setOnAction(event -> {
@@ -249,6 +252,7 @@ public class TonesEditor extends IdentifierEditor<TonesID>
iconNode.setIconSize(12);
iconNode.setFill(getMoveDownButton().getTextFill());
mMoveDownButton.setGraphic(iconNode);
mMoveDownButton.setAccessibleText("Move tone down");
mMoveDownButton.setMaxWidth(Double.MAX_VALUE);
mMoveDownButton.setDisable(true);
mMoveDownButton.setOnAction(event -> {
@@ -274,6 +278,7 @@ public class TonesEditor extends IdentifierEditor<TonesID>
mToneListView = new ListView<>();
mToneListView.setDisable(true);
mToneListView.setPrefHeight(75);
mToneListView.setAccessibleText("Tone sequence");
mToneListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
setTone(newValue);
});

View File

@@ -58,6 +58,7 @@ public class UnitStatusEditor extends IdentifierEditor<UnitStatusID>
gridPane.setVgap(3);
Label valueLabel = new Label("Unit Status");
valueLabel.setLabelFor(getUnitStatusField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 0, 0);
gridPane.getChildren().add(valueLabel);

View File

@@ -51,6 +51,7 @@ public class UserStatusEditor extends IdentifierEditor<UserStatusID>
gridPane.setVgap(3);
Label valueLabel = new Label("User Status");
valueLabel.setLabelFor(getUserStatusField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 0, 0);
gridPane.getChildren().add(valueLabel);

View File

@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.DbPowerMeter;
import io.github.dsheirer.gui.control.HexFormatter;
import io.github.dsheirer.gui.control.IntegerFormatter;
@@ -134,6 +136,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label bandwidthLabel = new Label("Channel Bandwidth");
AccessibilityUtil.labelGroup(bandwidthLabel, getBandwidthButton());
GridPane.setHalignment(bandwidthLabel, HPos.RIGHT);
GridPane.setConstraints(bandwidthLabel, 0, row);
gridPane.getChildren().add(bandwidthLabel);
@@ -142,6 +145,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getBandwidthButton());
Label squelchLabel = new Label("Squelch Threshold");
squelchLabel.setLabelFor(getSquelchThresholdField());
GridPane.setHalignment(squelchLabel, HPos.RIGHT);
GridPane.setConstraints(squelchLabel, 0, row);
gridPane.getChildren().add(squelchLabel);
@@ -150,6 +154,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getSquelchThresholdField());
Label autoTrackLabel = new Label("Squelch Auto-Track");
autoTrackLabel.setLabelFor(getSquelchAutoTrackSwitch());
GridPane.setHalignment(autoTrackLabel, HPos.RIGHT);
GridPane.setConstraints(autoTrackLabel, 2, row);
gridPane.getChildren().add(autoTrackLabel);
@@ -159,6 +164,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getSquelchAutoTrackSwitch());
Label talkgroupLabel = new Label("Talkgroup To Assign");
talkgroupLabel.setLabelFor(getTalkgroupField());
GridPane.setHalignment(talkgroupLabel, HPos.RIGHT);
GridPane.setConstraints(talkgroupLabel, 4, row);
gridPane.getChildren().add(talkgroupLabel);
@@ -210,6 +216,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getBasebandRecordSwitch());
Label recordBasebandLabel = new Label("Channel (Baseband I&Q)");
recordBasebandLabel.setLabelFor(getBasebandRecordSwitch());
GridPane.setHalignment(recordBasebandLabel, HPos.LEFT);
GridPane.setConstraints(recordBasebandLabel, 1, 1);
gridPane.getChildren().add(recordBasebandLabel);
@@ -254,7 +261,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
{
if(mBasebandRecordSwitch == null)
{
mBasebandRecordSwitch = new ToggleSwitch();
mBasebandRecordSwitch = new AccessibleToggleSwitch();
mBasebandRecordSwitch.setDisable(true);
mBasebandRecordSwitch.setTextAlignment(TextAlignment.RIGHT);
mBasebandRecordSwitch.selectedProperty()
@@ -348,7 +355,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
{
if(mSquelchAutoTrackSwitch == null)
{
mSquelchAutoTrackSwitch = new ToggleSwitch();
mSquelchAutoTrackSwitch = new AccessibleToggleSwitch();
mSquelchAutoTrackSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}

View File

@@ -23,6 +23,7 @@ import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelException;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.MaxLengthUnaryOperator;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
@@ -464,7 +465,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
{
if(mAutoStartSwitch == null)
{
mAutoStartSwitch = new ToggleSwitch();
mAutoStartSwitch = new AccessibleToggleSwitch();
mAutoStartSwitch.setDisable(true);
mAutoStartSwitch.setDisable(true);
mAutoStartSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -533,6 +534,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
int row = 0;
Label systemLabel = new Label("System");
systemLabel.setLabelFor(getSystemField());
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 0, row);
mTextFieldPane.getChildren().add(systemLabel);
@@ -542,6 +544,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
mTextFieldPane.getChildren().add(getSystemField());
Label autoStartLabel = new Label("Auto-Start");
autoStartLabel.setLabelFor(getAutoStartSwitch());
GridPane.setHalignment(autoStartLabel, HPos.RIGHT);
GridPane.setConstraints(autoStartLabel, 2, row);
mTextFieldPane.getChildren().add(autoStartLabel);
@@ -554,6 +557,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
mTextFieldPane.getChildren().add(getPlayButton());
Label siteLabel = new Label("Site");
siteLabel.setLabelFor(getSiteField());
GridPane.setHalignment(siteLabel, HPos.RIGHT);
GridPane.setConstraints(siteLabel, 0, ++row);
mTextFieldPane.getChildren().add(siteLabel);
@@ -563,6 +567,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
mTextFieldPane.getChildren().add(getSiteField());
Label autoStartOrderLabel = new Label("Start Order");
autoStartOrderLabel.setLabelFor(getAutoStartOrderSpinner());
GridPane.setHalignment(autoStartOrderLabel, HPos.RIGHT);
GridPane.setConstraints(autoStartOrderLabel, 2, row);
mTextFieldPane.getChildren().add(autoStartOrderLabel);
@@ -572,6 +577,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
mTextFieldPane.getChildren().add(getAutoStartOrderSpinner());
Label nameLabel = new Label("Name");
nameLabel.setLabelFor(getNameField());
GridPane.setHalignment(nameLabel, HPos.RIGHT);
GridPane.setConstraints(nameLabel, 0, ++row);
mTextFieldPane.getChildren().add(nameLabel);
@@ -581,6 +587,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
mTextFieldPane.getChildren().add(getNameField());
Label aliasListLabel = new Label("Alias List");
aliasListLabel.setLabelFor(getAliasListComboBox());
GridPane.setHalignment(aliasListLabel, HPos.RIGHT);
GridPane.setConstraints(aliasListLabel, 2, row);
mTextFieldPane.getChildren().add(aliasListLabel);
@@ -708,20 +715,25 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
audioPane.add(new Label(title), 0, 0);
Label noiseGainLabel = new Label("Comfort Noise Volume:");
noiseGainLabel.setLabelFor(noiseGainSlider);
GridPane.setHalignment(noiseGainLabel, HPos.RIGHT);
audioPane.add(noiseGainLabel, 1, 0);
audioPane.add(createSliderWithReset(noiseGainSlider, DecodeConfigJMBE.DEFAULT_COMFORT_NOISE_GAIN), 2, 0);
audioPane.add(createSliderWithReset(noiseGainSlider, DecodeConfigJMBE.DEFAULT_COMFORT_NOISE_GAIN,
"Reset comfort noise volume"), 2, 0);
Label agcLabel = new Label("AGC:");
agcLabel.setLabelFor(agcSwitch);
agcLabel.setTooltip(agcSwitch.getTooltip());
GridPane.setHalignment(agcLabel, HPos.RIGHT);
audioPane.add(agcLabel, 3, 0);
audioPane.add(agcSwitch, 4, 0);
Label toneGainLabel = new Label("Tone Volume:");
toneGainLabel.setLabelFor(toneGainSlider);
GridPane.setHalignment(toneGainLabel, HPos.RIGHT);
audioPane.add(toneGainLabel, 1, 1);
audioPane.add(createSliderWithReset(toneGainSlider, DecodeConfigJMBE.DEFAULT_TONE_GAIN), 2, 1);
audioPane.add(createSliderWithReset(toneGainSlider, DecodeConfigJMBE.DEFAULT_TONE_GAIN, "Reset tone volume"), 2, 1);
Label legacyToneLevelsLabel = new Label("Use Legacy Tone Levels:");
legacyToneLevelsLabel.setLabelFor(legacyToneLevelsSwitch);
legacyToneLevelsLabel.setTooltip(legacyToneLevelsSwitch.getTooltip());
GridPane.setHalignment(legacyToneLevelsLabel, HPos.RIGHT);
audioPane.add(legacyToneLevelsLabel, 3, 1);
@@ -739,9 +751,10 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
* current slider value and a small reset button. The label and button track the disabled state of the slider.
* @param slider to wrap
* @param defaultValue to apply to the slider when the reset button is clicked
* @param resetName accessible name for the reset button, announced by screen readers
* @return box containing the slider, the value label and the reset button
*/
private HBox createSliderWithReset(Slider slider, double defaultValue)
private HBox createSliderWithReset(Slider slider, double defaultValue, String resetName)
{
IconNode resetIcon = new IconNode(FontAwesome.UNDO);
resetIcon.setIconSize(10);
@@ -750,6 +763,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
resetIcon.fillProperty().bind(resetButton.textFillProperty());
resetButton.setGraphic(resetIcon);
resetButton.setTooltip(new Tooltip("Reset to default value"));
resetButton.setAccessibleText(resetName);
resetButton.disableProperty().bind(slider.disableProperty());
resetButton.setOnAction(event -> slider.setValue(defaultValue));

View File

@@ -21,6 +21,7 @@ package io.github.dsheirer.gui.playlist.channel;
import com.google.common.base.Joiner;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.playlist.IAliasListRefreshListener;
import io.github.dsheirer.module.decode.DecoderFactory;
import io.github.dsheirer.module.decode.DecoderType;
@@ -336,6 +337,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
searchBox.setSpacing(5);
searchBox.setAlignment(Pos.CENTER);
Label searchLabel = new Label("Search:");
searchLabel.setLabelFor(getSearchField());
searchLabel.setAlignment(Pos.CENTER_RIGHT);
searchBox.getChildren().addAll(searchLabel, getSearchField());
@@ -343,6 +345,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
viewBox.setSpacing(5);
viewBox.setAlignment(Pos.CENTER);
Label viewLabel = new Label("View Channels:");
AccessibilityUtil.labelGroup(viewLabel, getViewSegmentedButton());
viewBox.getChildren().addAll(viewLabel, getViewSegmentedButton());
mSearchAndViewBox.getChildren().addAll(searchBox, viewBox);
@@ -409,6 +412,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
if(mChannelTableView == null)
{
mChannelTableView = new TableView<>();
mChannelTableView.setAccessibleText("Channels");
TableColumn<Channel,Boolean> playingColumn = new TableColumn("Playing");
playingColumn.setPrefWidth(75);
@@ -425,12 +429,14 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
if(empty || item == null || !item)
{
setGraphic(null);
setAccessibleText(empty ? null : "No");
}
else
{
IconNode iconNode = new IconNode(FontAwesome.CHECK);
iconNode.setFill(Color.GREEN);
setGraphic(iconNode);
setAccessibleText("Yes");
}
}
};
@@ -453,12 +459,14 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
if(empty || item == null || !item)
{
setGraphic(null);
setAccessibleText(empty ? null : "No");
}
else
{
IconNode iconNode = new IconNode(FontAwesome.CHECK);
iconNode.setFill(Color.GREEN);
setGraphic(iconNode);
setAccessibleText("Yes");
}
}
};

View File

@@ -19,6 +19,7 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -142,6 +143,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label poolSizeLabel = new Label("Max Traffic Channels");
poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 0, row);
gridPane.getChildren().add(poolSizeLabel);
@@ -153,6 +155,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getIgnoreDataCallsButton());
Label ignoreDataLabel = new Label("Ignore Data Calls");
ignoreDataLabel.setLabelFor(getIgnoreDataCallsButton());
GridPane.setHalignment(ignoreDataLabel, HPos.LEFT);
GridPane.setConstraints(ignoreDataLabel, 3, row);
gridPane.getChildren().add(ignoreDataLabel);
@@ -161,6 +164,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getIgnoreCRCChecksumsButton());
Label ignoreCRCLabel = new Label("Ignore CRC Checksums (RAS)");
ignoreCRCLabel.setLabelFor(getIgnoreCRCChecksumsButton());
GridPane.setHalignment(ignoreCRCLabel, HPos.LEFT);
GridPane.setConstraints(ignoreCRCLabel, 5, row);
gridPane.getChildren().add(ignoreCRCLabel);
@@ -169,6 +173,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getUseCompressedTalkgroupsToggle());
Label useCompressedTalkgroupsLabel = new Label("Use Compressed Talkgroups");
useCompressedTalkgroupsLabel.setLabelFor(getUseCompressedTalkgroupsToggle());
GridPane.setHalignment(useCompressedTalkgroupsLabel, HPos.LEFT);
GridPane.setConstraints(useCompressedTalkgroupsLabel, 7, row);
gridPane.getChildren().add(useCompressedTalkgroupsLabel);
@@ -196,9 +201,11 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
editorBox.setSpacing(5);
Label lcnLabel = new Label("LCN");
lcnLabel.setLabelFor(getLogicalChannelNumberField());
editorBox.getChildren().addAll(lcnLabel, getLogicalChannelNumberField());
Label downlinkLabel = new Label("Frequency (MHz)");
downlinkLabel.setLabelFor(getDownlinkFrequencyField());
downlinkLabel.setPadding(new Insets(0,0,0,5));
editorBox.getChildren().addAll(downlinkLabel,getDownlinkFrequencyField());
@@ -286,6 +293,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
if(mTimeslotFrequencyTable == null)
{
mTimeslotFrequencyTable = new TableView<>(FXCollections.observableArrayList(TimeslotFrequency.extractor()));
mTimeslotFrequencyTable.setAccessibleText("Logical channel number (LCN) to frequency map");
mTimeslotFrequencyTable.setPrefHeight(100.0);
TableColumn<TimeslotFrequency,Number> numberColumn = new TableColumn("LCN");
@@ -346,6 +354,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
if(mAddTimeslotFrequencyButton == null)
{
mAddTimeslotFrequencyButton = new Button("Add");
mAddTimeslotFrequencyButton.setAccessibleText("Add LCN frequency");
mAddTimeslotFrequencyButton.setMaxWidth(Double.MAX_VALUE);
mAddTimeslotFrequencyButton.setOnAction(event -> addTimeslot());
}
@@ -394,6 +403,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
if(mDeleteTimeslotFrequencyButton == null)
{
mDeleteTimeslotFrequencyButton = new Button("Delete");
mDeleteTimeslotFrequencyButton.setAccessibleText("Delete LCN frequency");
mDeleteTimeslotFrequencyButton.setDisable(true);
mDeleteTimeslotFrequencyButton.setMaxWidth(Double.MAX_VALUE);
mDeleteTimeslotFrequencyButton.setOnAction(new EventHandler<ActionEvent>()
@@ -501,7 +511,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreDataCallsButton == null)
{
mIgnoreDataCallsButton = new ToggleSwitch();
mIgnoreDataCallsButton = new AccessibleToggleSwitch();
mIgnoreDataCallsButton.setDisable(true);
mIgnoreDataCallsButton.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -514,7 +524,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreCRCChecksumsButton == null)
{
mIgnoreCRCChecksumsButton = new ToggleSwitch();
mIgnoreCRCChecksumsButton = new AccessibleToggleSwitch();
mIgnoreCRCChecksumsButton.setDisable(true);
mIgnoreCRCChecksumsButton.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -532,7 +542,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mUseCompressedTalkgroupsToggle == null)
{
mUseCompressedTalkgroupsToggle = new ToggleSwitch();
mUseCompressedTalkgroupsToggle = new AccessibleToggleSwitch();
mUseCompressedTalkgroupsToggle.setTooltip(new Tooltip("Use Compressed Talkgroups. This is only for Hytera Tier-III Trunked Systems"));
mUseCompressedTalkgroupsToggle.setDisable(true);
mUseCompressedTalkgroupsToggle.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -563,7 +573,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeAGCButton == null)
{
mJmbeAGCButton = new ToggleSwitch();
mJmbeAGCButton = new AccessibleToggleSwitch();
mJmbeAGCButton.setTooltip(new Tooltip("Automatic Gain Control to automatically adjust call audio volume"));
mJmbeAGCButton.setDisable(true);
mJmbeAGCButton.selectedProperty().addListener((o, old, n) -> modifiedProperty().set(true));
@@ -576,7 +586,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mLegacyToneLevelsButton == null)
{
mLegacyToneLevelsButton = new ToggleSwitch();
mLegacyToneLevelsButton = new AccessibleToggleSwitch();
mLegacyToneLevelsButton.setTooltip(new Tooltip(
"Restore JMBE 1.x AMBE tone levels. Requires a supporting JMBE library; " +
"stock libraries ignore this option. Tone Volume still adjusts the selected level."));

View File

@@ -19,6 +19,7 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.playlist.decoder.AuxDecoderConfigurationEditor;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -146,6 +147,7 @@ public class LTRConfigurationEditor extends ChannelConfigurationEditor
gridPane.setHgap(10);
Label directionLabel = new Label("Direction");
AccessibilityUtil.labelGroup(directionLabel, getDirectionButton());
GridPane.setHalignment(directionLabel, HPos.LEFT);
GridPane.setConstraints(directionLabel, 0, 0);
gridPane.getChildren().add(directionLabel);

View File

@@ -19,6 +19,7 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.playlist.decoder.AuxDecoderConfigurationEditor;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -146,6 +147,7 @@ public class LTRNetConfigurationEditor extends ChannelConfigurationEditor
gridPane.setHgap(10);
Label directionLabel = new Label("Direction");
AccessibilityUtil.labelGroup(directionLabel, getDirectionButton());
GridPane.setHalignment(directionLabel, HPos.LEFT);
GridPane.setConstraints(directionLabel, 0, 0);
gridPane.getChildren().add(directionLabel);

View File

@@ -126,6 +126,7 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
gridPane.setVgap(10);
Label channelMapLabel = new Label("Channel Map");
channelMapLabel.setLabelFor(getChannelMapComboBox());
GridPane.setHalignment(channelMapLabel, HPos.RIGHT);
GridPane.setConstraints(channelMapLabel, 0, 0);
gridPane.getChildren().add(channelMapLabel);
@@ -138,6 +139,7 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getChannelMapEditButton());
Label poolSizeLabel = new Label("Max Traffic Channels");
poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 0, 1);
gridPane.getChildren().add(poolSizeLabel);
@@ -146,6 +148,7 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getTrafficChannelPoolSizeSpinner());
Label callTimeoutLabel = new Label("Call Timeout Seconds");
callTimeoutLabel.setLabelFor(getCallTimeoutSpinner());
GridPane.setHalignment(callTimeoutLabel, HPos.RIGHT);
GridPane.setConstraints(callTimeoutLabel, 2, 1);
gridPane.getChildren().add(callTimeoutLabel);

View File

@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.HexFormatter;
import io.github.dsheirer.gui.control.IntegerFormatter;
import io.github.dsheirer.gui.playlist.decoder.AuxDecoderConfigurationEditor;
@@ -135,6 +137,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
// channel bandwidth
Label bandwidthLabel = new Label("Channel Bandwidth");
AccessibilityUtil.labelGroup(bandwidthLabel, getBandwidthButton());
GridPane.setHalignment(bandwidthLabel, HPos.RIGHT);
GridPane.setConstraints(bandwidthLabel, 0, 0);
gridPane.getChildren().add(bandwidthLabel);
@@ -144,6 +147,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
//De-emphasis
Label deemphasisLabel = new Label("De-emphasis");
deemphasisLabel.setLabelFor(getDeemphasisCombo());
GridPane.setHalignment(deemphasisLabel, HPos.RIGHT);
GridPane.setConstraints(deemphasisLabel, 0, 1);
gridPane.getChildren().add(deemphasisLabel);
@@ -154,6 +158,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
// Note: normally the label and its toggle switch are one cell. They were split to make the next row
// look better. The label is manually created and placed here. Might want to create a new grid pane instead.
Label HPFLabel = new Label("High-pass Audio Filter (300 Hz)");
HPFLabel.setLabelFor(getAudioFilterEnable());
GridPane.setHalignment(HPFLabel, HPos.RIGHT);
GridPane.setConstraints(HPFLabel, 2, 1);
gridPane.getChildren().add(HPFLabel);
@@ -172,6 +177,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
*/
// talkgroup
Label talkgroupLabel = new Label("Talkgroup To Assign");
talkgroupLabel.setLabelFor(getTalkgroupField());
GridPane.setHalignment(talkgroupLabel, HPos.RIGHT);
GridPane.setConstraints(talkgroupLabel, 0, 2);
gridPane.getChildren().add(talkgroupLabel);
@@ -198,6 +204,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
modifiedProperty().set(true);
});
GridPane.setConstraints(mSquelchTypeCombo, 3, 2);
typeLabel.setLabelFor(mSquelchTypeCombo);
gridPane.getChildren().add(mSquelchTypeCombo);
// CTCSS code selector
@@ -318,6 +325,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getBasebandRecordSwitch());
Label recordBasebandLabel = new Label("Channel (Baseband I&Q)");
recordBasebandLabel.setLabelFor(getBasebandRecordSwitch());
GridPane.setHalignment(recordBasebandLabel, HPos.LEFT);
GridPane.setConstraints(recordBasebandLabel, 1, 0);
gridPane.getChildren().add(recordBasebandLabel);
@@ -381,7 +389,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
// removed label from toggle switch so that the label and the switch can be in different columns
// to make things look better on the next line. Perhaps make a new grid pane instead.
//mAudioFilterEnable = new ToggleSwitch("High-Pass Audio Filter (300 Hz)");
mAudioFilterEnable = new ToggleSwitch("");
mAudioFilterEnable = new AccessibleToggleSwitch("");
mAudioFilterEnable.setTooltip(new Tooltip("High-pass filter to remove DC offset and sub-audible signalling"));
mAudioFilterEnable.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
@@ -396,7 +404,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
{
if(mALCEnable == null)
{
mALCEnable = new ToggleSwitch("Automatic Level Control");
mALCEnable = new AccessibleToggleSwitch("Automatic Level Control");
mALCEnable.setTooltip(new Tooltip("Automatic Level Control (ALC). Tries to make the volume of each received transmission the same."));
mALCEnable.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
@@ -518,7 +526,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
{
if(mBasebandRecordSwitch == null)
{
mBasebandRecordSwitch = new ToggleSwitch();
mBasebandRecordSwitch = new AccessibleToggleSwitch();
mBasebandRecordSwitch.setDisable(true);
mBasebandRecordSwitch.setTextAlignment(TextAlignment.RIGHT);
mBasebandRecordSwitch.selectedProperty()

View File

@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -148,6 +150,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label mode = new Label("Mode:");
AccessibilityUtil.labelGroup(mode, getTransmissionModeButton());
GridPane.setHalignment(mode, HPos.RIGHT);
GridPane.setConstraints(mode, 0, row);
gridPane.getChildren().add(mode);
@@ -156,6 +159,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getTransmissionModeButton());
Label encoding = new Label("Talker Alias Encoding:");
encoding.setLabelFor(getEncodingComboBox());
GridPane.setHalignment(encoding, HPos.RIGHT);
GridPane.setConstraints(encoding, 2, row);
gridPane.getChildren().add(encoding);
@@ -164,6 +168,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getEncodingComboBox());
Label poolSizeLabel = new Label("Max Traffic Channels");
poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 4, row);
gridPane.getChildren().add(poolSizeLabel);
@@ -205,9 +210,11 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
editorBox.setSpacing(5);
Label lcnLabel = new Label("Channel Number");
lcnLabel.setLabelFor(getChannelField());
editorBox.getChildren().addAll(lcnLabel, getChannelField());
Label downlinkLabel = new Label("Downlink Frequency (MHz)");
downlinkLabel.setLabelFor(getDownlinkFrequencyField());
downlinkLabel.setPadding(new Insets(0,0,0,5));
editorBox.getChildren().addAll(downlinkLabel,getDownlinkFrequencyField());
@@ -312,6 +319,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
if(mChannelMapTable == null)
{
mChannelMapTable = new TableView<>(FXCollections.observableArrayList(ObservableChannelFrequency.extractor()));
mChannelMapTable.setAccessibleText("Channel number (LCN) to frequency map");
mChannelMapTable.setPrefHeight(100.0);
TableColumn<ObservableChannelFrequency,Number> channelColumn = new TableColumn<>("Channel");
@@ -401,6 +409,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
if(mAddTimeslotFrequencyButton == null)
{
mAddTimeslotFrequencyButton = new Button("Add");
mAddTimeslotFrequencyButton.setAccessibleText("Add channel frequency");
mAddTimeslotFrequencyButton.setMaxWidth(Double.MAX_VALUE);
mAddTimeslotFrequencyButton.setOnAction(event -> addChannelFrequency());
}
@@ -425,6 +434,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
if(mDeleteButton == null)
{
mDeleteButton = new Button("Delete");
mDeleteButton.setAccessibleText("Delete channel frequency");
mDeleteButton.setDisable(true);
mDeleteButton.setMaxWidth(Double.MAX_VALUE);
mDeleteButton.setOnAction(event -> {
@@ -519,7 +529,8 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreDataCallsToggle == null)
{
mIgnoreDataCallsToggle = new ToggleSwitch("Data Calls");
mIgnoreDataCallsToggle = new AccessibleToggleSwitch("Data Calls");
mIgnoreDataCallsToggle.setAccessibleText("Ignore data calls");
mIgnoreDataCallsToggle.setDisable(true);
mIgnoreDataCallsToggle.selectedProperty().addListener((o, ov, nv) -> modifiedProperty().set(true));
}
@@ -534,7 +545,8 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreEncryptedCallsToggle == null)
{
mIgnoreEncryptedCallsToggle = new ToggleSwitch("Encrypted Calls");
mIgnoreEncryptedCallsToggle = new AccessibleToggleSwitch("Encrypted Calls");
mIgnoreEncryptedCallsToggle.setAccessibleText("Ignore encrypted calls");
mIgnoreEncryptedCallsToggle.setDisable(true);
mIgnoreEncryptedCallsToggle.selectedProperty().addListener((o, ov, nv) -> modifiedProperty().set(true));
}
@@ -546,7 +558,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeAGCButton == null)
{
mJmbeAGCButton = new ToggleSwitch();
mJmbeAGCButton = new AccessibleToggleSwitch();
mJmbeAGCButton.setTooltip(new Tooltip("Automatic Gain Control to automatically adjust call audio volume"));
mJmbeAGCButton.setDisable(true);
mJmbeAGCButton.selectedProperty().addListener((o, old, n) -> modifiedProperty().set(true));
@@ -559,7 +571,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeLegacyToneLevelsButton == null)
{
mJmbeLegacyToneLevelsButton = new ToggleSwitch();
mJmbeLegacyToneLevelsButton = new AccessibleToggleSwitch();
mJmbeLegacyToneLevelsButton.setTooltip(new Tooltip(
"Restore JMBE 1.x AMBE tone levels. Requires a supporting JMBE library; " +
"stock libraries ignore this option. Tone Volume still adjusts the selected level."));

View File

@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
import io.github.dsheirer.gui.playlist.source.FrequencyEditor;
@@ -129,6 +131,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label modulationLabel = new Label("Modulation");
AccessibilityUtil.labelGroup(modulationLabel, getModulationSegmentedButton());
GridPane.setHalignment(modulationLabel, HPos.RIGHT);
GridPane.setConstraints(modulationLabel, 0, row);
gridPane.getChildren().add(modulationLabel);
@@ -137,6 +140,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().addAll(getModulationSegmentedButton());
Label poolSizeLabel = new Label("Max Traffic Channels");
poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 2, row);
gridPane.getChildren().add(poolSizeLabel);
@@ -148,6 +152,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getIgnoreDataCallsButton());
Label directionLabel = new Label("Ignore Data Calls");
directionLabel.setLabelFor(getIgnoreDataCallsButton());
GridPane.setHalignment(directionLabel, HPos.LEFT);
GridPane.setConstraints(directionLabel, 5, row);
gridPane.getChildren().add(directionLabel);
@@ -268,7 +273,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeAGCButton == null)
{
mJmbeAGCButton = new ToggleSwitch();
mJmbeAGCButton = new AccessibleToggleSwitch();
mJmbeAGCButton.setTooltip(new Tooltip("Automatic Gain Control to automatically adjust call audio volume"));
mJmbeAGCButton.setDisable(true);
mJmbeAGCButton.selectedProperty().addListener((o, old, n) -> modifiedProperty().set(true));
@@ -281,7 +286,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
{
if(mLegacyToneLevelsButton == null)
{
mLegacyToneLevelsButton = new ToggleSwitch();
mLegacyToneLevelsButton = new AccessibleToggleSwitch();
mLegacyToneLevelsButton.setTooltip(new Tooltip(
"Restore JMBE 1.x AMBE tone levels. Requires a supporting JMBE library; " +
"stock libraries ignore this option. Tone Volume still adjusts the selected level."));
@@ -342,7 +347,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreDataCallsButton == null)
{
mIgnoreDataCallsButton = new ToggleSwitch();
mIgnoreDataCallsButton = new AccessibleToggleSwitch();
mIgnoreDataCallsButton.setDisable(true);
mIgnoreDataCallsButton.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));

View File

@@ -19,6 +19,7 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -124,6 +125,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label poolSizeLabel = new Label("Max Traffic Channels");
poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 0, row);
gridPane.getChildren().add(poolSizeLabel);
@@ -135,11 +137,13 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getIgnoreDataCallsButton());
Label directionLabel = new Label("Ignore Data Calls");
directionLabel.setLabelFor(getIgnoreDataCallsButton());
GridPane.setHalignment(directionLabel, HPos.LEFT);
GridPane.setConstraints(directionLabel, 3, row);
gridPane.getChildren().add(directionLabel);
Label wacnLabel = new Label("WACN");
wacnLabel.setLabelFor(getWacnTextField());
GridPane.setHalignment(wacnLabel, HPos.RIGHT);
GridPane.setConstraints(wacnLabel, 0, ++row);
gridPane.getChildren().add(wacnLabel);
@@ -148,6 +152,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getWacnTextField());
Label systemLabel = new Label("System");
systemLabel.setLabelFor(getSystemTextField());
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 2, row);
gridPane.getChildren().add(systemLabel);
@@ -156,6 +161,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getSystemTextField());
Label nacLabel = new Label("NAC");
nacLabel.setLabelFor(getNacTextField());
GridPane.setHalignment(nacLabel, HPos.RIGHT);
GridPane.setConstraints(nacLabel, 4, row);
gridPane.getChildren().add(nacLabel);
@@ -183,7 +189,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeAGCButton == null)
{
mJmbeAGCButton = new ToggleSwitch();
mJmbeAGCButton = new AccessibleToggleSwitch();
mJmbeAGCButton.setTooltip(new Tooltip("Automatic Gain Control to automatically adjust call audio volume"));
mJmbeAGCButton.setDisable(true);
mJmbeAGCButton.selectedProperty().addListener((o, old, n) -> modifiedProperty().set(true));
@@ -196,7 +202,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
{
if(mLegacyToneLevelsButton == null)
{
mLegacyToneLevelsButton = new ToggleSwitch();
mLegacyToneLevelsButton = new AccessibleToggleSwitch();
mLegacyToneLevelsButton.setTooltip(new Tooltip(
"Restore JMBE 1.x AMBE tone levels. Requires a supporting JMBE library; " +
"stock libraries ignore this option. Tone Volume still adjusts the selected level."));
@@ -298,7 +304,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreDataCallsButton == null)
{
mIgnoreDataCallsButton = new ToggleSwitch();
mIgnoreDataCallsButton = new AccessibleToggleSwitch();
mIgnoreDataCallsButton.setDisable(true);
mIgnoreDataCallsButton.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));

View File

@@ -276,6 +276,7 @@ public class ChannelMapEditor extends SplitPane
if(mChannelMapListView == null)
{
mChannelMapListView = new ListView<>();
mChannelMapListView.setAccessibleText("Channel maps");
mChannelMapListView.setItems(mChannelMapModel.getChannelMaps());
mChannelMapListView.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> {
@@ -390,6 +391,7 @@ public class ChannelMapEditor extends SplitPane
mChannelRangePane.setHgap(5);
Label firstLabel = new Label("First Channel");
firstLabel.setLabelFor(getFirstField());
GridPane.setHalignment(firstLabel, HPos.RIGHT);
GridPane.setConstraints(firstLabel, 0, 0);
mChannelRangePane.getChildren().add(firstLabel);
@@ -399,6 +401,7 @@ public class ChannelMapEditor extends SplitPane
mChannelRangePane.getChildren().add(getFirstField());
Label lastLabel = new Label("Last Channel");
lastLabel.setLabelFor(getLastField());
GridPane.setHalignment(lastLabel, HPos.RIGHT);
GridPane.setConstraints(lastLabel, 0, 1);
mChannelRangePane.getChildren().add(lastLabel);
@@ -408,6 +411,7 @@ public class ChannelMapEditor extends SplitPane
mChannelRangePane.getChildren().add(getLastField());
Label baseLabel = new Label("Base Frequency (Hz)");
baseLabel.setLabelFor(getBaseFrequencyField());
GridPane.setHalignment(baseLabel, HPos.RIGHT);
GridPane.setConstraints(baseLabel, 0, 2);
mChannelRangePane.getChildren().add(baseLabel);
@@ -417,6 +421,7 @@ public class ChannelMapEditor extends SplitPane
mChannelRangePane.getChildren().add(getBaseFrequencyField());
Label stepSizeLabel = new Label("Step Size (Hz)");
stepSizeLabel.setLabelFor(getStepSizeField());
GridPane.setHalignment(stepSizeLabel, HPos.RIGHT);
GridPane.setConstraints(stepSizeLabel, 0, 3);
mChannelRangePane.getChildren().add(stepSizeLabel);
@@ -445,7 +450,9 @@ public class ChannelMapEditor extends SplitPane
nameBox.setSpacing(10);
nameBox.setAlignment(Pos.CENTER);
HBox.setHgrow(getNameField(), Priority.ALWAYS);
nameBox.getChildren().addAll(new Label("Name"), getNameField());
Label nameLabel = new Label("Name");
nameLabel.setLabelFor(getNameField());
nameBox.getChildren().addAll(nameLabel, getNameField());
VBox contentsBox = new VBox();
VBox.setVgrow(nameBox, Priority.NEVER);
@@ -599,6 +606,7 @@ public class ChannelMapEditor extends SplitPane
if(mChannelRangeTableView == null)
{
mChannelRangeTableView = new TableView<>();
mChannelRangeTableView.setAccessibleText("Channel ranges");
TableColumn<ChannelRange,Integer> firstColumn = new TableColumn("First");
firstColumn.setCellValueFactory(new PropertyValueFactory<ChannelRange,Integer>("firstChannel"));
@@ -790,6 +798,8 @@ public class ChannelMapEditor extends SplitPane
item == null || empty);
this.setStyle(invalidRange ? BACKGROUND_COLOR_RED : BACKGROND_COLOR_NONE);
//Invalid ranges are only shown in red, so also report them to screen readers
setAccessibleText(invalidRange && item != null && !empty ? item + ", invalid or overlapping range" : null);
}
@Override

View File

@@ -22,6 +22,7 @@
package io.github.dsheirer.gui.playlist.decoder;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.AuxDecodeConfiguration;
@@ -121,6 +122,7 @@ public class AuxDecoderConfigurationEditor extends Editor<AuxDecodeConfiguration
getChildren().add(getToggleSwitch());
Label label = new Label(mDecoderType.getDisplayString());
label.setLabelFor(getToggleSwitch());
GridPane.setHalignment(label, HPos.LEFT);
GridPane.setConstraints(label, 1, 0);
getChildren().add(label);
@@ -130,7 +132,7 @@ public class AuxDecoderConfigurationEditor extends Editor<AuxDecodeConfiguration
{
if(mToggleSwitch == null)
{
mToggleSwitch = new ToggleSwitch();
mToggleSwitch = new AccessibleToggleSwitch();
mToggleSwitch.setDisable(true);
mToggleSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}

View File

@@ -22,6 +22,7 @@
package io.github.dsheirer.gui.playlist.eventlog;
import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
@@ -117,6 +118,7 @@ public class EventLogConfigurationEditor extends Editor<EventLogConfiguration>
getChildren().add(getToggleSwitch());
Label label = new Label(mEventLogType.getDisplayString());
label.setLabelFor(getToggleSwitch());
GridPane.setHalignment(label, HPos.LEFT);
GridPane.setConstraints(label, 1, 0);
getChildren().add(label);
@@ -126,7 +128,7 @@ public class EventLogConfigurationEditor extends Editor<EventLogConfiguration>
{
if(mToggleSwitch == null)
{
mToggleSwitch = new ToggleSwitch();
mToggleSwitch = new AccessibleToggleSwitch();
mToggleSwitch.setDisable(true);
mToggleSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}

View File

@@ -127,6 +127,7 @@ public class PlaylistManagerEditor extends HBox
if(mPlaylistTableView == null)
{
mPlaylistTableView = new TableView<>();
mPlaylistTableView.setAccessibleText("Playlists");
TableColumn<Path,String> iconColumn = new TableColumn("Selected");
iconColumn.setPrefWidth(100);
iconColumn.setCellFactory(new Callback<TableColumn<Path,String>,TableCell<Path,String>>()
@@ -144,6 +145,7 @@ public class PlaylistManagerEditor extends HBox
if(empty || getTableRow() == null || getTableRow().getItem() == null)
{
setGraphic(null);
setAccessibleText(null);
}
else
{
@@ -154,16 +156,19 @@ public class PlaylistManagerEditor extends HBox
IconNode iconNode = new IconNode(FontAwesome.CHECK);
iconNode.setFill(Color.GREEN);
setGraphic(iconNode);
setAccessibleText("Selected");
}
else if(!path.toFile().exists())
{
IconNode iconNode = new IconNode(FontAwesome.TIMES);
iconNode.setFill(Color.RED);
setGraphic(iconNode);
setAccessibleText("File not found");
}
else
{
setGraphic(null);
setAccessibleText("Not selected");
}
}
}

View File

@@ -138,6 +138,7 @@ public class AgencyEditor extends VBox
if(mAgencyListView == null)
{
mAgencyListView = new ListView<>();
mAgencyListView.setAccessibleText("Agencies");
mAgencyListView.setPrefHeight(200);
mAgencyListView.setCellFactory(param -> new AgencyListCell());
mAgencyListView.getSelectionModel().selectedItemProperty()

View File

@@ -94,6 +94,7 @@ public class AgencyFrequencyEditor extends GridPane
getColumnConstraints().addAll(column1, column2, column3);
Label categoryLabel = new Label("Category");
categoryLabel.setLabelFor(getCategoryComboBox());
GridPane.setConstraints(categoryLabel, 0, row);
GridPane.setHalignment(categoryLabel, HPos.RIGHT);
getChildren().add(categoryLabel);
@@ -103,6 +104,7 @@ public class AgencyFrequencyEditor extends GridPane
getChildren().add(getCategoryComboBox());
Label subCategoryLabel = new Label("Sub-Category");
subCategoryLabel.setLabelFor(getSubCategoryComboBox());
GridPane.setConstraints(subCategoryLabel, 0, ++row);
GridPane.setHalignment(subCategoryLabel, HPos.RIGHT);
getChildren().add(subCategoryLabel);
@@ -207,6 +209,7 @@ public class AgencyFrequencyEditor extends GridPane
if(mFrequencyTableView == null)
{
mFrequencyTableView = new TableView<>();
mFrequencyTableView.setAccessibleText("Agency frequencies");
mFrequencyTableView.setMaxHeight(Double.MAX_VALUE);
mFrequencyTableView.setPlaceholder(getPlaceholderLabel());

View File

@@ -97,6 +97,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(detailsLabel);
Label alphaLabel = new Label("Alpha Tag");
alphaLabel.setLabelFor(getAlphaTagTextField());
GridPane.setHalignment(alphaLabel, HPos.RIGHT);
GridPane.setConstraints(alphaLabel, 0, ++row);
gridPane.getChildren().add(alphaLabel);
@@ -106,6 +107,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getAlphaTagTextField());
Label toneLabel = new Label("Tone");
toneLabel.setLabelFor(getToneTextField());
GridPane.setHalignment(toneLabel, HPos.RIGHT);
GridPane.setConstraints(toneLabel, 0, ++row);
gridPane.getChildren().add(toneLabel);
@@ -115,6 +117,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getToneTextField());
Label modeLabel = new Label("Mode");
modeLabel.setLabelFor(getModeTextField());
GridPane.setHalignment(modeLabel, HPos.RIGHT);
GridPane.setConstraints(modeLabel, 0, ++row);
gridPane.getChildren().add(modeLabel);
@@ -124,6 +127,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getModeTextField());
Label frequencyLabel = new Label("Frequency");
frequencyLabel.setLabelFor(getFrequencyTextField());
GridPane.setHalignment(frequencyLabel, HPos.RIGHT);
GridPane.setConstraints(frequencyLabel, 0, ++row);
gridPane.getChildren().add(frequencyLabel);
@@ -141,6 +145,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(createLabel);
Label systemLabel = new Label("System");
systemLabel.setLabelFor(getSystemTextField());
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 0, ++row);
gridPane.getChildren().add(systemLabel);
@@ -150,6 +155,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getSystemTextField());
Label siteLabel = new Label("Site");
siteLabel.setLabelFor(getSiteTextField());
GridPane.setHalignment(siteLabel, HPos.RIGHT);
GridPane.setConstraints(siteLabel, 0, ++row);
gridPane.getChildren().add(siteLabel);
@@ -159,6 +165,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getSiteTextField());
Label nameLabel = new Label("Name");
nameLabel.setLabelFor(getNameTextField());
GridPane.setHalignment(nameLabel, HPos.RIGHT);
GridPane.setConstraints(nameLabel, 0, ++row);
gridPane.getChildren().add(nameLabel);
@@ -168,6 +175,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getNameTextField());
Label decoderLabel = new Label("Decoder");
decoderLabel.setLabelFor(getDecoderTextField());
GridPane.setHalignment(decoderLabel, HPos.RIGHT);
GridPane.setConstraints(decoderLabel, 0, ++row);
gridPane.getChildren().add(decoderLabel);

Some files were not shown because too many files have changed in this diff Show More