mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
Compare commits
21 Commits
jtransform
...
v1.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4ec04a499 | ||
|
|
006e382b34 | ||
|
|
beb2593dd9 | ||
|
|
657cbf5c55 | ||
|
|
aff34fa5eb | ||
|
|
22cd8968cb | ||
|
|
ec7efa4d17 | ||
|
|
07fd8c2923 | ||
|
|
a7301e93d9 | ||
|
|
5912a958b3 | ||
|
|
9ca4ae4d8f | ||
|
|
6cd2cea30b | ||
|
|
ba16c8b377 | ||
|
|
d72c5d87be | ||
|
|
dea3a3a41e | ||
|
|
48296990e9 | ||
|
|
289150d415 | ||
|
|
69d49d9dd0 | ||
|
|
e8e813142b | ||
|
|
968705a589 | ||
|
|
1c097491d9 |
10
.github/workflows/gradle.yml
vendored
10
.github/workflows/gradle.yml
vendored
@@ -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
|
||||
|
||||
17
.github/workflows/nightly.yml
vendored
17
.github/workflows/nightly.yml
vendored
@@ -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
130
.github/workflows/release.yml
vendored
Normal 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
|
||||
25
README.md
25
README.md
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
21
build.gradle
21
build.gradle
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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'
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,779 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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/>
|
||||
* ****************************************************************************
|
||||
*
|
||||
* The transform kernels in this class are ported from the mixed radix complex FFT (cffti, cfftf and passf2/3/4/5)
|
||||
* in JTransforms org.jtransforms.fft.FloatFFT_1D, which carries the following license:
|
||||
*
|
||||
* JTransforms
|
||||
* Copyright (c) 2007 onward, Piotr Wendykier
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package io.github.dsheirer.dsp.fft;
|
||||
|
||||
/**
|
||||
* Allocation-free mixed radix complex FFT for sizes that factor into 2, 3 and 5 only.
|
||||
*
|
||||
* This is a port of the JTransforms FloatFFT_1D mixed radix complex transform path. JTransforms allocates a new
|
||||
* 2N float scratch array on every mixed radix transform, which creates significant garbage collection pressure when
|
||||
* the transform runs at block rate (e.g. the polyphase channelizer IFFT). This implementation pre-allocates the
|
||||
* scratch buffer once and reuses it for each transform.
|
||||
*
|
||||
* Results match JTransforms FloatFFT_1D complexForward() and complexInverse(): the forward transform uses the
|
||||
* exp(-j...) kernel, the inverse uses exp(+j...), and the inverse optionally scales by 1/N. Twiddle factors are
|
||||
* calculated in double precision, so results can differ from JTransforms in the least significant bits.
|
||||
*
|
||||
* Samples are interleaved complex: a[offset + 2k] = Re[k] and a[offset + 2k + 1] = Im[k] for 0 <= k < N.
|
||||
*
|
||||
* Note: this class is NOT thread-safe since it reuses an internal scratch buffer. Use one instance per thread.
|
||||
*/
|
||||
public class MixedRadixComplexFFT
|
||||
{
|
||||
private static final int[] FACTORS = {4, 2, 3, 5};
|
||||
private static final int FORWARD = -1;
|
||||
private static final int INVERSE = 1;
|
||||
|
||||
private final int mSize;
|
||||
private final int[] mFactors;
|
||||
private final float[] mTwiddles;
|
||||
private final float[] mScratch;
|
||||
private final float mScale;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param size of the transform in complex samples. Must be a positive value with no prime factors other than
|
||||
* 2, 3 and 5.
|
||||
* @throws IllegalArgumentException if the size is not supported
|
||||
*/
|
||||
public MixedRadixComplexFFT(int size)
|
||||
{
|
||||
if(!isSupported(size))
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported FFT size [" + size +
|
||||
"] - size must be positive and have no prime factors other than 2, 3 and 5");
|
||||
}
|
||||
|
||||
mSize = size;
|
||||
mFactors = factorize(size);
|
||||
mTwiddles = new float[2 * size + 2];
|
||||
mScratch = new float[2 * size];
|
||||
mScale = 1.0f / size;
|
||||
calculateTwiddles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the size can be transformed by this class.
|
||||
* @param size in complex samples
|
||||
* @return true if the size is positive and has no prime factors other than 2, 3 and 5.
|
||||
*/
|
||||
public static boolean isSupported(int size)
|
||||
{
|
||||
if(size < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int remainder = size;
|
||||
|
||||
for(int factor: new int[]{2, 3, 5})
|
||||
{
|
||||
while(remainder % factor == 0)
|
||||
{
|
||||
remainder /= factor;
|
||||
}
|
||||
}
|
||||
|
||||
return remainder == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of this transform
|
||||
* @return size in complex samples
|
||||
*/
|
||||
public int getSize()
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the forward DFT of the interleaved complex samples, in place. Equivalent to JTransforms
|
||||
* FloatFFT_1D.complexForward(samples, offset).
|
||||
* @param samples interleaved complex, with at least 2N values from the offset
|
||||
* @param offset to the first sample
|
||||
*/
|
||||
public void forward(float[] samples, int offset)
|
||||
{
|
||||
transform(samples, offset, FORWARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the forward DFT of the interleaved complex samples, in place.
|
||||
* @param samples interleaved complex, with at least 2N values
|
||||
*/
|
||||
public void forward(float[] samples)
|
||||
{
|
||||
forward(samples, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the inverse DFT of the interleaved complex samples, in place. Equivalent to JTransforms
|
||||
* FloatFFT_1D.complexInverse(samples, offset, scale).
|
||||
* @param samples interleaved complex, with at least 2N values from the offset
|
||||
* @param offset to the first sample
|
||||
* @param scale true to scale the results by 1/N
|
||||
*/
|
||||
public void inverse(float[] samples, int offset, boolean scale)
|
||||
{
|
||||
transform(samples, offset, INVERSE);
|
||||
|
||||
if(scale && mSize > 1)
|
||||
{
|
||||
int end = offset + 2 * mSize;
|
||||
|
||||
for(int x = offset; x < end; x++)
|
||||
{
|
||||
samples[x] *= mScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the inverse DFT of the interleaved complex samples, in place.
|
||||
* @param samples interleaved complex, with at least 2N values
|
||||
* @param scale true to scale the results by 1/N
|
||||
*/
|
||||
public void inverse(float[] samples, boolean scale)
|
||||
{
|
||||
inverse(samples, 0, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factors the size into the radix sequence used by the transform, ported from JTransforms cffti(). Radix 4 is
|
||||
* preferred, then 2, 3 and 5, and a radix 2 factor is moved to the front of the sequence.
|
||||
*/
|
||||
private static int[] factorize(int size)
|
||||
{
|
||||
if(size == 1)
|
||||
{
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
int[] factors = new int[32];
|
||||
int count = 0;
|
||||
int remainder = size;
|
||||
|
||||
for(int factor: FACTORS)
|
||||
{
|
||||
while(remainder % factor == 0)
|
||||
{
|
||||
factors[count++] = factor;
|
||||
remainder /= factor;
|
||||
|
||||
if(factor == 2 && count != 1)
|
||||
{
|
||||
System.arraycopy(factors, 0, factors, 1, count - 1);
|
||||
factors[0] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(remainder != 1)
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported FFT size [" + size + "]");
|
||||
}
|
||||
|
||||
int[] result = new int[count];
|
||||
System.arraycopy(factors, 0, result, 0, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the twiddle factor table, ported from JTransforms cffti() with the table offset removed and the
|
||||
* angles calculated in double precision.
|
||||
*/
|
||||
private void calculateTwiddles()
|
||||
{
|
||||
if(mSize == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double angleIncrement = 2.0 * Math.PI / mSize;
|
||||
int i = 1;
|
||||
int l1 = 1;
|
||||
|
||||
for(int factor: mFactors)
|
||||
{
|
||||
int ld = 0;
|
||||
int l2 = l1 * factor;
|
||||
int ido = mSize / l2;
|
||||
int idot = ido + ido + 2;
|
||||
|
||||
for(int j = 1; j < factor; j++)
|
||||
{
|
||||
mTwiddles[i - 1] = 1;
|
||||
mTwiddles[i] = 0;
|
||||
ld += l1;
|
||||
int fi = 0;
|
||||
double argld = ld * angleIncrement;
|
||||
|
||||
for(int ii = 4; ii <= idot; ii += 2)
|
||||
{
|
||||
i += 2;
|
||||
fi++;
|
||||
double arg = fi * argld;
|
||||
mTwiddles[i - 1] = (float)Math.cos(arg);
|
||||
mTwiddles[i] = (float)Math.sin(arg);
|
||||
}
|
||||
}
|
||||
|
||||
l1 = l2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the transform, ported from JTransforms cfftf() using the reusable scratch buffer.
|
||||
* @param a samples
|
||||
* @param offa offset to first sample
|
||||
* @param isign -1 for forward and +1 for inverse
|
||||
*/
|
||||
private void transform(float[] a, int offa, int isign)
|
||||
{
|
||||
if(mSize == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float[] ch = mScratch;
|
||||
int twon = 2 * mSize;
|
||||
boolean inScratch = false;
|
||||
int l1 = 1;
|
||||
int iw = 0;
|
||||
|
||||
for(int factor: mFactors)
|
||||
{
|
||||
int l2 = factor * l1;
|
||||
int idot = 2 * (mSize / l2);
|
||||
|
||||
switch(factor)
|
||||
{
|
||||
case 4:
|
||||
if(inScratch)
|
||||
{
|
||||
passf4(idot, l1, ch, 0, a, offa, iw, isign);
|
||||
}
|
||||
else
|
||||
{
|
||||
passf4(idot, l1, a, offa, ch, 0, iw, isign);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if(inScratch)
|
||||
{
|
||||
passf2(idot, l1, ch, 0, a, offa, iw, isign);
|
||||
}
|
||||
else
|
||||
{
|
||||
passf2(idot, l1, a, offa, ch, 0, iw, isign);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if(inScratch)
|
||||
{
|
||||
passf3(idot, l1, ch, 0, a, offa, iw, isign);
|
||||
}
|
||||
else
|
||||
{
|
||||
passf3(idot, l1, a, offa, ch, 0, iw, isign);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if(inScratch)
|
||||
{
|
||||
passf5(idot, l1, ch, 0, a, offa, iw, isign);
|
||||
}
|
||||
else
|
||||
{
|
||||
passf5(idot, l1, a, offa, ch, 0, iw, isign);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported radix: " + factor);
|
||||
}
|
||||
|
||||
inScratch = !inScratch;
|
||||
l1 = l2;
|
||||
iw += (factor - 1) * idot;
|
||||
}
|
||||
|
||||
if(inScratch)
|
||||
{
|
||||
System.arraycopy(ch, 0, a, offa, twon);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radix 2 butterfly, ported from JTransforms passf2().
|
||||
*/
|
||||
private void passf2(final int ido, final int l1, final float[] in, final int in_off, final float[] out,
|
||||
final int out_off, final int offset, final int isign)
|
||||
{
|
||||
final float[] wtable = mTwiddles;
|
||||
int iw1 = offset;
|
||||
int idx = ido * l1;
|
||||
|
||||
if(ido <= 2)
|
||||
{
|
||||
for(int k = 0; k < l1; k++)
|
||||
{
|
||||
int idx0 = k * ido;
|
||||
int iidx1 = in_off + 2 * idx0;
|
||||
int iidx2 = iidx1 + ido;
|
||||
float a1r = in[iidx1];
|
||||
float a1i = in[iidx1 + 1];
|
||||
float a2r = in[iidx2];
|
||||
float a2i = in[iidx2 + 1];
|
||||
|
||||
int oidx1 = out_off + idx0;
|
||||
int oidx2 = oidx1 + idx;
|
||||
out[oidx1] = a1r + a2r;
|
||||
out[oidx1 + 1] = a1i + a2i;
|
||||
out[oidx2] = a1r - a2r;
|
||||
out[oidx2 + 1] = a1i - a2i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < l1; k++)
|
||||
{
|
||||
for(int i = 0; i < ido - 1; i += 2)
|
||||
{
|
||||
int idx0 = k * ido;
|
||||
int iidx1 = in_off + i + 2 * idx0;
|
||||
int iidx2 = iidx1 + ido;
|
||||
float i1r = in[iidx1];
|
||||
float i1i = in[iidx1 + 1];
|
||||
float i2r = in[iidx2];
|
||||
float i2i = in[iidx2 + 1];
|
||||
|
||||
int widx1 = i + iw1;
|
||||
float w1r = wtable[widx1];
|
||||
float w1i = isign * wtable[widx1 + 1];
|
||||
|
||||
float t1r = i1r - i2r;
|
||||
float t1i = i1i - i2i;
|
||||
|
||||
int oidx1 = out_off + i + idx0;
|
||||
int oidx2 = oidx1 + idx;
|
||||
out[oidx1] = i1r + i2r;
|
||||
out[oidx1 + 1] = i1i + i2i;
|
||||
out[oidx2] = w1r * t1r - w1i * t1i;
|
||||
out[oidx2 + 1] = w1r * t1i + w1i * t1r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radix 3 butterfly, ported from JTransforms passf3().
|
||||
*/
|
||||
private void passf3(final int ido, final int l1, final float[] in, final int in_off, final float[] out,
|
||||
final int out_off, final int offset, final int isign)
|
||||
{
|
||||
final float taur = -0.5f;
|
||||
final float taui = 0.866025403784438707610604524234076962f;
|
||||
final float[] wtable = mTwiddles;
|
||||
int iw1 = offset;
|
||||
int iw2 = iw1 + ido;
|
||||
final int idxt = l1 * ido;
|
||||
|
||||
if(ido == 2)
|
||||
{
|
||||
for(int k = 1; k <= l1; k++)
|
||||
{
|
||||
int iidx1 = in_off + (3 * k - 2) * ido;
|
||||
int iidx2 = iidx1 + ido;
|
||||
int iidx3 = iidx1 - ido;
|
||||
float i1r = in[iidx1];
|
||||
float i1i = in[iidx1 + 1];
|
||||
float i2r = in[iidx2];
|
||||
float i2i = in[iidx2 + 1];
|
||||
float i3r = in[iidx3];
|
||||
float i3i = in[iidx3 + 1];
|
||||
|
||||
float tr2 = i1r + i2r;
|
||||
float cr2 = i3r + taur * tr2;
|
||||
float ti2 = i1i + i2i;
|
||||
float ci2 = i3i + taur * ti2;
|
||||
float cr3 = isign * taui * (i1r - i2r);
|
||||
float ci3 = isign * taui * (i1i - i2i);
|
||||
|
||||
int oidx1 = out_off + (k - 1) * ido;
|
||||
int oidx2 = oidx1 + idxt;
|
||||
int oidx3 = oidx2 + idxt;
|
||||
out[oidx1] = i3r + tr2;
|
||||
out[oidx1 + 1] = i3i + ti2;
|
||||
out[oidx2] = cr2 - ci3;
|
||||
out[oidx2 + 1] = ci2 + cr3;
|
||||
out[oidx3] = cr2 + ci3;
|
||||
out[oidx3 + 1] = ci2 - cr3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 1; k <= l1; k++)
|
||||
{
|
||||
int idx1 = in_off + (3 * k - 2) * ido;
|
||||
int idx2 = out_off + (k - 1) * ido;
|
||||
|
||||
for(int i = 0; i < ido - 1; i += 2)
|
||||
{
|
||||
int iidx1 = i + idx1;
|
||||
int iidx2 = iidx1 + ido;
|
||||
int iidx3 = iidx1 - ido;
|
||||
float a1r = in[iidx1];
|
||||
float a1i = in[iidx1 + 1];
|
||||
float a2r = in[iidx2];
|
||||
float a2i = in[iidx2 + 1];
|
||||
float a3r = in[iidx3];
|
||||
float a3i = in[iidx3 + 1];
|
||||
|
||||
float tr2 = a1r + a2r;
|
||||
float cr2 = a3r + taur * tr2;
|
||||
float ti2 = a1i + a2i;
|
||||
float ci2 = a3i + taur * ti2;
|
||||
float cr3 = isign * taui * (a1r - a2r);
|
||||
float ci3 = isign * taui * (a1i - a2i);
|
||||
float dr2 = cr2 - ci3;
|
||||
float dr3 = cr2 + ci3;
|
||||
float di2 = ci2 + cr3;
|
||||
float di3 = ci2 - cr3;
|
||||
|
||||
int widx1 = i + iw1;
|
||||
int widx2 = i + iw2;
|
||||
float w1r = wtable[widx1];
|
||||
float w1i = isign * wtable[widx1 + 1];
|
||||
float w2r = wtable[widx2];
|
||||
float w2i = isign * wtable[widx2 + 1];
|
||||
|
||||
int oidx1 = i + idx2;
|
||||
int oidx2 = oidx1 + idxt;
|
||||
int oidx3 = oidx2 + idxt;
|
||||
out[oidx1] = a3r + tr2;
|
||||
out[oidx1 + 1] = a3i + ti2;
|
||||
out[oidx2] = w1r * dr2 - w1i * di2;
|
||||
out[oidx2 + 1] = w1r * di2 + w1i * dr2;
|
||||
out[oidx3] = w2r * dr3 - w2i * di3;
|
||||
out[oidx3 + 1] = w2r * di3 + w2i * dr3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radix 4 butterfly, ported from JTransforms passf4().
|
||||
*/
|
||||
private void passf4(final int ido, final int l1, final float[] in, final int in_off, final float[] out,
|
||||
final int out_off, final int offset, final int isign)
|
||||
{
|
||||
final float[] wtable = mTwiddles;
|
||||
int iw1 = offset;
|
||||
int iw2 = iw1 + ido;
|
||||
int iw3 = iw2 + ido;
|
||||
int idx0 = l1 * ido;
|
||||
|
||||
if(ido == 2)
|
||||
{
|
||||
for(int k = 0; k < l1; k++)
|
||||
{
|
||||
int idxt1 = k * ido;
|
||||
int iidx1 = in_off + 4 * idxt1 + 1;
|
||||
int iidx2 = iidx1 + ido;
|
||||
int iidx3 = iidx2 + ido;
|
||||
int iidx4 = iidx3 + ido;
|
||||
|
||||
float i1i = in[iidx1 - 1];
|
||||
float i1r = in[iidx1];
|
||||
float i2i = in[iidx2 - 1];
|
||||
float i2r = in[iidx2];
|
||||
float i3i = in[iidx3 - 1];
|
||||
float i3r = in[iidx3];
|
||||
float i4i = in[iidx4 - 1];
|
||||
float i4r = in[iidx4];
|
||||
|
||||
float ti1 = i1r - i3r;
|
||||
float ti2 = i1r + i3r;
|
||||
float tr4 = i4r - i2r;
|
||||
float ti3 = i2r + i4r;
|
||||
float tr1 = i1i - i3i;
|
||||
float tr2 = i1i + i3i;
|
||||
float ti4 = i2i - i4i;
|
||||
float tr3 = i2i + i4i;
|
||||
|
||||
int oidx1 = out_off + idxt1;
|
||||
int oidx2 = oidx1 + idx0;
|
||||
int oidx3 = oidx2 + idx0;
|
||||
int oidx4 = oidx3 + idx0;
|
||||
out[oidx1] = tr2 + tr3;
|
||||
out[oidx1 + 1] = ti2 + ti3;
|
||||
out[oidx2] = tr1 + isign * tr4;
|
||||
out[oidx2 + 1] = ti1 + isign * ti4;
|
||||
out[oidx3] = tr2 - tr3;
|
||||
out[oidx3 + 1] = ti2 - ti3;
|
||||
out[oidx4] = tr1 - isign * tr4;
|
||||
out[oidx4 + 1] = ti1 - isign * ti4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < l1; k++)
|
||||
{
|
||||
int idx1 = k * ido;
|
||||
int idx2 = in_off + 1 + 4 * idx1;
|
||||
|
||||
for(int i = 0; i < ido - 1; i += 2)
|
||||
{
|
||||
int iidx1 = i + idx2;
|
||||
int iidx2 = iidx1 + ido;
|
||||
int iidx3 = iidx2 + ido;
|
||||
int iidx4 = iidx3 + ido;
|
||||
float i1i = in[iidx1 - 1];
|
||||
float i1r = in[iidx1];
|
||||
float i2i = in[iidx2 - 1];
|
||||
float i2r = in[iidx2];
|
||||
float i3i = in[iidx3 - 1];
|
||||
float i3r = in[iidx3];
|
||||
float i4i = in[iidx4 - 1];
|
||||
float i4r = in[iidx4];
|
||||
|
||||
float ti1 = i1r - i3r;
|
||||
float ti2 = i1r + i3r;
|
||||
float ti3 = i2r + i4r;
|
||||
float tr4 = i4r - i2r;
|
||||
float tr1 = i1i - i3i;
|
||||
float tr2 = i1i + i3i;
|
||||
float ti4 = i2i - i4i;
|
||||
float tr3 = i2i + i4i;
|
||||
float cr3 = tr2 - tr3;
|
||||
float ci3 = ti2 - ti3;
|
||||
float cr2 = tr1 + isign * tr4;
|
||||
float cr4 = tr1 - isign * tr4;
|
||||
float ci2 = ti1 + isign * ti4;
|
||||
float ci4 = ti1 - isign * ti4;
|
||||
|
||||
int widx1 = i + iw1;
|
||||
int widx2 = i + iw2;
|
||||
int widx3 = i + iw3;
|
||||
float w1r = wtable[widx1];
|
||||
float w1i = isign * wtable[widx1 + 1];
|
||||
float w2r = wtable[widx2];
|
||||
float w2i = isign * wtable[widx2 + 1];
|
||||
float w3r = wtable[widx3];
|
||||
float w3i = isign * wtable[widx3 + 1];
|
||||
|
||||
int oidx1 = out_off + i + idx1;
|
||||
int oidx2 = oidx1 + idx0;
|
||||
int oidx3 = oidx2 + idx0;
|
||||
int oidx4 = oidx3 + idx0;
|
||||
out[oidx1] = tr2 + tr3;
|
||||
out[oidx1 + 1] = ti2 + ti3;
|
||||
out[oidx2] = w1r * cr2 - w1i * ci2;
|
||||
out[oidx2 + 1] = w1r * ci2 + w1i * cr2;
|
||||
out[oidx3] = w2r * cr3 - w2i * ci3;
|
||||
out[oidx3 + 1] = w2r * ci3 + w2i * cr3;
|
||||
out[oidx4] = w3r * cr4 - w3i * ci4;
|
||||
out[oidx4 + 1] = w3r * ci4 + w3i * cr4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radix 5 butterfly, ported from JTransforms passf5().
|
||||
*/
|
||||
private void passf5(final int ido, final int l1, final float[] in, final int in_off, final float[] out,
|
||||
final int out_off, final int offset, final int isign)
|
||||
{
|
||||
final float tr11 = 0.309016994374947451262869435595348477f;
|
||||
final float ti11 = 0.951056516295153531181938433292089030f;
|
||||
final float tr12 = -0.809016994374947340240566973079694435f;
|
||||
final float ti12 = 0.587785252292473248125759255344746634f;
|
||||
final float[] wtable = mTwiddles;
|
||||
int iw1 = offset;
|
||||
int iw2 = iw1 + ido;
|
||||
int iw3 = iw2 + ido;
|
||||
int iw4 = iw3 + ido;
|
||||
int idx0 = l1 * ido;
|
||||
|
||||
if(ido == 2)
|
||||
{
|
||||
for(int k = 1; k <= l1; ++k)
|
||||
{
|
||||
int iidx1 = in_off + (5 * k - 4) * ido + 1;
|
||||
int iidx2 = iidx1 + ido;
|
||||
int iidx3 = iidx1 - ido;
|
||||
int iidx4 = iidx2 + ido;
|
||||
int iidx5 = iidx4 + ido;
|
||||
|
||||
float i1i = in[iidx1 - 1];
|
||||
float i1r = in[iidx1];
|
||||
float i2i = in[iidx2 - 1];
|
||||
float i2r = in[iidx2];
|
||||
float i3i = in[iidx3 - 1];
|
||||
float i3r = in[iidx3];
|
||||
float i4i = in[iidx4 - 1];
|
||||
float i4r = in[iidx4];
|
||||
float i5i = in[iidx5 - 1];
|
||||
float i5r = in[iidx5];
|
||||
|
||||
float ti5 = i1r - i5r;
|
||||
float ti2 = i1r + i5r;
|
||||
float ti4 = i2r - i4r;
|
||||
float ti3 = i2r + i4r;
|
||||
float tr5 = i1i - i5i;
|
||||
float tr2 = i1i + i5i;
|
||||
float tr4 = i2i - i4i;
|
||||
float tr3 = i2i + i4i;
|
||||
float cr2 = i3i + tr11 * tr2 + tr12 * tr3;
|
||||
float ci2 = i3r + tr11 * ti2 + tr12 * ti3;
|
||||
float cr3 = i3i + tr12 * tr2 + tr11 * tr3;
|
||||
float ci3 = i3r + tr12 * ti2 + tr11 * ti3;
|
||||
float cr5 = isign * (ti11 * tr5 + ti12 * tr4);
|
||||
float ci5 = isign * (ti11 * ti5 + ti12 * ti4);
|
||||
float cr4 = isign * (ti12 * tr5 - ti11 * tr4);
|
||||
float ci4 = isign * (ti12 * ti5 - ti11 * ti4);
|
||||
|
||||
int oidx1 = out_off + (k - 1) * ido;
|
||||
int oidx2 = oidx1 + idx0;
|
||||
int oidx3 = oidx2 + idx0;
|
||||
int oidx4 = oidx3 + idx0;
|
||||
int oidx5 = oidx4 + idx0;
|
||||
out[oidx1] = i3i + tr2 + tr3;
|
||||
out[oidx1 + 1] = i3r + ti2 + ti3;
|
||||
out[oidx2] = cr2 - ci5;
|
||||
out[oidx2 + 1] = ci2 + cr5;
|
||||
out[oidx3] = cr3 - ci4;
|
||||
out[oidx3 + 1] = ci3 + cr4;
|
||||
out[oidx4] = cr3 + ci4;
|
||||
out[oidx4 + 1] = ci3 - cr4;
|
||||
out[oidx5] = cr2 + ci5;
|
||||
out[oidx5 + 1] = ci2 - cr5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 1; k <= l1; k++)
|
||||
{
|
||||
int idx1 = in_off + 1 + (k * 5 - 4) * ido;
|
||||
int idx2 = out_off + (k - 1) * ido;
|
||||
|
||||
for(int i = 0; i < ido - 1; i += 2)
|
||||
{
|
||||
int iidx1 = i + idx1;
|
||||
int iidx2 = iidx1 + ido;
|
||||
int iidx3 = iidx1 - ido;
|
||||
int iidx4 = iidx2 + ido;
|
||||
int iidx5 = iidx4 + ido;
|
||||
float i1i = in[iidx1 - 1];
|
||||
float i1r = in[iidx1];
|
||||
float i2i = in[iidx2 - 1];
|
||||
float i2r = in[iidx2];
|
||||
float i3i = in[iidx3 - 1];
|
||||
float i3r = in[iidx3];
|
||||
float i4i = in[iidx4 - 1];
|
||||
float i4r = in[iidx4];
|
||||
float i5i = in[iidx5 - 1];
|
||||
float i5r = in[iidx5];
|
||||
|
||||
float ti5 = i1r - i5r;
|
||||
float ti2 = i1r + i5r;
|
||||
float ti4 = i2r - i4r;
|
||||
float ti3 = i2r + i4r;
|
||||
float tr5 = i1i - i5i;
|
||||
float tr2 = i1i + i5i;
|
||||
float tr4 = i2i - i4i;
|
||||
float tr3 = i2i + i4i;
|
||||
float cr2 = i3i + tr11 * tr2 + tr12 * tr3;
|
||||
float ci2 = i3r + tr11 * ti2 + tr12 * ti3;
|
||||
float cr3 = i3i + tr12 * tr2 + tr11 * tr3;
|
||||
float ci3 = i3r + tr12 * ti2 + tr11 * ti3;
|
||||
float cr5 = isign * (ti11 * tr5 + ti12 * tr4);
|
||||
float ci5 = isign * (ti11 * ti5 + ti12 * ti4);
|
||||
float cr4 = isign * (ti12 * tr5 - ti11 * tr4);
|
||||
float ci4 = isign * (ti12 * ti5 - ti11 * ti4);
|
||||
float dr3 = cr3 - ci4;
|
||||
float dr4 = cr3 + ci4;
|
||||
float di3 = ci3 + cr4;
|
||||
float di4 = ci3 - cr4;
|
||||
float dr5 = cr2 + ci5;
|
||||
float dr2 = cr2 - ci5;
|
||||
float di5 = ci2 - cr5;
|
||||
float di2 = ci2 + cr5;
|
||||
|
||||
int widx1 = i + iw1;
|
||||
int widx2 = i + iw2;
|
||||
int widx3 = i + iw3;
|
||||
int widx4 = i + iw4;
|
||||
float w1r = wtable[widx1];
|
||||
float w1i = isign * wtable[widx1 + 1];
|
||||
float w2r = wtable[widx2];
|
||||
float w2i = isign * wtable[widx2 + 1];
|
||||
float w3r = wtable[widx3];
|
||||
float w3i = isign * wtable[widx3 + 1];
|
||||
float w4r = wtable[widx4];
|
||||
float w4i = isign * wtable[widx4 + 1];
|
||||
|
||||
int oidx1 = i + idx2;
|
||||
int oidx2 = oidx1 + idx0;
|
||||
int oidx3 = oidx2 + idx0;
|
||||
int oidx4 = oidx3 + idx0;
|
||||
int oidx5 = oidx4 + idx0;
|
||||
out[oidx1] = i3i + tr2 + tr3;
|
||||
out[oidx1 + 1] = i3r + ti2 + ti3;
|
||||
out[oidx2] = w1r * dr2 - w1i * di2;
|
||||
out[oidx2 + 1] = w1r * di2 + w1i * dr2;
|
||||
out[oidx3] = w2r * dr3 - w2i * di3;
|
||||
out[oidx3 + 1] = w2r * di3 + w2i * dr3;
|
||||
out[oidx4] = w3r * dr4 - w3i * di4;
|
||||
out[oidx4 + 1] = w3r * di4 + w3i * dr4;
|
||||
out[oidx5] = w4r * dr5 - w4i * di5;
|
||||
out[oidx5 + 1] = w4r * di5 + w4i * dr5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package io.github.dsheirer.dsp.filter.channelizer;
|
||||
|
||||
import io.github.dsheirer.dsp.fft.MixedRadixComplexFFT;
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
@@ -27,7 +28,6 @@ import java.util.Arrays;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.jtransforms.fft.FloatFFT_1D;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -85,7 +85,8 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
|
||||
|
||||
//Sized to process 40 times per second
|
||||
private IFFTProcessorDispatcher mIFFTProcessorDispatcher = new IFFTProcessorDispatcher(25);
|
||||
private FloatFFT_1D mFFT;
|
||||
//Allocation-free IFFT. Only used by the IFFT processor dispatcher thread since it reuses a scratch buffer.
|
||||
private MixedRadixComplexFFT mFFT;
|
||||
private ChannelDFT mChannelDFT;
|
||||
private float[] mInlineSamples;
|
||||
private float[] mInlineFilter;
|
||||
@@ -453,7 +454,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
|
||||
private void init(float[] coefficients)
|
||||
{
|
||||
int channelCount = getChannelCount();
|
||||
mFFT = new FloatFFT_1D(channelCount);
|
||||
mFFT = new MixedRadixComplexFFT(channelCount);
|
||||
mChannelDFT = ChannelDFTFactory.getChannelDFT(channelCount);
|
||||
int bufferLength = getSubChannelCount() * mTapsPerChannel;
|
||||
mSamplesPerBlock = channelCount; //Same as subChannelCount / 2
|
||||
@@ -505,7 +506,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
|
||||
for(int offset = 0; offset < samples.length; offset += blockLength)
|
||||
{
|
||||
//Rotate each of the channels to the correct phase using the IFFT
|
||||
mFFT.complexInverse(samples, offset, true);
|
||||
mFFT.inverse(samples, offset, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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."));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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."));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user