mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
Compare commits
77 Commits
v0.5.1
...
v0.6.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c96349fa1c | ||
|
|
f640576cc3 | ||
|
|
77d725a488 | ||
|
|
2c0305f928 | ||
|
|
21fd61d507 | ||
|
|
8fb56a5342 | ||
|
|
5d7b73f876 | ||
|
|
85b2a38058 | ||
|
|
c78517ee75 | ||
|
|
ae116d3dfe | ||
|
|
93252f9a40 | ||
|
|
3c03a4db2e | ||
|
|
d0fcd617aa | ||
|
|
6ca0decbd6 | ||
|
|
ddd9ca933e | ||
|
|
b2cc29569e | ||
|
|
411735772b | ||
|
|
2ffb2963af | ||
|
|
227c88c8a2 | ||
|
|
cbe9c79498 | ||
|
|
1b8c100b43 | ||
|
|
30c5fd64f2 | ||
|
|
3a6b486340 | ||
|
|
ded7335dbf | ||
|
|
005a4f8119 | ||
|
|
1ac49bfa7f | ||
|
|
c302c4d121 | ||
|
|
b0a524cf62 | ||
|
|
d246ffff30 | ||
|
|
adfc8b2d6f | ||
|
|
ad2ab08466 | ||
|
|
f3369410b2 | ||
|
|
bac38a5bd8 | ||
|
|
e5f5df74ad | ||
|
|
b3837f581a | ||
|
|
b2ab6f58e9 | ||
|
|
e04b59ba15 | ||
|
|
366eacd234 | ||
|
|
f792876c0a | ||
|
|
19f7a5171b | ||
|
|
5b6377cc11 | ||
|
|
9502ca3069 | ||
|
|
77629000b2 | ||
|
|
ca7916f992 | ||
|
|
9de83843a3 | ||
|
|
cbab7bb0c9 | ||
|
|
3df8d60eed | ||
|
|
b39dd1a19f | ||
|
|
0af5963d08 | ||
|
|
4f8b8e978b | ||
|
|
5654254cbb | ||
|
|
11f37e896a | ||
|
|
e8f563deeb | ||
|
|
f877354a11 | ||
|
|
df3d3b5ddc | ||
|
|
59e74d70b4 | ||
|
|
8d6b7c3ac8 | ||
|
|
372d2ff870 | ||
|
|
32a8363b45 | ||
|
|
87f1f8986f | ||
|
|
b3c08053fb | ||
|
|
a5208dcf1e | ||
|
|
2782953518 | ||
|
|
366055145e | ||
|
|
39ebdb47b1 | ||
|
|
c788292db8 | ||
|
|
f0dc80437c | ||
|
|
2d6c5c2331 | ||
|
|
bc4e6cb5b0 | ||
|
|
d69ad46c10 | ||
|
|
ba7b616f88 | ||
|
|
8355b7e3b9 | ||
|
|
22ffb656be | ||
|
|
de80d2a6c1 | ||
|
|
a320b4a24f | ||
|
|
fc13777211 | ||
|
|
75347f9d0a |
38
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve sdrtrunk
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**sdrtrunk Version**
|
||||
Indicate which sdrtrunk version you are using. Use 'master-branch' if you are running the latest code.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Application Log**
|
||||
If applicable, include either the full application log,or a snippet of the error logging.
|
||||
|
||||
**Desktop (optional - complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- CPU Cores: [e.g. 4 cores]
|
||||
- RAM:[e.g. 16GB]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
16
.github/ISSUE_TEMPLATE/how-to-question-or-does-the-software-support-xxx.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/how-to-question-or-does-the-software-support-xxx.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: How-To Question or Does The Software Support xxx
|
||||
about: End user question or support discussion
|
||||
title: STOP - Don't Use Issue Reporting to Post End-User Questions!!!
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
STOP !!!!
|
||||
|
||||
GitHub issues are for reporting software bugs or requesting enhancements.
|
||||
|
||||
If you have a question about how to use the software, or if you want to know if sdrtrunk supports a protocol, tuner, or other aspect, please post your question in one of the Support forums.
|
||||
|
||||
Visit: https://github.com/DSheirer/sdrtrunk/wiki
|
||||
8
.github/workflows/gradle.yml
vendored
8
.github/workflows/gradle.yml
vendored
@@ -22,13 +22,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
- name: Set up JDK 20
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
java-version: '20'
|
||||
distribution: 'liberica'
|
||||
java-package: 'jdk+fx'
|
||||
- name: Build with Gradle
|
||||
uses: gradle/gradle-build-action@937999e9cc2425eddc7fd62d1053baf041147db7
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: clean build
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -4,7 +4,7 @@
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="liberica-19.0.1.fx" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_20" default="true" project-jdk-name="liberica-20" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
36
artifacts/sdrplay-api-headers/README.txt
Normal file
36
artifacts/sdrplay-api-headers/README.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
Auto-generating Java code from SDRPlay API using jextract.
|
||||
|
||||
Note: the api headers in the 3.11 folder are equivalent to the 3.08 headers. The only
|
||||
difference from the 3.07 headers is the addition of the boolean 'valid' flag to DeviceT structure.
|
||||
|
||||
1. Download JDK19 compatible build of jextract JDK: https://jdk.java.net/jextract/
|
||||
2. If generating on windows platform, modify the sdrplay_api.h to remove the windows
|
||||
pieces of the file. Specifically:
|
||||
2a. Comment out these lines but leave the *HANDLE typedef:
|
||||
|
||||
//#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
|
||||
|
||||
2b. Remove the '_SDRPLAY_DLL_QUALIFIER' from in front of the method declarations
|
||||
|
||||
2c. Change the 3.11 version string to 3.08 either in the header file, or in the generated java class
|
||||
|
||||
3. Open command prompt to the root source code where the generated files should be placed:
|
||||
..\SDRtrunk\sdrplay-api\src\main\java
|
||||
|
||||
4. Generate the code:
|
||||
c:\Users\Denny\Downloads\jdk-19-ea2-panama\bin\jextract "C:\Users\Denny\git\sdrtrunk\sdrplay-api\api\v3_11\sdrplay_api.h" -l libsdrplay_api --source
|
||||
|
||||
5. Do this for all versions of the API.
|
||||
|
||||
229
artifacts/sdrplay-api-headers/v3_07/sdrplay_api.h
Normal file
229
artifacts/sdrplay-api-headers/v3_07/sdrplay_api.h
Normal file
@@ -0,0 +1,229 @@
|
||||
#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
|
||||
78
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_callback.h
Normal file
78
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_callback.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#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
|
||||
55
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_control.h
Normal file
55
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_control.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#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
|
||||
51
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_dev.h
Normal file
51
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_dev.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#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
|
||||
22
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rsp1a.h
Normal file
22
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rsp1a.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#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
|
||||
35
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rsp2.h
Normal file
35
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rsp2.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#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
|
||||
40
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rspDuo.h
Normal file
40
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rspDuo.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#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
|
||||
45
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rspDx.h
Normal file
45
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rspDx.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#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
|
||||
21
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rx_channel.h
Normal file
21
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_rx_channel.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#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
|
||||
93
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_tuner.h
Normal file
93
artifacts/sdrplay-api-headers/v3_07/sdrplay_api_tuner.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#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
|
||||
230
artifacts/sdrplay-api-headers/v3_08/sdrplay_api.h
Normal file
230
artifacts/sdrplay-api-headers/v3_08/sdrplay_api.h
Normal file
@@ -0,0 +1,230 @@
|
||||
#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
|
||||
78
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_callback.h
Normal file
78
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_callback.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#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
|
||||
55
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_control.h
Normal file
55
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_control.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#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
|
||||
51
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_dev.h
Normal file
51
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_dev.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#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
|
||||
22
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rsp1a.h
Normal file
22
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rsp1a.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#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
|
||||
35
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rsp2.h
Normal file
35
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rsp2.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#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
|
||||
40
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rspDuo.h
Normal file
40
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rspDuo.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#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
|
||||
45
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rspDx.h
Normal file
45
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rspDx.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#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
|
||||
21
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rx_channel.h
Normal file
21
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_rx_channel.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#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
|
||||
93
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_tuner.h
Normal file
93
artifacts/sdrplay-api-headers/v3_08/sdrplay_api_tuner.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#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
|
||||
230
artifacts/sdrplay-api-headers/v3_11/sdrplay_api.h
Normal file
230
artifacts/sdrplay-api-headers/v3_11/sdrplay_api.h
Normal file
@@ -0,0 +1,230 @@
|
||||
#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.11)
|
||||
|
||||
// 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_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, double newSampleRate);
|
||||
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
|
||||
79
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_callback.h
Normal file
79
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_callback.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#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
|
||||
55
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_control.h
Normal file
55
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_control.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#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
|
||||
51
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_dev.h
Normal file
51
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_dev.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#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
|
||||
22
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rsp1a.h
Normal file
22
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rsp1a.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#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
|
||||
35
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rsp2.h
Normal file
35
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rsp2.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#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
|
||||
40
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rspDuo.h
Normal file
40
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rspDuo.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#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
|
||||
46
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rspDx.h
Normal file
46
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rspDx.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#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
|
||||
21
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rx_channel.h
Normal file
21
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_rx_channel.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#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
|
||||
93
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_tuner.h
Normal file
93
artifacts/sdrplay-api-headers/v3_11/sdrplay_api_tuner.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#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
|
||||
286
build.gradle
286
build.gradle
@@ -1,6 +1,8 @@
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -16,6 +18,29 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Instructions for building/compiling the sdrtrunk application.
|
||||
*
|
||||
* Prerequisites:
|
||||
* Install and configure an OpenJDK version 19+ that includes the JavaFX modules (e.g. Bellsoft Liberica JDK)
|
||||
* - Optional: install and configure Gradle 7.6+
|
||||
*
|
||||
* Scenario 1: run the application via gradle command line from the source code root directory:
|
||||
* command: ./gradlew run
|
||||
*
|
||||
* Scenario 2: create an application release for the current operating system
|
||||
* command: ./gradlew runtimeZipCurrent
|
||||
* Note: release image is located in the /build/image/ directory
|
||||
*
|
||||
* Scenario 3: create releases for Linux and OSX operating systems using downloaded JDKs
|
||||
* command: ./gradlew runtimeZipOthers
|
||||
* Note: release images are located in the /build/image/ directory
|
||||
*
|
||||
* Scenario 4: create release for Windows operating system using downloaded JDK
|
||||
* command: ./gradlew runtimeZipWindows
|
||||
* Note: release image is located in the /build/image/ directory
|
||||
*/
|
||||
plugins {
|
||||
id 'application'
|
||||
id 'java'
|
||||
@@ -28,8 +53,14 @@ repositories {
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
|
||||
version = '0.5.1'
|
||||
sourceCompatibility = '17'
|
||||
version = '0.6.0-beta2'
|
||||
|
||||
//Java 20 is required for this version of the Project Panama preview/incubator feature
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(20)
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs 'src/main'
|
||||
@@ -90,19 +121,46 @@ dependencies {
|
||||
implementation 'pl.edu.icm:JLargeArrays:1.6'
|
||||
}
|
||||
|
||||
def os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem
|
||||
|
||||
/**
|
||||
* This is needed for the JDK17 vector API ... until it moves out of incubation
|
||||
* This is needed for the JDK19 panama and vector API ... until it moves out of incubation or preview
|
||||
*/
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs.add("--enable-preview") //Panama Foreign Function/Memory is preview in JDK 19
|
||||
options.compilerArgs.add("--add-modules=jdk.incubator.vector") //Needed while Panama Vector API remains in incubator
|
||||
}
|
||||
|
||||
/**
|
||||
* JVM Arguments for running the application on Windows.
|
||||
*/
|
||||
def jvmArgsWindows =
|
||||
['--add-exports=javafx.base/com.sun.javafx.event=ALL-UNNAMED', //Needed for controls-fx.jar
|
||||
'--add-exports=java.desktop/com.sun.java.swing.plaf.windows=ALL-UNNAMED', //windows users for JIDE
|
||||
'--add-modules=jdk.incubator.vector', //Needed while Project Panama API is still in incubator
|
||||
'--enable-preview', //Project Panama Vector & Foreign Function APIs remain in incubator
|
||||
'--enable-native-access=ALL-UNNAMED',
|
||||
'-Djava.library.path=c:\\Program Files\\SDRplay\\API\\x64'] //Allows Project Panama Foreign Function access to system library
|
||||
|
||||
/**
|
||||
* JVM Arguments for running the application on Linux.
|
||||
*/
|
||||
def jvmArgsLinux =
|
||||
['--add-exports=javafx.base/com.sun.javafx.event=ALL-UNNAMED', //Needed for controls-fx.jar
|
||||
'--add-modules=jdk.incubator.vector', //Needed while Project Panama API is still in incubator
|
||||
'--enable-preview', //Project Panama Vector & Foreign Function APIs remain in incubator
|
||||
'--enable-native-access=ALL-UNNAMED',
|
||||
'-Djava.library.path=/usr/local/lib'] //Allows Project Panama Foreign Function access to system library
|
||||
|
||||
application {
|
||||
mainClassName = "io.github.dsheirer.gui.SDRTrunk"
|
||||
applicationDefaultJvmArgs =
|
||||
['--add-exports=javafx.base/com.sun.javafx.event=ALL-UNNAMED', //Needed for controls-fx.jar
|
||||
'--add-modules=jdk.incubator.vector', //Needed while Panama Vector API remains in incubator
|
||||
'--add-exports=java.desktop/com.sun.java.swing.plaf.windows=ALL-UNNAMED'] //Windows Swing - jide-oss library
|
||||
|
||||
if(os.isWindows()) {
|
||||
applicationDefaultJvmArgs = jvmArgsWindows
|
||||
}
|
||||
else {
|
||||
applicationDefaultJvmArgs = jvmArgsLinux
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
@@ -111,7 +169,7 @@ jar {
|
||||
'Implementation-Title' : 'sdrtrunk project',
|
||||
'Implementation-Version' : archiveVersion,
|
||||
'Created-By' : "Gradle ${gradle.gradleVersion}",
|
||||
'Build-Timestamp' : new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
|
||||
'Build-Timestamp' : new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
|
||||
'Build-JDK' : "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']}",
|
||||
'Build-OS' : "${System.properties['os.name']} (${System.properties['os.arch']} ${System.properties['os.version']}"
|
||||
)
|
||||
@@ -139,90 +197,158 @@ String targetWindowsX86_64 = 'windows-x86_64-v' + version
|
||||
* author's development environment, but can be changed to your environment if you want to use this build pattern.
|
||||
*/
|
||||
String jdk_base = '/home/denny/java_jdks/'
|
||||
String jdk_linux_aarch64 = jdk_base + 'linux-arm64/jdk-19.0.1-full'
|
||||
String jdk_linux_x86_64 = jdk_base + 'linux-x64/jdk-19.0.1-full'
|
||||
String jdk_osx_x86_64 = jdk_base + 'osx-x64/jdk-19.0.1-full.jdk'
|
||||
String jdk_osx_aarch64 = jdk_base + 'osx-arm64/jdk-19.0.1-full.jdk'
|
||||
String jdk_windows_x86_64 = jdk_base + 'windows-x64/jdk-19.0.1-full'
|
||||
|
||||
boolean hasTargetJdks = file(jdk_linux_x86_64).exists() || file(jdk_linux_aarch64).exists() ||
|
||||
file(jdk_osx_x86_64).exists() || file(jdk_osx_aarch64).exists() || file(jdk_windows_x86_64).exists()
|
||||
String jdk_linux_aarch64 = jdk_base + 'linux-arm64/jdk-20.0.1-full'
|
||||
String jdk_linux_x86_64 = jdk_base + 'linux-x64/jdk-20.0.1-full'
|
||||
String jdk_osx_x86_64 = jdk_base + 'osx-x64/jdk-20.0.1-full.jdk'
|
||||
String jdk_osx_aarch64 = jdk_base + 'osx-arm64/jdk-20.0.1-full.jdk'
|
||||
String jdk_windows_x86_64 = jdk_base + 'windows-x64/jdk-20.0.1-full'
|
||||
|
||||
/**
|
||||
* Auto-download target JDKs. Defaults to false, or the user can override via the gradle command line by
|
||||
* including this option: '-PdownloadJdks=true'
|
||||
* Download URLs to download the JDK as part of the gradle build packaging process
|
||||
*/
|
||||
boolean downloadJdks = project.hasProperty('downloadJdks') ? project.getProperty('downloadJdks') : false
|
||||
def jdk_download_base = "https://download.bell-sw.com/java/20.0.1+10/bellsoft-jdk20.0.1+10-"
|
||||
def jdk_download_suffix = "-full.tar.gz"
|
||||
def jdk_download_linux_aarch64 = jdk_download_base + "linux-aarch64" + jdk_download_suffix
|
||||
def jdk_download_linux_x86_64 = jdk_download_base + "linux-amd64" + jdk_download_suffix
|
||||
def jdk_download_osx_x86_64 = jdk_download_base + "macos-aarch64" + jdk_download_suffix
|
||||
def jdk_download_osx_aarch64 = jdk_download_base + "macos-amd64" + jdk_download_suffix
|
||||
def jdk_download_windows_x86_64 = jdk_download_base + "windows-amd64-full.zip"
|
||||
|
||||
/**
|
||||
* Beryx Runtime plugin for creating OS and CPU targeted build(s) of the software.
|
||||
*
|
||||
* Option 1: Create a single target build using the locally installed JDK, or optionally create target builds for
|
||||
* all OS and CPU targets when pre-downloaded target JDKs are available (see 'jdk_*' properties above).
|
||||
*
|
||||
* >: gradle runtimeZip
|
||||
*
|
||||
* Option 2: Create all OS and CPU target builds by automatically downloading all JDKs needed to build the target images
|
||||
*
|
||||
* >: gradle runtimeZip -PdownloadJdks=true
|
||||
* Configures the runtime zip task with additional options/settings.
|
||||
* @param rt task to configure
|
||||
* @param jvmArgs to use (windows or linux)
|
||||
* @return configured task.
|
||||
*/
|
||||
runtime {
|
||||
//Build by automatically downloading JDKs for the OS and CPU targets
|
||||
if(downloadJdks) {
|
||||
println("Building all runtime images")
|
||||
println("The [ > jre: ] build phase downloads several target JDKs and this may take a while ... please wait")
|
||||
targetPlatform(targetLinuxAarch64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-linux-aarch64-full.tar.gz")
|
||||
}
|
||||
targetPlatform(targetLinuxX86_64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-linux-amd64-full.tar.gz")
|
||||
}
|
||||
targetPlatform(targetOsxAarch64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-macos-aarch64-full.tar.gz")
|
||||
}
|
||||
targetPlatform(targetOsxX86_64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-macos-amd64-full.tar.gz")
|
||||
}
|
||||
targetPlatform(targetWindowsX86_64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-windows-amd64-full.zip")
|
||||
}
|
||||
}
|
||||
//Build using pre-downloaded JDKs
|
||||
else if(hasTargetJdks) {
|
||||
println("Building all runtime images using pre-downloaded JDKs")
|
||||
if(file(jdk_linux_aarch64).exists()) {
|
||||
targetPlatform(targetLinuxAarch64, jdk_linux_aarch64)
|
||||
}
|
||||
if(file(jdk_linux_x86_64).exists()) {
|
||||
targetPlatform(targetLinuxX86_64, jdk_linux_x86_64)
|
||||
}
|
||||
if(file(jdk_osx_aarch64).exists()) {
|
||||
targetPlatform(targetOsxAarch64, jdk_osx_aarch64)
|
||||
}
|
||||
if(file(jdk_osx_x86_64).exists()) {
|
||||
targetPlatform(targetOsxX86_64, jdk_osx_x86_64)
|
||||
}
|
||||
if(file(jdk_windows_x86_64).exists()) {
|
||||
targetPlatform(targetWindowsX86_64, jdk_windows_x86_64)
|
||||
}
|
||||
}
|
||||
//Build using locally installed JDK only
|
||||
else {
|
||||
println("Building a single runtime image using locally installed JDK")
|
||||
}
|
||||
def configure(org.beryx.runtime.RuntimeZipTask rt, List<String> jvmArgs) {
|
||||
//Use the correct set of JVM arguments
|
||||
rt.extension.launcherData.get().jvmArgs = jvmArgs
|
||||
|
||||
//Additional modules to include with auto-detected modules.
|
||||
//jdk.crypto.ec needed for HTTPS connections (broadcastify calls & map tile server)
|
||||
//jdk.crypto.ec - needed for HTTPS connections (broadcastify calls & map tile server)
|
||||
//jdk.incubator.vector - needed for Project Panama foreign function and vector apis
|
||||
//jdk.accessibility is used with assistive technologies like screen readers
|
||||
modules = ['jdk.crypto.ec','jdk.accessibility']
|
||||
rt.extension.addModules('jdk.crypto.ec', 'jdk.incubator.vector', 'jdk.accessibility')
|
||||
|
||||
//Use auto-detected modules and 'add' any specified modules.
|
||||
additive = true
|
||||
rt.extension.additive.set(true)
|
||||
|
||||
//Recommended options for reducing/shrinking runtime target image size
|
||||
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
|
||||
rt.extension.addOptions('--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages')
|
||||
|
||||
//Runtime images have auto appended version in image zip name. Override to include version in single target image.
|
||||
imageZip = (hasTargetJdks | downloadJdks) ? file("$buildDir/image/sdr-trunk.zip") :
|
||||
file("$buildDir/image/sdr-trunk-" + version + ".zip")
|
||||
rt.extension.imageZip.set(file("$buildDir/image/sdr-trunk.zip"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a release package for the current operating system.
|
||||
*
|
||||
* Note: you cannot create both Windows and Linux releases at the same time due to limitations of the Runtime plugin
|
||||
*
|
||||
* Usage: gradle runtimeZipCurrent
|
||||
* Usage: gradlew runtimeZipCurrent (windows - using gradlew)
|
||||
* Usage: ./gradlew runtimeZipCurrent (linux - using gradlew)
|
||||
*/
|
||||
tasks.register('runtimeZipCurrent', org.beryx.runtime.RuntimeZipTask) {rt ->
|
||||
def arch = System.properties.getProperty("os.arch")
|
||||
def javaHome = org.gradle.internal.jvm.Jvm.current().getJavaHome().toString()
|
||||
|
||||
if(os.isWindows()) {
|
||||
rt.extension.targetPlatform(targetWindowsX86_64, javaHome)
|
||||
configure(rt, jvmArgsWindows)
|
||||
}
|
||||
else if(os.isLinux())
|
||||
{
|
||||
if(arch.equals("amd64")) {
|
||||
rt.extension.targetPlatform(targetLinuxX86_64, javaHome)
|
||||
}
|
||||
else if(arch.equals("aarch64")) {
|
||||
rt.extension.targetPlatform(targetLinuxAarch64, javaHome)
|
||||
}
|
||||
configure(rt, jvmArgsLinux)
|
||||
}
|
||||
else if(os.isMacOsX())
|
||||
{
|
||||
if(arch.equals("amd64")) {
|
||||
rt.extension.targetPlatform(targetOsxX86_64, javaHome)
|
||||
}
|
||||
else if(arch.equals("aarch64")) {
|
||||
rt.extension.targetPlatform(targetOsxAarch64, javaHome)
|
||||
}
|
||||
configure(rt, jvmArgsLinux)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Windows release package.
|
||||
*
|
||||
* Note: you cannot create both Windows and Linux releases at the same time due to limitations of the Runtime plugin
|
||||
*
|
||||
* Usage: gradle runtimeZipWindows
|
||||
* Usage: gradlew runtimeZipWindows (windows - using gradlew)
|
||||
* Usage: ./gradlew runtimeZipWindows (linux - using gradlew)
|
||||
*/
|
||||
tasks.register('runtimeZipWindows', org.beryx.runtime.RuntimeZipTask) {rt ->
|
||||
if(file(jdk_windows_x86_64).exists()) {
|
||||
rt.extension.targetPlatform(targetWindowsX86_64, jdk_windows_x86_64, [])
|
||||
}
|
||||
else {
|
||||
rt.extension.targetPlatform(targetWindowsX86_64) {
|
||||
jdkHome = jdkDownload(jdk_download_windows_x86_64)
|
||||
}
|
||||
}
|
||||
|
||||
configure(rt, jvmArgsWindows)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Linux & OSX release packages.
|
||||
*
|
||||
* Note: you cannot create both Windows and Linux releases at the same time due to limitations of the Runtime plugin
|
||||
*
|
||||
* Usage: gradle runtimeZipOthers
|
||||
* Usage: gradlew runtimeZipOthers (windows - using gradlew)
|
||||
* Usage: ./gradlew runtimeZipOthers (linux - using gradlew)
|
||||
*/
|
||||
tasks.register('runtimeZipOthers', org.beryx.runtime.RuntimeZipTask) {rt ->
|
||||
//Linux aarch64
|
||||
if(file(jdk_linux_aarch64).exists()) {
|
||||
rt.extension.targetPlatform(targetLinuxAarch64, jdk_linux_aarch64, [])
|
||||
}
|
||||
else {
|
||||
rt.extension.targetPlatform(targetLinuxAarch64) {
|
||||
jdkHome = jdkDownload(jdk_download_linux_aarch64)
|
||||
}
|
||||
}
|
||||
//Linux x86-64
|
||||
if(file(jdk_linux_x86_64).exists()) {
|
||||
rt.extension.targetPlatform(targetLinuxX86_64, jdk_linux_x86_64, [])
|
||||
}
|
||||
else {
|
||||
rt.extension.targetPlatform(targetLinuxX86_64) {
|
||||
jdkHome = jdkDownload(jdk_download_linux_x86_64)
|
||||
}
|
||||
}
|
||||
//OSX aarch64
|
||||
if(file(jdk_osx_aarch64).exists()) {
|
||||
rt.extension.targetPlatform(targetOsxAarch64, jdk_osx_aarch64, [])
|
||||
}
|
||||
else {
|
||||
rt.extension.targetPlatform(targetOsxAarch64) {
|
||||
jdkHome = jdkDownload(jdk_download_osx_aarch64)
|
||||
}
|
||||
}
|
||||
//Linux x86-64
|
||||
if(file(jdk_osx_x86_64).exists()) {
|
||||
rt.extension.targetPlatform(targetOsxX86_64, jdk_osx_x86_64, [])
|
||||
}
|
||||
else {
|
||||
rt.extension.targetPlatform(targetOsxX86_64) {
|
||||
jdkHome = jdkDownload(jdk_download_osx_x86_64)
|
||||
}
|
||||
}
|
||||
|
||||
configure(rt, jvmArgsLinux);
|
||||
}
|
||||
|
||||
|
||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# ******************************************************************************
|
||||
# Copyright (C) 2014-2022 Dennis Sheirer
|
||||
# Copyright (C) 2014-2023 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
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -19,22 +19,15 @@
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import io.github.dsheirer.alias.action.AliasAction;
|
||||
import io.github.dsheirer.alias.action.AliasActionType;
|
||||
import io.github.dsheirer.alias.action.beep.BeepAction;
|
||||
import io.github.dsheirer.alias.action.clip.ClipAction;
|
||||
import io.github.dsheirer.alias.action.script.ScriptAction;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.dcs.Dcs;
|
||||
import io.github.dsheirer.alias.id.esn.Esn;
|
||||
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID;
|
||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID;
|
||||
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable;
|
||||
import io.github.dsheirer.alias.id.legacy.siteID.SiteID;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.radio.Radio;
|
||||
@@ -45,11 +38,10 @@ import io.github.dsheirer.alias.id.status.UserStatusID;
|
||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||
import io.github.dsheirer.alias.id.tone.TonesID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AliasFactory
|
||||
{
|
||||
@@ -64,6 +56,11 @@ public class AliasFactory
|
||||
BroadcastChannel copyBroadcast = new BroadcastChannel();
|
||||
copyBroadcast.setChannelName(originalBroadcast.getChannelName());
|
||||
return copyBroadcast;
|
||||
case DCS:
|
||||
Dcs originalDcs = (Dcs)id;
|
||||
Dcs copyDcs = new Dcs();
|
||||
copyDcs.setDCSCode(originalDcs.getDCSCode());
|
||||
return copyDcs;
|
||||
case ESN:
|
||||
Esn originalESN = (Esn)id;
|
||||
Esn copyESN = new Esn();
|
||||
@@ -234,65 +231,4 @@ public class AliasFactory
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static AliasID getAliasID(AliasIDType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BROADCAST_CHANNEL:
|
||||
return new BroadcastChannel();
|
||||
case ESN:
|
||||
return new Esn();
|
||||
case FLEETSYNC:
|
||||
return new FleetsyncID();
|
||||
case LTR_NET_UID:
|
||||
return new UniqueID();
|
||||
case LOJACK:
|
||||
return new LoJackFunctionAndID();
|
||||
case MDC1200:
|
||||
return new MDC1200ID();
|
||||
case MIN:
|
||||
return new Min();
|
||||
case MPT1327:
|
||||
return new MPT1327ID();
|
||||
case NON_RECORDABLE:
|
||||
return new NonRecordable();
|
||||
case PRIORITY:
|
||||
return new Priority();
|
||||
case RADIO_ID:
|
||||
return new Radio();
|
||||
case RADIO_ID_RANGE:
|
||||
return new RadioRange();
|
||||
case RECORD:
|
||||
return new Record();
|
||||
case TALKGROUP:
|
||||
return new Talkgroup();
|
||||
case TALKGROUP_RANGE:
|
||||
return new TalkgroupRange();
|
||||
case SITE:
|
||||
return new SiteID();
|
||||
case STATUS:
|
||||
return new UserStatusID();
|
||||
case LEGACY_TALKGROUP:
|
||||
return new LegacyTalkgroupID();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized Alias ID type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public static AliasAction getAliasAction(AliasActionType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BEEP:
|
||||
return new BeepAction();
|
||||
case CLIP:
|
||||
return new ClipAction();
|
||||
case SCRIPT:
|
||||
return new ScriptAction();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized Alias Action type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -21,6 +21,7 @@ package io.github.dsheirer.alias;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.dcs.Dcs;
|
||||
import io.github.dsheirer.alias.id.esn.Esn;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.radio.Radio;
|
||||
@@ -32,6 +33,7 @@ import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||
import io.github.dsheirer.alias.id.tone.TonesID;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.dcs.DCSIdentifier;
|
||||
import io.github.dsheirer.identifier.esn.ESNIdentifier;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroup;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
@@ -41,12 +43,8 @@ import io.github.dsheirer.identifier.status.UserStatusIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.identifier.tone.ToneIdentifier;
|
||||
import io.github.dsheirer.identifier.tone.ToneSequence;
|
||||
import io.github.dsheirer.module.decode.dcs.DCSCode;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -57,6 +55,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* List of aliases that share the same alias list name and provides convenient methods for looking up alias
|
||||
@@ -67,6 +69,7 @@ public class AliasList
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AliasList.class);
|
||||
private Map<Protocol,TalkgroupAliasList> mTalkgroupProtocolMap = new EnumMap<>(Protocol.class);
|
||||
private Map<Protocol,RadioAliasList> mRadioProtocolMap = new EnumMap<>(Protocol.class);
|
||||
private Map<DCSCode,Alias> mDCSCodeAliasMap = new EnumMap<>(DCSCode.class);
|
||||
private Map<String,Alias> mESNMap = new HashMap<>();
|
||||
private Map<Integer,Alias> mUnitStatusMap = new HashMap<>();
|
||||
private Map<Integer,Alias> mUserStatusMap = new HashMap<>();
|
||||
@@ -178,6 +181,12 @@ public class AliasList
|
||||
|
||||
radioRangeAliasList.add(radioRange, alias);
|
||||
break;
|
||||
case DCS:
|
||||
if(id instanceof Dcs dcs)
|
||||
{
|
||||
mDCSCodeAliasMap.put(dcs.getDCSCode(), alias);
|
||||
}
|
||||
break;
|
||||
case ESN:
|
||||
String esn = ((Esn)id).getEsn();
|
||||
|
||||
@@ -418,7 +427,7 @@ public class AliasList
|
||||
return toList(radioAliasList.getAlias(radio));
|
||||
}
|
||||
break;
|
||||
case ESN:
|
||||
case DCS:
|
||||
if(identifier instanceof ESNIdentifier)
|
||||
{
|
||||
return toList(getESNAlias(((ESNIdentifier)identifier).getValue()));
|
||||
@@ -439,9 +448,9 @@ public class AliasList
|
||||
}
|
||||
break;
|
||||
case TONE:
|
||||
if(identifier instanceof ToneIdentifier)
|
||||
if(identifier instanceof ToneIdentifier toneIdentifier)
|
||||
{
|
||||
ToneSequence toneSequence = ((ToneIdentifier)identifier).getValue();
|
||||
ToneSequence toneSequence = toneIdentifier.getValue();
|
||||
|
||||
if(toneSequence != null && toneSequence.hasTones())
|
||||
{
|
||||
@@ -454,6 +463,15 @@ public class AliasList
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(identifier instanceof DCSIdentifier dcsIdentifier)
|
||||
{
|
||||
DCSCode dcsCode = dcsIdentifier.getValue();
|
||||
|
||||
if(dcsCode != null)
|
||||
{
|
||||
return toList(mDCSCodeAliasMap.get(dcsCode));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -23,17 +23,16 @@ import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Alias Model contains all aliases and is responsible for creation and management of alias lists. Alias lists are a
|
||||
@@ -184,10 +183,8 @@ public class AliasModel
|
||||
*/
|
||||
public void addAliases(List<Alias> aliases)
|
||||
{
|
||||
for(Alias alias : aliases)
|
||||
{
|
||||
addAlias(alias);
|
||||
}
|
||||
removeAliases(aliases);
|
||||
mAliases.addAll(aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,6 +238,26 @@ public class AliasModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the list of aliases from this model and any alias lists that might contain each alias.
|
||||
* @param aliases
|
||||
*/
|
||||
public void removeAliases(List<Alias> aliases)
|
||||
{
|
||||
if(aliases != null && !aliases.isEmpty())
|
||||
{
|
||||
mAliases.removeAll(aliases);
|
||||
|
||||
for(Alias alias: aliases)
|
||||
{
|
||||
if(hasAliasList(alias.getAliasListName()))
|
||||
{
|
||||
getAliasList(alias.getAliasListName()).removeAlias(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all aliases that match the alias list and have at least one alias ID of the specified type
|
||||
* @param aliasListName to search
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.alias.id;
|
||||
|
||||
@@ -26,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.dcs.Dcs;
|
||||
import io.github.dsheirer.alias.id.esn.Esn;
|
||||
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID;
|
||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID;
|
||||
@@ -54,6 +52,7 @@ import javafx.util.Callback;
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = BroadcastChannel.class, name = "broadcastChannel"),
|
||||
@JsonSubTypes.Type(value = Dcs.class, name = "dcs"),
|
||||
@JsonSubTypes.Type(value = Esn.class, name = "esn"),
|
||||
@JsonSubTypes.Type(value = FleetsyncID.class, name = "fleetsyncID"),
|
||||
@JsonSubTypes.Type(value = LegacyTalkgroupID.class, name = "talkgroupID"),
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.alias.id;
|
||||
|
||||
@@ -26,6 +23,7 @@ import java.util.EnumSet;
|
||||
public enum AliasIDType
|
||||
{
|
||||
BROADCAST_CHANNEL("Audio Broadcast Channel"),
|
||||
DCS("Digital Coded Squelch (DCS)"),
|
||||
ESN("ESN"),
|
||||
INVERT("Audio Inversion"),
|
||||
LOJACK("LoJack"),
|
||||
|
||||
111
src/main/java/io/github/dsheirer/alias/id/dcs/Dcs.java
Normal file
111
src/main/java/io/github/dsheirer/alias/id/dcs/Dcs.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.alias.id.dcs;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.module.decode.dcs.DCSCode;
|
||||
|
||||
/**
|
||||
* Digital Coded Squelch (DCS) tone identifier
|
||||
*/
|
||||
public class Dcs extends AliasID implements Comparable<Dcs>
|
||||
{
|
||||
private DCSCode mDCSCode;
|
||||
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.DCS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
if(isValid() && id instanceof Dcs other)
|
||||
{
|
||||
return other.isValid() && getDCSCode().equals(other.getDCSCode());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* DCS code
|
||||
* @return DCS code or null.
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "code")
|
||||
public DCSCode getDCSCode()
|
||||
{
|
||||
return mDCSCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DCS code value.
|
||||
* @param dcsCode to set
|
||||
*/
|
||||
public void setDCSCode(DCSCode dcsCode)
|
||||
{
|
||||
mDCSCode = dcsCode;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return mDCSCode != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if(isValid())
|
||||
{
|
||||
return getDCSCode().toString();
|
||||
}
|
||||
|
||||
return "DCS-Invalid - No Tone Selected";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Dcs o)
|
||||
{
|
||||
if(isValid())
|
||||
{
|
||||
if(o.isValid())
|
||||
{
|
||||
return getDCSCode().compareTo(o.getDCSCode());
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ import io.github.dsheirer.protocol.Protocol;
|
||||
*/
|
||||
public enum TalkgroupFormat
|
||||
{
|
||||
AM("*****", 1, 0xFFFF, "1 to 65,535",
|
||||
"AM valid value range is 1-65,535"),
|
||||
APCO25("********", 0, 0xFFFF, "0 to 65,535",
|
||||
"<html>APCO25 talkgroup valid range is 0 to 65,535"),
|
||||
DMR("********", 0, 0xFFFFFF, "0 to 16,777,215",
|
||||
@@ -122,6 +124,8 @@ public enum TalkgroupFormat
|
||||
|
||||
switch(protocol)
|
||||
{
|
||||
case AM:
|
||||
return AM;
|
||||
case APCO25:
|
||||
return APCO25;
|
||||
case FLEETSYNC:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.id.talkgroup;
|
||||
@@ -23,21 +22,20 @@ package io.github.dsheirer.alias.id.talkgroup;
|
||||
import io.github.dsheirer.preference.identifier.IntegerFormat;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.AbstractIntegerFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.AnalogTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.DMRTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.FleetsyncTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.LTRTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.MDC1200TalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.MPT1327TalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.NBFMTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.PassportTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.UnknownTalkgroupFormatter;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Protocol-specific formatting of talkgroup values and parsing of formatted talkgroup values.
|
||||
@@ -49,6 +47,10 @@ public class TalkgroupFormatter
|
||||
|
||||
static
|
||||
{
|
||||
AnalogTalkgroupFormatter analogTalkgroupFormatter = new AnalogTalkgroupFormatter();
|
||||
mFormatterMap.put(Protocol.AM, analogTalkgroupFormatter);
|
||||
mFormatterMap.put(Protocol.NBFM, analogTalkgroupFormatter);
|
||||
|
||||
mFormatterMap.put(Protocol.APCO25, new APCO25TalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.DMR, new DMRTalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.FLEETSYNC, new FleetsyncTalkgroupFormatter());
|
||||
@@ -57,7 +59,6 @@ public class TalkgroupFormatter
|
||||
mFormatterMap.put(Protocol.LTR_NET, ltr);
|
||||
mFormatterMap.put(Protocol.MDC1200, new MDC1200TalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.MPT1327, new MPT1327TalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.NBFM, new NBFMTalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.PASSPORT, new PassportTalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.UNKNOWN, new UnknownTalkgroupFormatter());
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.audio;
|
||||
@@ -106,7 +105,7 @@ public abstract class AbstractAudioModule extends Module implements IAudioSegmen
|
||||
* Gets the current audio segment, or creates a new audio segment as necessary and broadcasts it to any registered
|
||||
* listener(s).
|
||||
*/
|
||||
protected AudioSegment getAudioSegment()
|
||||
public AudioSegment getAudioSegment()
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
@@ -135,7 +134,7 @@ public abstract class AbstractAudioModule extends Module implements IAudioSegmen
|
||||
}
|
||||
}
|
||||
|
||||
protected void addAudio(float[] audioBuffer)
|
||||
public void addAudio(float[] audioBuffer)
|
||||
{
|
||||
AudioSegment audioSegment = getAudioSegment();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -43,6 +43,7 @@ public class AudioModule extends AbstractAudioModule implements ISquelchStateLis
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
|
||||
private static float[] sHighPassFilterCoefficients;
|
||||
private final boolean mAudioFilterEnable;
|
||||
|
||||
static
|
||||
{
|
||||
@@ -70,27 +71,33 @@ public class AudioModule extends AbstractAudioModule implements ISquelchStateLis
|
||||
}
|
||||
}
|
||||
|
||||
private IRealFilter mHighPassFilter = FilterFactory.getRealFilter(sHighPassFilterCoefficients);
|
||||
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
||||
private final IRealFilter mHighPassFilter = FilterFactory.getRealFilter(sHighPassFilterCoefficients);
|
||||
private final SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
||||
private SquelchState mSquelchState = SquelchState.SQUELCH;
|
||||
|
||||
/**
|
||||
* Creates an Audio Module.
|
||||
*
|
||||
* @param aliasList for aliasing identifiers
|
||||
* @param timeslot for this audio module
|
||||
* @param maxAudioSegmentLength in milliseconds
|
||||
* @param audioFilterEnable to enable or disable high-pass audio filter
|
||||
*/
|
||||
public AudioModule(AliasList aliasList, int timeslot, long maxAudioSegmentLength)
|
||||
public AudioModule(AliasList aliasList, int timeslot, long maxAudioSegmentLength, boolean audioFilterEnable)
|
||||
{
|
||||
super(aliasList, timeslot, maxAudioSegmentLength);
|
||||
mAudioFilterEnable = audioFilterEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Audio Module.
|
||||
* @param aliasList for aliasing identifiers
|
||||
* @param audioFilterEnable to enable or disable high-pass audio filter
|
||||
*/
|
||||
public AudioModule(AliasList aliasList)
|
||||
public AudioModule(AliasList aliasList, boolean audioFilterEnable)
|
||||
{
|
||||
super(aliasList);
|
||||
mAudioFilterEnable = audioFilterEnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,7 +128,11 @@ public class AudioModule extends AbstractAudioModule implements ISquelchStateLis
|
||||
{
|
||||
if(mSquelchState == SquelchState.UNSQUELCH)
|
||||
{
|
||||
audioBuffer = mHighPassFilter.filter(audioBuffer);
|
||||
if(mAudioFilterEnable)
|
||||
{
|
||||
audioBuffer = mHighPassFilter.filter(audioBuffer);
|
||||
}
|
||||
|
||||
addAudio(audioBuffer);
|
||||
}
|
||||
}
|
||||
@@ -141,11 +152,16 @@ public class AudioModule extends AbstractAudioModule implements ISquelchStateLis
|
||||
@Override
|
||||
public void receive(SquelchStateEvent event)
|
||||
{
|
||||
mSquelchState = event.getSquelchState();
|
||||
SquelchState squelchState = event.getSquelchState();
|
||||
|
||||
if(mSquelchState == SquelchState.SQUELCH)
|
||||
if(mSquelchState != squelchState)
|
||||
{
|
||||
closeAudioSegment();
|
||||
mSquelchState = squelchState;
|
||||
|
||||
if(mSquelchState == SquelchState.SQUELCH)
|
||||
{
|
||||
closeAudioSegment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program.
|
||||
* If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
@@ -26,15 +29,6 @@ import io.github.dsheirer.identifier.IdentifierUpdateNotification;
|
||||
import io.github.dsheirer.identifier.MutableIdentifierCollection;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -43,6 +37,14 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Audio segment containing all related metadata and a dynamic collection of audio packets. An audio segment can be
|
||||
@@ -138,6 +140,15 @@ public class AudioSegment implements Listener<IdentifierUpdateNotification>
|
||||
return mComplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this audio segment is completed.
|
||||
* @return true if completed.
|
||||
*/
|
||||
public boolean isComplete()
|
||||
{
|
||||
return mComplete.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate call audio property. This flag is set to true whenever a duplicate call detection function detects
|
||||
* an audio segment is a duplicate.
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.rdioscanner.RdioScannerConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
||||
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
|
||||
@@ -46,6 +47,7 @@ import java.net.SocketAddress;
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = BroadcastifyCallConfiguration.class, name="broadcastifyCallConfiguration"),
|
||||
@JsonSubTypes.Type(value = RdioScannerConfiguration.class, name="RdioScannerConfiguration"),
|
||||
@JsonSubTypes.Type(value = IcecastConfiguration.class, name="icecastConfiguration"),
|
||||
@JsonSubTypes.Type(value = ShoutcastV1Configuration.class, name="shoutcastV1Configuration"),
|
||||
@JsonSubTypes.Type(value = ShoutcastV2Configuration.class, name="shoutcastV2Configuration"),
|
||||
|
||||
@@ -22,6 +22,9 @@ import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallBroadcaster;
|
||||
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyFeedConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.rdioscanner.RdioScannerBroadcaster;
|
||||
import io.github.dsheirer.audio.broadcast.rdioscanner.RdioScannerConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.rdioscanner.RdioScannerFeedConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPAudioBroadcaster;
|
||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPAudioBroadcaster;
|
||||
@@ -61,6 +64,9 @@ public class BroadcastFactory
|
||||
case BROADCASTIFY_CALL:
|
||||
return new BroadcastifyCallBroadcaster((BroadcastifyCallConfiguration)configuration,
|
||||
inputAudioFormat, mp3Setting, aliasModel);
|
||||
case RDIOSCANNER_CALL:
|
||||
return new RdioScannerBroadcaster((RdioScannerConfiguration)configuration,
|
||||
inputAudioFormat, mp3Setting, aliasModel);
|
||||
case BROADCASTIFY:
|
||||
return new IcecastTCPAudioBroadcaster((BroadcastifyFeedConfiguration) configuration,
|
||||
inputAudioFormat, mp3Setting, aliasModel);
|
||||
@@ -99,6 +105,8 @@ public class BroadcastFactory
|
||||
{
|
||||
case BROADCASTIFY_CALL:
|
||||
return new BroadcastifyCallConfiguration(format);
|
||||
case RDIOSCANNER_CALL:
|
||||
return new RdioScannerConfiguration(format);
|
||||
case BROADCASTIFY:
|
||||
return new BroadcastifyFeedConfiguration(format);
|
||||
case ICECAST_HTTP:
|
||||
|
||||
@@ -31,6 +31,7 @@ public enum BroadcastServerType
|
||||
BROADCASTIFY_CALL("Broadcastify Call", "images/broadcastify.png"),
|
||||
|
||||
ICECAST_HTTP("Icecast 2 (v2.4+)", "images/icecast.png"),
|
||||
RDIOSCANNER_CALL("Rdio Scanner", "images/rdioscanner.png"),
|
||||
ICECAST_TCP("Icecast (v2.3)", "images/icecast.png"),
|
||||
SHOUTCAST_V1("Shoutcast v1.x", "images/shoutcast.png"),
|
||||
SHOUTCAST_V2("Shoutcast v2.x", "images/shoutcast.png"),
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 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.audio.broadcast.rdioscanner;
|
||||
|
||||
/**
|
||||
* HTTP headers used for posting to Rdio Scanner API
|
||||
*/
|
||||
public enum FormField
|
||||
{
|
||||
AUDIO("audio"),
|
||||
AUDIO_NAME("audioName"),
|
||||
AUDIO_TYPE("audioType"),
|
||||
DATE_TIME("dateTime"),
|
||||
FREQUENCIES("frequencies"),
|
||||
FREQUENCY("frequency"),
|
||||
KEY("key"),
|
||||
PATCHES("patches"),
|
||||
SOURCE("source"),
|
||||
SOURCES("sources"),
|
||||
SYSTEM("system"),
|
||||
SYSTEM_LABEL("systemLabel"),
|
||||
TALKGROUP_ID("talkgroup"),
|
||||
TALKGROUP_GROUP("talkgroupGroup"),
|
||||
TALKGROUP_LABEL("talkgroupLabel"),
|
||||
TALKGROUP_TAG("talkgroupTag"),
|
||||
TEST("test");
|
||||
|
||||
private String mHeader;
|
||||
|
||||
FormField(String header)
|
||||
{
|
||||
mHeader = header;
|
||||
}
|
||||
|
||||
public String getHeader()
|
||||
{
|
||||
return mHeader;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.audio.broadcast.rdioscanner;
|
||||
|
||||
import com.google.common.net.HttpHeaders;
|
||||
import com.google.common.base.Joiner;
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.audio.broadcast.AbstractAudioBroadcaster;
|
||||
import io.github.dsheirer.audio.broadcast.AudioRecording;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastEvent;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastState;
|
||||
import io.github.dsheirer.audio.convert.InputAudioFormat;
|
||||
import io.github.dsheirer.audio.convert.MP3Setting;
|
||||
import io.github.dsheirer.gui.playlist.radioreference.RadioReferenceDecoder;
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierClass;
|
||||
import io.github.dsheirer.identifier.MutableIdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.configuration.ConfigurationLongIdentifier;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroup;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* Audio broadcaster to push completed audio recordings to the Rdio Scanner call upload API.
|
||||
*
|
||||
*/
|
||||
public class RdioScannerBroadcaster extends AbstractAudioBroadcaster<RdioScannerConfiguration>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RdioScannerBroadcaster.class);
|
||||
|
||||
private static final String ENCODING_TYPE_MP3 = "mp3";
|
||||
private static final String MULTIPART_TYPE = "multipart";
|
||||
private static final String DEFAULT_SUBTYPE = "form-data";
|
||||
private static final String MULTIPART_FORM_DATA = MULTIPART_TYPE + "/" + DEFAULT_SUBTYPE;
|
||||
private Queue<AudioRecording> mAudioRecordingQueue = new LinkedTransferQueue<>();
|
||||
private ScheduledFuture<?> mAudioRecordingProcessorFuture;
|
||||
private HttpClient mHttpClient = HttpClient.newBuilder()
|
||||
.version(HttpClient.Version.HTTP_2)
|
||||
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||
.connectTimeout(Duration.ofSeconds(20))
|
||||
.build();
|
||||
private long mLastConnectionAttempt;
|
||||
private long mConnectionAttemptInterval = 5000; //Every 5 seconds
|
||||
private AliasModel mAliasModel;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the broadcaster
|
||||
* @param config to use
|
||||
* @param aliasModel for access to aliases
|
||||
*/
|
||||
public RdioScannerBroadcaster(RdioScannerConfiguration config, InputAudioFormat inputAudioFormat,
|
||||
MP3Setting mp3Setting, AliasModel aliasModel)
|
||||
{
|
||||
super(config);
|
||||
mAliasModel = aliasModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the audio recording processor thread
|
||||
*/
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
setBroadcastState(BroadcastState.CONNECTING);
|
||||
String response = testConnection(getBroadcastConfiguration());
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* Rdio Scanner API does not currently expose a test method.
|
||||
*/
|
||||
if(response != null && response.toLowerCase().startsWith("incomplete call data: no talkgroup"))
|
||||
{
|
||||
setBroadcastState(BroadcastState.CONNECTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Error connecting to Rdio Scanner server on startup [" + response + "]");
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
}
|
||||
|
||||
if(mAudioRecordingProcessorFuture == null)
|
||||
{
|
||||
mAudioRecordingProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new AudioRecordingProcessor(),
|
||||
0, 500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the audio recording processor thread
|
||||
*/
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
if(mAudioRecordingProcessorFuture != null)
|
||||
{
|
||||
mAudioRecordingProcessorFuture.cancel(true);
|
||||
mAudioRecordingProcessorFuture = null;
|
||||
dispose();
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares for disposal
|
||||
*/
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
AudioRecording audioRecording = mAudioRecordingQueue.poll();
|
||||
|
||||
while(audioRecording != null)
|
||||
{
|
||||
audioRecording.removePendingReplay();
|
||||
audioRecording = mAudioRecordingQueue.poll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this broadcaster continues to have successful connections to and transactions with the remote
|
||||
* server. If there is a connectivity or other issue, the broadcast state is set to temporary error and
|
||||
* the audio processor thread will persistently invoke this method to attempt a reconnect.
|
||||
*
|
||||
* Rdio Scanner does not have a test API endpoint, so we look for the incomplete call response.
|
||||
*/
|
||||
private boolean connected()
|
||||
{
|
||||
if(getBroadcastState() != BroadcastState.CONNECTED &&
|
||||
(System.currentTimeMillis() - mLastConnectionAttempt > mConnectionAttemptInterval))
|
||||
{
|
||||
setBroadcastState(BroadcastState.CONNECTING);
|
||||
|
||||
String response = testConnection(getBroadcastConfiguration());
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
|
||||
if(response != null && response.toLowerCase().startsWith("incomplete call data: no talkgroup"))
|
||||
{
|
||||
setBroadcastState(BroadcastState.CONNECTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return getBroadcastState() == BroadcastState.CONNECTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAudioQueueSize()
|
||||
{
|
||||
return mAudioRecordingQueue.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(AudioRecording audioRecording)
|
||||
{
|
||||
mAudioRecordingQueue.offer(audioRecording);
|
||||
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the audio recording is non-null and not too old, meaning that the age of the recording has not
|
||||
* exceeded the max age value indicated in the broadcast configuration. Audio recordings that are too old will be
|
||||
* deleted to ensure that the in-memory queue size doesn't blow up.
|
||||
* @param audioRecording to test
|
||||
* @return true if the recording is valid
|
||||
*/
|
||||
private boolean isValid(AudioRecording audioRecording)
|
||||
{
|
||||
return audioRecording != null && System.currentTimeMillis() - audioRecording.getStartTime() <=
|
||||
getBroadcastConfiguration().getMaximumRecordingAge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes any enqueued audio recordings. This method employs asynchronous
|
||||
* interaction with the server, so multiple audio recording uploads can occur simultaneously.
|
||||
*/
|
||||
private void processRecordingQueue()
|
||||
{
|
||||
|
||||
|
||||
while(connected() && !mAudioRecordingQueue.isEmpty())
|
||||
{
|
||||
final AudioRecording audioRecording = mAudioRecordingQueue.poll();
|
||||
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
||||
|
||||
if(isValid(audioRecording) && audioRecording.getRecordingLength() > 0)
|
||||
{
|
||||
float durationSeconds = (float)(audioRecording.getRecordingLength() / 1E3f);
|
||||
long timestampSeconds = (int)(audioRecording.getStartTime() / 1E3);
|
||||
String talkgroup = getTo(audioRecording);
|
||||
String radioId = getFrom(audioRecording);
|
||||
Long frequency = getFrequency(audioRecording);
|
||||
String patches = getPatches(audioRecording);
|
||||
String talkgroupLabel = getTalkgroupLabel(audioRecording);
|
||||
String talkgroupGroup = getTalkgroupGroup(audioRecording);
|
||||
String systemLabel = getSystemLabel(audioRecording);
|
||||
|
||||
try
|
||||
{
|
||||
byte[] audioBytes = null;
|
||||
|
||||
try
|
||||
{
|
||||
audioBytes = Files.readAllBytes(audioRecording.getPath());
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("Rdio Scanner API - audio recording file not found - ignoring upload");
|
||||
}
|
||||
|
||||
if(audioBytes != null)
|
||||
{
|
||||
|
||||
RdioScannerBuilder bodyBuilder = new RdioScannerBuilder();
|
||||
bodyBuilder.addPart(FormField.KEY, getBroadcastConfiguration().getApiKey())
|
||||
.addPart(FormField.SYSTEM, getBroadcastConfiguration().getSystemID())
|
||||
.addFile(audioBytes)
|
||||
.addPart(FormField.DATE_TIME, timestampSeconds)
|
||||
.addPart(FormField.TALKGROUP_ID, talkgroup)
|
||||
.addPart(FormField.SOURCE, radioId)
|
||||
.addPart(FormField.FREQUENCY, frequency)
|
||||
.addPart(FormField.TALKGROUP_LABEL, talkgroupLabel)
|
||||
.addPart(FormField.TALKGROUP_GROUP, talkgroupGroup)
|
||||
.addPart(FormField.SYSTEM_LABEL, systemLabel)
|
||||
.addPart(FormField.PATCHES, patches);
|
||||
|
||||
HttpRequest fileRequest = HttpRequest.newBuilder()
|
||||
.uri(URI.create(getBroadcastConfiguration().getHost()))
|
||||
.header(HttpHeaders.CONTENT_TYPE, MULTIPART_FORM_DATA + "; boundary=" + bodyBuilder.getBoundary())
|
||||
.header(HttpHeaders.USER_AGENT, "sdrtrunk")
|
||||
.header(HttpHeaders.CONTENT_TYPE, "audio/mpeg")
|
||||
.POST(bodyBuilder.build())
|
||||
.build();
|
||||
|
||||
mHttpClient.sendAsync(fileRequest, HttpResponse.BodyHandlers.ofString())
|
||||
.whenComplete((fileResponse, throwable1) -> {
|
||||
if(throwable1 != null || fileResponse.statusCode() != 200)
|
||||
{
|
||||
if(throwable1 instanceof IOException || throwable1 instanceof CompletionException)
|
||||
{
|
||||
//We get socket reset exceptions occasionally when the remote server doesn't
|
||||
//fully read our request and immediately responds.
|
||||
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
||||
mLog.error("Rdio Scanner API file upload fail [" +
|
||||
fileResponse.statusCode() + "] response [" +
|
||||
fileResponse.body() + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
||||
mLog.error("Rdio Scanner API file upload fail [" +
|
||||
fileResponse.statusCode() + "] response [" +
|
||||
fileResponse.body() + "]");
|
||||
}
|
||||
|
||||
incrementErrorAudioCount();
|
||||
broadcast(new BroadcastEvent(RdioScannerBroadcaster.this,
|
||||
BroadcastEvent.Event.BROADCASTER_ERROR_COUNT_CHANGE));
|
||||
}
|
||||
else
|
||||
{
|
||||
String fileResponseString = fileResponse.body();
|
||||
|
||||
if(fileResponseString.contains("Call imported successfully."))
|
||||
{
|
||||
incrementStreamedAudioCount();
|
||||
broadcast(new BroadcastEvent(RdioScannerBroadcaster.this,
|
||||
BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE));
|
||||
audioRecording.removePendingReplay();
|
||||
}
|
||||
else if(fileResponseString.contains("duplicate call rejected"))
|
||||
{
|
||||
//Rdio Scanner is telling us to skip audio upload - someone already uploaded it
|
||||
audioRecording.removePendingReplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
||||
mLog.error("Rdio Scanner API file upload fail [" +
|
||||
fileResponse.statusCode() + "] response [" +
|
||||
fileResponse.body() + "]");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
//Register an error for the file not found exception
|
||||
mLog.error("Rdio Scanner API - upload file not found [" +
|
||||
audioRecording.getPath().toString() + "]");
|
||||
incrementErrorAudioCount();
|
||||
broadcast(new BroadcastEvent(RdioScannerBroadcaster.this,
|
||||
BroadcastEvent.Event.BROADCASTER_ERROR_COUNT_CHANGE));
|
||||
audioRecording.removePendingReplay();
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Unknown Error", e);
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
incrementErrorAudioCount();
|
||||
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_ERROR_COUNT_CHANGE));
|
||||
audioRecording.removePendingReplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If we're not connected and there are recordings in the queue, check the recording at the head of the queue
|
||||
// and start age-off once the recordings become too old. The recordings should be time ordered in the queue.
|
||||
AudioRecording audioRecording = mAudioRecordingQueue.peek();
|
||||
|
||||
while(audioRecording != null)
|
||||
{
|
||||
if(isValid(audioRecording))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Remove the recording from the queue, remove a replay, and peek at the next recording in the queue
|
||||
mAudioRecordingQueue.poll();
|
||||
audioRecording.removePendingReplay();
|
||||
incrementAgedOffAudioCount();
|
||||
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_AGED_OFF_COUNT_CHANGE));
|
||||
audioRecording = mAudioRecordingQueue.peek();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a frequency value from the audio recording identifier collection.
|
||||
*/
|
||||
private static Long getFrequency(AudioRecording audioRecording)
|
||||
{
|
||||
Identifier identifier = audioRecording.getIdentifierCollection().getIdentifier(IdentifierClass.CONFIGURATION,
|
||||
Form.CHANNEL_FREQUENCY, Role.ANY);
|
||||
|
||||
if(identifier instanceof ConfigurationLongIdentifier)
|
||||
{
|
||||
Long value = ((ConfigurationLongIdentifier)identifier).getValue();
|
||||
|
||||
if(value != null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return Long.valueOf(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted string with the FROM identifier or uses a default of zero(0)
|
||||
*/
|
||||
private static String getFrom(AudioRecording audioRecording)
|
||||
{
|
||||
for(Identifier identifier: audioRecording.getIdentifierCollection().getIdentifiers(Role.FROM))
|
||||
{
|
||||
if(identifier instanceof RadioIdentifier)
|
||||
{
|
||||
return ((RadioIdentifier)identifier).getValue().toString();
|
||||
}
|
||||
}
|
||||
|
||||
return "0";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted string with the TO identifiers or uses a default of zero (0)
|
||||
*
|
||||
*/
|
||||
private static String getTo(AudioRecording audioRecording)
|
||||
{
|
||||
Identifier identifier = audioRecording.getIdentifierCollection().getToIdentifier();
|
||||
|
||||
if(identifier instanceof PatchGroupIdentifier patchGroupIdentifier)
|
||||
{
|
||||
return patchGroupIdentifier.getValue().getPatchGroup().getValue().toString();
|
||||
}
|
||||
else if(identifier instanceof TalkgroupIdentifier talkgroupIdentifier)
|
||||
{
|
||||
return String.valueOf(RadioReferenceDecoder.convertToRadioReferenceTalkgroup(talkgroupIdentifier.getValue(),
|
||||
talkgroupIdentifier.getProtocol()));
|
||||
}
|
||||
else if(identifier instanceof RadioIdentifier radioIdentifier)
|
||||
{
|
||||
return radioIdentifier.getValue().toString();
|
||||
}
|
||||
|
||||
return "0";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted string with the Talkgroup Label from the Audio Recording Alias
|
||||
* If this is a PatchGroup we return only the first label as the primary talkgroup label.
|
||||
*
|
||||
*/
|
||||
private String getTalkgroupLabel(AudioRecording audioRecording)
|
||||
{
|
||||
|
||||
AliasList aliasList = mAliasModel.getAliasList(audioRecording.getIdentifierCollection());
|
||||
Identifier identifier = audioRecording.getIdentifierCollection().getToIdentifier();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(identifier != null)
|
||||
{
|
||||
List<Alias> aliases = aliasList.getAliases(identifier);
|
||||
if(!aliases.isEmpty())
|
||||
{
|
||||
sb.append(aliases.get(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted string with the Talkgroup Group from the Audio Recording Alias
|
||||
* If this is a PatchGroup we return only the first group as the primary talkgroup group.
|
||||
*
|
||||
*/
|
||||
private String getTalkgroupGroup(AudioRecording audioRecording)
|
||||
{
|
||||
|
||||
AliasList aliasList = mAliasModel.getAliasList(audioRecording.getIdentifierCollection());
|
||||
Identifier identifier = audioRecording.getIdentifierCollection().getToIdentifier();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(identifier != null)
|
||||
{
|
||||
List<Alias> aliases = aliasList.getAliases(identifier);
|
||||
if(!aliases.isEmpty())
|
||||
{
|
||||
sb.append(aliases.get(0).getGroup());
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted string with the System Label from the Audio Recording Alias
|
||||
* If this is a PatchGroup we return only the first sytem as the primary talkgroup system.
|
||||
*
|
||||
*/
|
||||
private String getSystemLabel(AudioRecording audioRecording)
|
||||
{
|
||||
List<Identifier> systems = audioRecording.getIdentifierCollection().getIdentifiers(Form.SYSTEM);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!systems.isEmpty())
|
||||
{
|
||||
sb.append(systems.get(0));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted string with the patched talkgroups
|
||||
*/
|
||||
public static String getPatches(AudioRecording audioRecording)
|
||||
{
|
||||
Identifier identifier = audioRecording.getIdentifierCollection().getToIdentifier();
|
||||
|
||||
if(identifier instanceof PatchGroupIdentifier patchGroupIdentifier)
|
||||
{
|
||||
PatchGroup patchGroup = patchGroupIdentifier.getValue();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
|
||||
sb.append(patchGroup.getPatchGroup().getValue().toString());
|
||||
|
||||
for(TalkgroupIdentifier patched: patchGroup.getPatchedGroupIdentifiers())
|
||||
{
|
||||
sb.append(",").append(patched.getValue());
|
||||
}
|
||||
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
return "[]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests both the connection and configuration against the RdioScanner Call API service
|
||||
* @param configuration containing API key and system id
|
||||
* @return error string or null if test is successful
|
||||
*/
|
||||
public static String testConnection(RdioScannerConfiguration configuration)
|
||||
{
|
||||
HttpClient httpClient = HttpClient.newBuilder()
|
||||
.version(HttpClient.Version.HTTP_1_1)
|
||||
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||
.connectTimeout(Duration.ofSeconds(20))
|
||||
.build();
|
||||
|
||||
RdioScannerBuilder bodyBuilder = new RdioScannerBuilder();
|
||||
bodyBuilder.addPart(FormField.KEY, configuration.getApiKey())
|
||||
.addPart(FormField.SYSTEM, configuration.getSystemID())
|
||||
.addPart(FormField.TEST, 1);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(configuration.getHost()))
|
||||
.header(HttpHeaders.CONTENT_TYPE, MULTIPART_FORM_DATA + "; boundary=" + bodyBuilder.getBoundary())
|
||||
.header(HttpHeaders.USER_AGENT, "sdrtrunk")
|
||||
.header(HttpHeaders.ACCEPT, "*/*")
|
||||
.POST(bodyBuilder.build())
|
||||
.build();
|
||||
|
||||
HttpResponse.BodyHandler<String> responseHandler = HttpResponse.BodyHandlers.ofString();
|
||||
|
||||
try
|
||||
{
|
||||
HttpResponse<String> response = httpClient.send(request, responseHandler);
|
||||
String responseBody = response.body();
|
||||
return (responseBody != null ? responseBody : "(no response)") + " Status Code:" + response.statusCode();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
return e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public class AudioRecordingProcessor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
processRecordingQueue();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
mLog.debug("Starting ...");
|
||||
|
||||
RdioScannerConfiguration config = new RdioScannerConfiguration();
|
||||
config.setHost("https://api.RdioScanner.com/call-upload-dev");
|
||||
config.setApiKey("c33aae37-8572-11ea-bd8b-0ecc8ab9ccec");
|
||||
config.setSystemID(11);
|
||||
|
||||
String response = testConnection(config);
|
||||
|
||||
if(response == null)
|
||||
{
|
||||
mLog.debug("Test Successful!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(response.contains("1 Invalid-API-Key"))
|
||||
{
|
||||
mLog.error("Invalid API Key");
|
||||
}
|
||||
else if(response.contains("1 API-Key-Access-Denied"))
|
||||
{
|
||||
mLog.error("System ID not valid for API Key");
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.debug("Response: " + response);
|
||||
}
|
||||
}
|
||||
|
||||
mLog.debug("Finished!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 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.audio.broadcast.rdioscanner;
|
||||
|
||||
import java.net.http.HttpRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.lang.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Builder for an HTTP body publisher to produce a RdioScanner call event
|
||||
*/
|
||||
public class RdioScannerBuilder
|
||||
{
|
||||
private static final String DASH_DASH = "--";
|
||||
private static final String BOUNDARY = "--sdrtrunk-sdrtrunk-sdrtrunk";
|
||||
private List<Part> mParts = new ArrayList<>();
|
||||
private byte[] audioBytes = null;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public RdioScannerBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the static multi-part boundary string
|
||||
*/
|
||||
public String getBoundary()
|
||||
{
|
||||
return BOUNDARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Audio bytes part to the call
|
||||
*/
|
||||
public RdioScannerBuilder addFile(byte[] value)
|
||||
{
|
||||
audioBytes = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a string part to the call
|
||||
*/
|
||||
public RdioScannerBuilder addPart(FormField key, String value)
|
||||
{
|
||||
if(key != null && value != null)
|
||||
{
|
||||
mParts.add(new Part(key.getHeader(), value));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a number part to the call
|
||||
*/
|
||||
public RdioScannerBuilder addPart(FormField key, Number value)
|
||||
{
|
||||
if(key != null && value != null)
|
||||
{
|
||||
mParts.add(new Part(key.getHeader(), value.toString()));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a form-data item
|
||||
*/
|
||||
private static String formatPart(Part part, String boundary)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(DASH_DASH).append(boundary).append("\r\n");
|
||||
sb.append("Content-Disposition: form-data; name=\"").append(part.mKey).append("\"\r\n\r\n");
|
||||
sb.append(part.getValue()).append("\r\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the audio file item
|
||||
*/
|
||||
private String formatFilePart(String boundary)
|
||||
{
|
||||
|
||||
if(audioBytes == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb= new StringBuilder();
|
||||
sb.append(DASH_DASH).append(boundary).append("\r\n");
|
||||
sb.append("Content-Disposition: form-data; name=\"").append("audio").append("\"\r\n\r\n");
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the boundary closing item
|
||||
*/
|
||||
private static String getClosingBoundary(String boundary)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(DASH_DASH).append(boundary).append(DASH_DASH).append("\r\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPublisher for accessing the call form data
|
||||
*/
|
||||
public HttpRequest.BodyPublisher build()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(Part part: mParts)
|
||||
{
|
||||
sb.append(formatPart(part, BOUNDARY));
|
||||
}
|
||||
|
||||
sb.append(formatFilePart(BOUNDARY));
|
||||
|
||||
|
||||
/**
|
||||
* We need to create a ByteArray consisting of the Sting "parts" and the audio file bytes
|
||||
*/
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
outputStream.write(sb.toString().getBytes());
|
||||
|
||||
if(audioBytes != null)
|
||||
{
|
||||
outputStream.write(audioBytes);
|
||||
}
|
||||
|
||||
sb = new StringBuilder();
|
||||
sb.append("\r\n");
|
||||
sb.append(getClosingBoundary(BOUNDARY));
|
||||
|
||||
outputStream.write(sb.toString().getBytes());
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
//mLog.error("Rdio Scanner API - unable to create POST reqeust.");
|
||||
}
|
||||
|
||||
return HttpRequest.BodyPublishers.ofByteArray(outputStream.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(Part part: mParts)
|
||||
{
|
||||
sb.append(formatPart(part, BOUNDARY));
|
||||
}
|
||||
|
||||
sb.append(getClosingBoundary(BOUNDARY));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Key:Value pair holder
|
||||
*/
|
||||
public class Part
|
||||
{
|
||||
private String mKey;
|
||||
private String mValue;
|
||||
|
||||
/**
|
||||
* Constructs a new part
|
||||
* @param key value
|
||||
* @param value item
|
||||
*/
|
||||
public Part(String key, String value)
|
||||
{
|
||||
mKey = key;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
{
|
||||
return mKey;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.audio.broadcast.rdioscanner;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
/**
|
||||
* Streaming configuration for Rdio Scanner API.
|
||||
*
|
||||
* Note: this API is not a streaming audio service, rather a completed call push service. However, it fits nicely
|
||||
* with the structure of the audio streaming subsystem in sdrtrunk
|
||||
*/
|
||||
public class RdioScannerConfiguration extends BroadcastConfiguration
|
||||
{
|
||||
private IntegerProperty mSystemID = new SimpleIntegerProperty();
|
||||
private StringProperty mApiKey = new SimpleStringProperty();
|
||||
|
||||
/**
|
||||
* Constructor for faster jackson
|
||||
*/
|
||||
public RdioScannerConfiguration()
|
||||
{
|
||||
this(BroadcastFormat.MP3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
* @param format to use for audio recording (MP3)
|
||||
*/
|
||||
public RdioScannerConfiguration(BroadcastFormat format)
|
||||
{
|
||||
super(format);
|
||||
|
||||
//The parent class binds this property, so we unbind it and rebind it here
|
||||
mValid.unbind();
|
||||
mValid.bind(Bindings.and(Bindings.and(Bindings.greaterThan(mSystemID, 0), Bindings.isNotNull(mApiKey)),
|
||||
Bindings.isNotNull(mHost)));
|
||||
|
||||
if(mHost.getValue() == null || mHost.getValue().isEmpty())
|
||||
{
|
||||
mHost.set("http://localhost");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* System ID as a property
|
||||
*/
|
||||
public IntegerProperty systemIDProperty()
|
||||
{
|
||||
return mSystemID;
|
||||
}
|
||||
|
||||
/**
|
||||
* API key as a property
|
||||
*/
|
||||
public StringProperty apiKeyProperty()
|
||||
{
|
||||
return mApiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* API Key
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "api_key")
|
||||
public String getApiKey()
|
||||
{
|
||||
return mApiKey.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the api key
|
||||
* @param apiKey
|
||||
*/
|
||||
public void setApiKey(String apiKey)
|
||||
{
|
||||
mApiKey.setValue(apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* System ID as provided by RdioScanner.com
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "system_id")
|
||||
public int getSystemID()
|
||||
{
|
||||
return mSystemID.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the system ID provided by RdioScanner.com
|
||||
*/
|
||||
public void setSystemID(int systemID)
|
||||
{
|
||||
mSystemID.set(systemID);
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public BroadcastServerType getBroadcastServerType()
|
||||
{
|
||||
return BroadcastServerType.RDIOSCANNER_CALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BroadcastConfiguration copyOf()
|
||||
{
|
||||
RdioScannerConfiguration copy = new RdioScannerConfiguration();
|
||||
copy.setSystemID(getSystemID());
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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.audio.broadcast.rdioscanner;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPConfiguration;
|
||||
import io.github.dsheirer.rrapi.type.UserFeedBroadcast;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RdioScannerFeedConfiguration extends IcecastTCPConfiguration
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RdioScannerFeedConfiguration.class);
|
||||
|
||||
private int mFeedID;
|
||||
|
||||
public RdioScannerFeedConfiguration()
|
||||
{
|
||||
//No-arg constructor for JAXB
|
||||
this(BroadcastFormat.MP3);
|
||||
}
|
||||
|
||||
/**
|
||||
* RdioScanner configuration for Icecast 2.3.2 compatible servers
|
||||
*
|
||||
* @param format of output audio (MP3)
|
||||
*/
|
||||
public RdioScannerFeedConfiguration(BroadcastFormat format)
|
||||
{
|
||||
super(format);
|
||||
|
||||
setBitRate(16);
|
||||
setChannels(1);
|
||||
setSampleRate(8000);
|
||||
setInline(true);
|
||||
}
|
||||
|
||||
public static RdioScannerFeedConfiguration from(UserFeedBroadcast userFeedBroadcast)
|
||||
{
|
||||
RdioScannerFeedConfiguration config = new RdioScannerFeedConfiguration(BroadcastFormat.MP3);
|
||||
config.setName(userFeedBroadcast.getDescription());
|
||||
config.setHost(userFeedBroadcast.getHostname());
|
||||
config.setMountPoint(userFeedBroadcast.getMount());
|
||||
config.setFeedID(userFeedBroadcast.getFeedId());
|
||||
config.setPassword(userFeedBroadcast.getPassword());
|
||||
|
||||
try
|
||||
{
|
||||
config.setPort(Integer.parseInt(userFeedBroadcast.getPort()));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error creating Rdio Scanner configuration from radio reference user feed instance");
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BroadcastConfiguration copyOf()
|
||||
{
|
||||
RdioScannerFeedConfiguration copy = new RdioScannerFeedConfiguration(getBroadcastFormat());
|
||||
|
||||
//Broadcast Configuration Parameters
|
||||
copy.setName(getName());
|
||||
copy.setHost(getHost());
|
||||
copy.setPort(getPort());
|
||||
copy.setInline(getInline());
|
||||
copy.setPassword(getPassword());
|
||||
copy.setDelay(getDelay());
|
||||
copy.setEnabled(false);
|
||||
|
||||
//Icecast Configuration Parameters
|
||||
copy.setUserName(getUserName());
|
||||
copy.setMountPoint(getMountPoint());
|
||||
copy.setDescription(getDescription());
|
||||
copy.setGenre(getGenre());
|
||||
copy.setPublic(isPublic());
|
||||
copy.setURL(getURL());
|
||||
|
||||
//RdioScanner Configuration Parameters
|
||||
copy.setFeedID(getFeedID());
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public BroadcastServerType getBroadcastServerType()
|
||||
{
|
||||
return BroadcastServerType.RDIOSCANNER_CALL;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "feed_id")
|
||||
public int getFeedID()
|
||||
{
|
||||
return mFeedID;
|
||||
}
|
||||
|
||||
public void setFeedID(int feedID)
|
||||
{
|
||||
mFeedID = feedID;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.audio.codec.mbe;
|
||||
@@ -32,11 +29,6 @@ import io.github.dsheirer.message.IMessageListener;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import jmbe.iface.IAudioCodec;
|
||||
import jmbe.iface.IAudioCodecLibrary;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@@ -44,15 +36,20 @@ import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jmbe.iface.IAudioCodec;
|
||||
import jmbe.iface.IAudioCodecLibrary;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class JmbeAudioModule extends AbstractAudioModule implements Listener<IMessage>, IMessageListener,
|
||||
ISquelchStateListener
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(JmbeAudioModule.class);
|
||||
private static final String JMBE_AUDIO_LIBRARY = "JMBE";
|
||||
private static List<String> mLibraryLoadStatusLogged = new ArrayList<>();
|
||||
private static final List<String> mLibraryLoadStatusLogged = new ArrayList<>();
|
||||
private IAudioCodec mAudioCodec;
|
||||
private UserPreferences mUserPreferences;
|
||||
private final UserPreferences mUserPreferences;
|
||||
private static Class sLoadedJmbeAudioConverterClass;
|
||||
|
||||
public JmbeAudioModule(UserPreferences userPreferences, AliasList aliasList, int timeslot)
|
||||
{
|
||||
@@ -70,7 +67,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis
|
||||
mAudioCodec = null;
|
||||
}
|
||||
|
||||
protected IAudioCodec getAudioCodec()
|
||||
public IAudioCodec getAudioCodec()
|
||||
{
|
||||
return mAudioCodec;
|
||||
}
|
||||
@@ -110,38 +107,67 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis
|
||||
protected abstract String getCodecName();
|
||||
|
||||
/**
|
||||
* Loads audio frame processing chain. Constructs an imbe targetdataline
|
||||
* to receive the raw imbe frames. Adds an IMBE to 8k PCM format conversion
|
||||
* stream wrapper. Finally, adds an upsampling (8k to 48k) stream wrapper.
|
||||
* Loads JMBE audio converter library class and then instantiates new converter instances from the loaded class.
|
||||
*/
|
||||
protected void loadConverter()
|
||||
{
|
||||
IAudioCodec audioConverter = null;
|
||||
|
||||
Path path = mUserPreferences.getJmbeLibraryPreference().getPathJmbeLibrary();
|
||||
|
||||
if(path != null)
|
||||
if(sLoadedJmbeAudioConverterClass == null)
|
||||
{
|
||||
try
|
||||
Path path = mUserPreferences.getJmbeLibraryPreference().getPathJmbeLibrary();
|
||||
|
||||
if(path != null)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.info("Loading JMBE library from [" + path.toString() + "]");
|
||||
mLog.info("Loading JMBE library from [" + path + "]");
|
||||
}
|
||||
|
||||
URLClassLoader childClassLoader = new URLClassLoader(new URL[]{path.toUri().toURL()},
|
||||
this.getClass().getClassLoader());
|
||||
|
||||
Class classToLoad = Class.forName("jmbe.JMBEAudioLibrary", true, childClassLoader);
|
||||
|
||||
Object instance = classToLoad.getDeclaredConstructor().newInstance();
|
||||
|
||||
if(instance instanceof IAudioCodecLibrary)
|
||||
try
|
||||
{
|
||||
IAudioCodecLibrary library = (IAudioCodecLibrary)instance;
|
||||
URLClassLoader childClassLoader = new URLClassLoader(new URL[]{path.toUri().toURL()},
|
||||
this.getClass().getClassLoader());
|
||||
|
||||
sLoadedJmbeAudioConverterClass = Class.forName("jmbe.JMBEAudioLibrary", true, childClassLoader);
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY + getCodecName()))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - " + iae.getMessage());
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY + getCodecName());
|
||||
}
|
||||
}
|
||||
catch(MalformedURLException mue)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library from path [" + path + "]");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(ClassNotFoundException e1)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - class not found");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sLoadedJmbeAudioConverterClass != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object instance = sLoadedJmbeAudioConverterClass.getDeclaredConstructor().newInstance();
|
||||
|
||||
if(instance instanceof IAudioCodecLibrary library)
|
||||
{
|
||||
if((library.getMajorVersion() == 1 && library.getMinorVersion() >= 0 &&
|
||||
library.getBuildVersion() >= 0) || library.getMajorVersion() >= 1)
|
||||
library.getBuildVersion() >= 0) || library.getMajorVersion() >= 1)
|
||||
{
|
||||
audioConverter = library.getAudioConverter(getCodecName());
|
||||
|
||||
@@ -169,38 +195,6 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY + getCodecName()))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - " + iae.getMessage());
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY + getCodecName());
|
||||
}
|
||||
}
|
||||
catch(NoSuchMethodException nsme)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - no such method exception");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(MalformedURLException mue)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library from path [" + path + "]");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(ClassNotFoundException e1)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - class not found");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(InvocationTargetException ite)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
@@ -225,6 +219,14 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(NoSuchMethodException nsme)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - no such method exception");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -235,13 +237,6 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis
|
||||
}
|
||||
}
|
||||
|
||||
if(audioConverter != null)
|
||||
{
|
||||
mAudioCodec = audioConverter;
|
||||
}
|
||||
else
|
||||
{
|
||||
mAudioCodec = null;
|
||||
}
|
||||
mAudioCodec = audioConverter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,46 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.audio.codec.mbe;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.audio.AudioSegment;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
|
||||
import io.github.dsheirer.module.decode.p25.audio.P25P1AudioModule;
|
||||
import io.github.dsheirer.module.decode.p25.audio.P25P1CallSequenceRecorder;
|
||||
import io.github.dsheirer.module.decode.p25.audio.VoiceFrame;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25Talkgroup;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.record.AudioSegmentRecorder;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import jmbe.iface.IAudioCodec;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.filefilter.DirectoryFileFilter;
|
||||
import org.apache.commons.io.filefilter.SuffixFileFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -32,79 +51,130 @@ public class MBECallSequenceConverter
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MBECallSequenceConverter.class);
|
||||
|
||||
// public static void convert(Path input, Path output) throws IOException
|
||||
// {
|
||||
// InputStream inputStream = Files.newInputStream(input);
|
||||
// ObjectMapper mapper = new ObjectMapper();
|
||||
// MBECallSequence sequence = mapper.readValue(inputStream, MBECallSequence.class);
|
||||
// convert(sequence, output);
|
||||
// }
|
||||
//
|
||||
// public static void convert(MBECallSequence callSequence, Path outputPath)
|
||||
// {
|
||||
// if(callSequence == null || callSequence.isEncrypted())
|
||||
// {
|
||||
// throw new IllegalArgumentException("Cannot decode null or encrypted call sequence");
|
||||
// }
|
||||
//
|
||||
// if(callSequence != null && !callSequence.isEncrypted())
|
||||
// {
|
||||
// ThumbDv.AudioProtocol protocol = ThumbDv.AudioProtocol.P25_PHASE2;
|
||||
//
|
||||
// AudioPacketWaveRecorder recorder = new AudioPacketWaveRecorder(outputPath);
|
||||
// recorder.start();
|
||||
//
|
||||
// long delayMillis = 0;
|
||||
//
|
||||
// try(ThumbDv thumbDv = new ThumbDv(protocol, recorder))
|
||||
// {
|
||||
// thumbDv.start();
|
||||
// for(VoiceFrame voiceFrame: callSequence.getVoiceFrames())
|
||||
// {
|
||||
// mLog.debug("Frame [" + voiceFrame.getFrame() + "] + Hex [" + AmbeResponse.toHex(voiceFrame.getFrameBytes()) + "]");
|
||||
// thumbDv.decode(voiceFrame.getFrameBytes());
|
||||
// delayMillis += 30;
|
||||
// }
|
||||
//
|
||||
// if(delayMillis > 0)
|
||||
// {
|
||||
// delayMillis += 1000;
|
||||
// try
|
||||
// {
|
||||
// Thread.sleep(delayMillis);
|
||||
// }
|
||||
// catch(InterruptedException ie)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// catch(IOException ioe)
|
||||
// {
|
||||
// mLog.error("Error", ioe);
|
||||
// }
|
||||
//
|
||||
// recorder.stop(Paths.get(outputPath.toString().replace(".tmp", ".wav")), new WaveMetadata());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static void main(String[] args)
|
||||
// {
|
||||
// String mbe = "/home/denny/SDRTrunk/recordings/20190331085324_154250000_3_TS0_65084_6591007.mbe";
|
||||
// String mbe = "/home/denny/SDRTrunk/recordings/20190331085324_154250000_2_TS1_65035.mbe";
|
||||
//
|
||||
// Path input = Paths.get(mbe);
|
||||
// Path output = Paths.get(mbe.replace(".mbe", ".tmp"));
|
||||
//
|
||||
// mLog.info("Converting: " + mbe);
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// MBECallSequenceConverter.convert(input, output);
|
||||
// }
|
||||
// catch(IOException ioe)
|
||||
// {
|
||||
// mLog.error("Error", ioe);
|
||||
// }
|
||||
// }
|
||||
/**
|
||||
* Converts the input MBE file to PCM audio and writes to the output wave file.
|
||||
* @param input path to the MBE file.
|
||||
* @param output path to write the WAVE recording.
|
||||
* @throws IOException if there is an error.
|
||||
*/
|
||||
public static void convert(Path input, Path output) throws IOException
|
||||
{
|
||||
InputStream inputStream = Files.newInputStream(input);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
MBECallSequence sequence = mapper.readValue(inputStream, MBECallSequence.class);
|
||||
convert(sequence, output);
|
||||
}
|
||||
|
||||
public static void convert(MBECallSequence callSequence, Path outputPath)
|
||||
{
|
||||
if(callSequence == null || callSequence.isEncrypted())
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot decode null or encrypted call sequence");
|
||||
}
|
||||
|
||||
if(callSequence != null)
|
||||
{
|
||||
if(callSequence.getProtocol().equals(P25P1CallSequenceRecorder.PROTOCOL))
|
||||
{
|
||||
P25P1AudioModule audioModule = new P25P1AudioModule(new UserPreferences(), new AliasList("mbe generator"));
|
||||
audioModule.setRecordAudio(true);
|
||||
audioModule.start();
|
||||
|
||||
if(callSequence.getFromIdentifier() != null)
|
||||
{
|
||||
int from = 0;
|
||||
|
||||
try
|
||||
{
|
||||
from = Integer.parseInt(callSequence.getFromIdentifier());
|
||||
audioModule.getIdentifierUpdateListener().receive(new IdentifierUpdateNotification(APCO25RadioIdentifier.createFrom(from),
|
||||
IdentifierUpdateNotification.Operation.ADD, 0));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error parsing from identifier from value [" + callSequence.getFromIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
if(callSequence.getToIdentifier() != null)
|
||||
{
|
||||
int to = 0;
|
||||
|
||||
try
|
||||
{
|
||||
to = Integer.parseInt(callSequence.getToIdentifier());
|
||||
audioModule.getIdentifierUpdateListener().receive(new IdentifierUpdateNotification(APCO25Talkgroup.create(to),
|
||||
IdentifierUpdateNotification.Operation.ADD, 0));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error parsing from identifier from value [" + callSequence.getFromIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
IAudioCodec codec = audioModule.getAudioCodec();
|
||||
|
||||
for(VoiceFrame voiceFrame: callSequence.getVoiceFrames())
|
||||
{
|
||||
byte[] frameBytes = voiceFrame.getFrameBytes();
|
||||
float[] audio = codec.getAudio(frameBytes);
|
||||
audioModule.addAudio(audio);
|
||||
}
|
||||
|
||||
AudioSegment audioSegment = audioModule.getAudioSegment();
|
||||
|
||||
try
|
||||
{
|
||||
AudioSegmentRecorder.recordWAVE(audioSegment, outputPath);
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.error("Error writing audio segment, ioe");
|
||||
}
|
||||
|
||||
audioModule.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
boolean all = true;
|
||||
|
||||
String path = "/home/denny/SDRTrunk/recordings";
|
||||
Path input = Paths.get(path);
|
||||
|
||||
if(all)
|
||||
{
|
||||
Collection<File> mbeFiles = FileUtils.listFiles(input.toFile(), new SuffixFileFilter(".mbe"), DirectoryFileFilter.DIRECTORY);
|
||||
|
||||
for(File inputFile: mbeFiles)
|
||||
{
|
||||
Path output = Paths.get(inputFile.getAbsolutePath().replace(".mbe", ".wav"));
|
||||
mLog.info("Converting: " + inputFile);
|
||||
try
|
||||
{
|
||||
MBECallSequenceConverter.convert(inputFile.toPath(), output);
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.error("Error", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Path output = Paths.get(path.replace(".mbe", ".wav"));
|
||||
mLog.info("Converting: " + path);
|
||||
|
||||
try
|
||||
{
|
||||
MBECallSequenceConverter.convert(input, output);
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.error("Error", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.audio.playback;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.audio.AudioEvent;
|
||||
import io.github.dsheirer.audio.AudioSegment;
|
||||
import io.github.dsheirer.controller.NamingThreadFactory;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
|
||||
@@ -33,11 +30,13 @@ import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.mixer.MixerChannel;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -51,10 +50,6 @@ import javax.sound.sampled.LineListener;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Audio output/playback channel for a single audio mixer channel. Providers support for playback of audio segments
|
||||
@@ -67,7 +62,6 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
private int mBufferStopThreshold;
|
||||
private Listener<IdentifierCollection> mIdentifierCollectionListener;
|
||||
private Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
|
||||
private ScheduledFuture<?> mProcessorFuture;
|
||||
private SourceDataLine mOutput;
|
||||
private Mixer mMixer;
|
||||
private MixerChannel mMixerChannel;
|
||||
@@ -76,17 +70,18 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
private AudioEvent mAudioStartEvent;
|
||||
private AudioEvent mAudioStopEvent;
|
||||
private boolean mCanProcessAudio = false;
|
||||
private LinkedTransferQueue<AudioSegment> mAudioSegmentQueue = new LinkedTransferQueue<>();
|
||||
private AudioSegment mCurrentAudioSegment;
|
||||
private AudioSegment mNextAudioSegment;
|
||||
private ReentrantLock mLock = new ReentrantLock();
|
||||
private int mCurrentBufferIndex = 0;
|
||||
private UserPreferences mUserPreferences;
|
||||
private BooleanProperty mEmptyProperty = new SimpleBooleanProperty(true);
|
||||
private IntegerProperty mAudioPriority = new SimpleIntegerProperty(Priority.DEFAULT_PRIORITY);
|
||||
private ByteBuffer mAudioSegmentStartTone;
|
||||
private ByteBuffer mAudioSegmentPreemptTone;
|
||||
private ByteBuffer mAudioSegmentDropTone;
|
||||
private boolean mRunning = false;
|
||||
private ScheduledExecutorService mScheduledExecutorService;
|
||||
private ScheduledFuture<?> mProcessorFuture;
|
||||
private boolean mDropDuplicates;
|
||||
private long mOutputLastTimestamp = 0;
|
||||
private static final long STALE_PLAYBACK_THRESHOLD_MS = 500;
|
||||
|
||||
/**
|
||||
* Single audio channel playback with automatic starting and stopping of the
|
||||
@@ -107,7 +102,10 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
{
|
||||
mMixer = mixer;
|
||||
mMixerChannel = mixerChannel;
|
||||
mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new NamingThreadFactory(
|
||||
"sdrtrunk audio output " + mixerChannel.name()));
|
||||
mUserPreferences = userPreferences;
|
||||
mDropDuplicates = mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -150,7 +148,7 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
}
|
||||
|
||||
//Run the queue processor task every 100 milliseconds or 10 times a second
|
||||
mProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new AudioSegmentProcessor(),
|
||||
mProcessorFuture = mScheduledExecutorService.scheduleAtFixedRate(new AudioSegmentProcessor(),
|
||||
0, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@@ -167,31 +165,22 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
|
||||
updateToneInsertionAudioClips();
|
||||
|
||||
//Register to receive directory preference update notifications so we can update the preference items
|
||||
//Register to receive preference update notifications so we can update the preference items
|
||||
MyEventBus.getGlobalEventBus().register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean property that indicates if this audio output has an audio segment queued or in process of playback.
|
||||
* Indicates if this audio output doesn't currently have any audio segments queued for playback.
|
||||
* @return true if empty
|
||||
*/
|
||||
public BooleanProperty emptyProperty()
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return mEmptyProperty;
|
||||
return mAudioSegmentQueue.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio playback priority of the current audio segment or default audio priority if nothing is currently loaded.
|
||||
*/
|
||||
public IntegerProperty audioPriorityProperty()
|
||||
{
|
||||
return mAudioPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the audio segment for playback.
|
||||
*
|
||||
* Note: if a segment is currently playing and another audio segment is already queued for playback, invoking
|
||||
* this method will overwrite the queued segment with the argument.
|
||||
* Schedules the audio segment for playback. The audio segment user count should already be incremented by the
|
||||
* calling entity.
|
||||
*
|
||||
* @param audioSegment to schedule for playback.
|
||||
*/
|
||||
@@ -199,23 +188,8 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
{
|
||||
if(audioSegment != null)
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
if(mNextAudioSegment != null)
|
||||
{
|
||||
mNextAudioSegment.decrementConsumerCount();
|
||||
mNextAudioSegment = null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mNextAudioSegment = audioSegment;
|
||||
mEmptyProperty.set(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
//Audio segment use count has already been incremented by the external caller.
|
||||
mAudioSegmentQueue.add(audioSegment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +228,10 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
{
|
||||
updateToneInsertionAudioClips();
|
||||
}
|
||||
else if(preferenceType == PreferenceType.DUPLICATE_CALL_DETECTION)
|
||||
{
|
||||
mDropDuplicates = mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,7 +240,6 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
private void updateToneInsertionAudioClips()
|
||||
{
|
||||
mAudioSegmentStartTone = null;
|
||||
mAudioSegmentPreemptTone = null;
|
||||
mAudioSegmentDropTone = null;
|
||||
|
||||
float[] start = mUserPreferences.getPlaybackPreference().getStartTone();
|
||||
@@ -272,13 +249,6 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
mAudioSegmentStartTone = convert(start);
|
||||
}
|
||||
|
||||
float[] preempt = mUserPreferences.getPlaybackPreference().getPreemptTone();
|
||||
|
||||
if(preempt != null)
|
||||
{
|
||||
mAudioSegmentPreemptTone = convert(preempt);
|
||||
}
|
||||
|
||||
float[] drop = mUserPreferences.getPlaybackPreference().getDropTone();
|
||||
|
||||
if(drop != null)
|
||||
@@ -287,48 +257,6 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of a processed audio segment and notifies the originator that processing of the segment is complete
|
||||
* by decrementing the consumer count.
|
||||
* @param audioSegment to dispose
|
||||
*/
|
||||
private void dispose(AudioSegment audioSegment)
|
||||
{
|
||||
mAudioPriority.unbind();
|
||||
|
||||
if(audioSegment != null)
|
||||
{
|
||||
audioSegment.decrementConsumerCount();
|
||||
audioSegment.removeIdentifierUpdateNotificationListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a tone indicating that a new audio segment is starting
|
||||
*/
|
||||
private ByteBuffer getAudioSegmentStartTone()
|
||||
{
|
||||
return mAudioSegmentStartTone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a tone indicating that the current audio segment playback has been preempted for a higher priority
|
||||
* audio segment that is now starting.
|
||||
*/
|
||||
private ByteBuffer getAudioSegmentPreemptionTone()
|
||||
{
|
||||
return mAudioSegmentPreemptTone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a tone indicating that the current audio segment playback has been dropped because the audio segment
|
||||
* has been flagged as Do Not Monitor after playback has started.
|
||||
*/
|
||||
private ByteBuffer getAudioSegmentDropTone()
|
||||
{
|
||||
return mAudioSegmentDropTone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the audio buffer data to the source data line. If the data line is not currently playing, write as
|
||||
* much of the buffer to the data line as will fit, start the dataline, and then finish writing the residual
|
||||
@@ -355,6 +283,7 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
|
||||
//Top off the buffer and check if we can start it
|
||||
wrote += mOutput.write(buffer.array(), 0, toWrite);
|
||||
mOutputLastTimestamp = System.currentTimeMillis();
|
||||
|
||||
checkStart();
|
||||
}
|
||||
@@ -363,149 +292,146 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
{
|
||||
//This will block until the buffer is fully written to the data line
|
||||
mOutput.write(buffer.array(), wrote, buffer.array().length - wrote);
|
||||
mOutputLastTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of the currently assigned audio segment.
|
||||
*/
|
||||
private void disposeCurrentAudioSegment()
|
||||
{
|
||||
if(mCurrentAudioSegment != null)
|
||||
{
|
||||
mCurrentAudioSegment.decrementConsumerCount();
|
||||
mCurrentAudioSegment.removeIdentifierUpdateNotificationListener(this);
|
||||
mCurrentAudioSegment = null;
|
||||
broadcast(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the next audio segment from the queue.
|
||||
*/
|
||||
private void loadNextAudioSegment()
|
||||
{
|
||||
AudioSegment audioSegment = mAudioSegmentQueue.poll();
|
||||
|
||||
boolean verificationInProgress = (audioSegment != null);
|
||||
|
||||
while(verificationInProgress)
|
||||
{
|
||||
if(audioSegment != null)
|
||||
{
|
||||
//Throw away the audio segment if it has been flagged as do not monitor or is duplicate
|
||||
if(isThrowaway(audioSegment))
|
||||
{
|
||||
audioSegment.decrementConsumerCount();
|
||||
audioSegment = mAudioSegmentQueue.poll();
|
||||
}
|
||||
else
|
||||
{
|
||||
verificationInProgress = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
verificationInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentAudioSegment = audioSegment;
|
||||
|
||||
if(audioSegment != null)
|
||||
{
|
||||
mCurrentAudioSegment.addIdentifierUpdateNotificationListener(this);
|
||||
broadcast(mCurrentAudioSegment.getIdentifierCollection());
|
||||
}
|
||||
|
||||
mCurrentBufferIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the audio segment should be thrown away because it is marked as duplicate or do-not-monitor.
|
||||
* @param audioSegment to evaluate
|
||||
* @return true if the audio should be thrown away.
|
||||
*/
|
||||
private boolean isThrowaway(AudioSegment audioSegment)
|
||||
{
|
||||
return audioSegment != null && (audioSegment.isDoNotMonitor() || mDropDuplicates && (audioSegment.isDuplicate()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage audio segment playback and process audio segment buffers. This method is designed to be called
|
||||
* by a threaded processor repeatedly to playback the current audio segment and check for and start a newly
|
||||
* assigned audio segment. It also handles starting and stopping the playback source data line to avoid audio
|
||||
* by a threaded processor repeatedly to playback the current audio segment and check for and start newly
|
||||
* added audio segments. It also handles starting and stopping the playback source data line to avoid audio
|
||||
* discontinuities due to buffer underruns.
|
||||
*/
|
||||
private void processAudio()
|
||||
{
|
||||
if(mNextAudioSegment != null)
|
||||
if(mCurrentAudioSegment == null)
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
//Remove an assigned segment that's subsequently flagged as duplicate or do not monitor before playback
|
||||
if(mNextAudioSegment.isDoNotMonitor() || (mNextAudioSegment.isDuplicate() &&
|
||||
mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled()))
|
||||
{
|
||||
mNextAudioSegment = null;
|
||||
|
||||
if(mCurrentAudioSegment == null)
|
||||
{
|
||||
mEmptyProperty.set(true);
|
||||
}
|
||||
}
|
||||
//For linked audio segments, allow the linked segment to complete first before assigning the next
|
||||
else if(mNextAudioSegment.isLinked())
|
||||
{
|
||||
if(mCurrentAudioSegment == null)
|
||||
{
|
||||
mCurrentAudioSegment = mNextAudioSegment;
|
||||
mNextAudioSegment = null;
|
||||
mCurrentBufferIndex = 0;
|
||||
|
||||
if(mCurrentAudioSegment != null)
|
||||
{
|
||||
mAudioPriority.bind(mCurrentAudioSegment.monitorPriorityProperty());
|
||||
mCurrentAudioSegment.addIdentifierUpdateNotificationListener(this);
|
||||
broadcast(mCurrentAudioSegment.getIdentifierCollection());
|
||||
}
|
||||
else
|
||||
{
|
||||
mAudioPriority.setValue(Priority.DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Insert audio segment start or audio priority preemption bonk tone
|
||||
if(mCurrentAudioSegment == null)
|
||||
{
|
||||
playAudio(getAudioSegmentStartTone());
|
||||
}
|
||||
else if(mCurrentBufferIndex > 0 &&
|
||||
(!mCurrentAudioSegment.completeProperty().get() ||
|
||||
mCurrentBufferIndex < mCurrentAudioSegment.getAudioBufferCount()))
|
||||
{
|
||||
playAudio(getAudioSegmentPreemptionTone());
|
||||
}
|
||||
else
|
||||
{
|
||||
playAudio(getAudioSegmentStartTone());
|
||||
}
|
||||
|
||||
//Close current audio segment
|
||||
dispose(mCurrentAudioSegment);
|
||||
mCurrentAudioSegment = mNextAudioSegment;
|
||||
mNextAudioSegment = null;
|
||||
mCurrentBufferIndex = 0;
|
||||
|
||||
if(mCurrentAudioSegment != null)
|
||||
{
|
||||
mAudioPriority.bind(mCurrentAudioSegment.monitorPriorityProperty());
|
||||
mCurrentAudioSegment.addIdentifierUpdateNotificationListener(this);
|
||||
broadcast(mCurrentAudioSegment.getIdentifierCollection());
|
||||
}
|
||||
else
|
||||
{
|
||||
mAudioPriority.setValue(Priority.DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
loadNextAudioSegment();
|
||||
}
|
||||
|
||||
if(mCurrentAudioSegment != null)
|
||||
//Evaluate current audio segment to see if the status has changed for duplicate or do-not-monitor.
|
||||
while(isThrowaway(mCurrentAudioSegment))
|
||||
{
|
||||
//Check for completed audio segment or a segment flagged as duplicate or Do Not Monitor
|
||||
if(mCurrentAudioSegment.isDoNotMonitor() ||
|
||||
(mCurrentAudioSegment.isDuplicate() &&
|
||||
mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled()) ||
|
||||
(mCurrentAudioSegment.completeProperty().get() && mCurrentBufferIndex >= mCurrentAudioSegment.getAudioBufferCount()))
|
||||
if(mCurrentBufferIndex > 0)
|
||||
{
|
||||
if(mCurrentAudioSegment.isDoNotMonitor())
|
||||
playAudio(mAudioSegmentDropTone);
|
||||
}
|
||||
|
||||
disposeCurrentAudioSegment();
|
||||
loadNextAudioSegment();
|
||||
}
|
||||
|
||||
while(mCurrentAudioSegment != null && mCurrentBufferIndex < mCurrentAudioSegment.getAudioBufferCount())
|
||||
{
|
||||
//Continuously evaluate current audio segment to see if the status has changed for duplicate or do-not-monitor.
|
||||
if(isThrowaway(mCurrentAudioSegment))
|
||||
{
|
||||
if(mCurrentBufferIndex > 0)
|
||||
{
|
||||
playAudio(getAudioSegmentDropTone());
|
||||
playAudio(mAudioSegmentDropTone);
|
||||
}
|
||||
|
||||
dispose(mCurrentAudioSegment);
|
||||
mCurrentAudioSegment = null;
|
||||
|
||||
mLock.lock();
|
||||
disposeCurrentAudioSegment();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mCurrentBufferIndex == 0)
|
||||
{
|
||||
playAudio(mAudioSegmentStartTone);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(mNextAudioSegment == null)
|
||||
float[] audioBuffer = mCurrentAudioSegment.getAudioBuffers().get(mCurrentBufferIndex++);
|
||||
|
||||
if(audioBuffer != null)
|
||||
{
|
||||
mEmptyProperty.set(true);
|
||||
ByteBuffer audio = convert(audioBuffer);
|
||||
//This call blocks until all audio bytes are dumped into the data line.
|
||||
playAudio(audio);
|
||||
}
|
||||
}
|
||||
finally
|
||||
catch(Exception e)
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//Process any new buffers that have been added to the audio segment. If a next audio segment gets assigned
|
||||
//while processing, exit the loop so that we can evaluate the next for higher priority preempt. If the next
|
||||
//segment is a linked segment, ignore it so that we can close out the current segment.
|
||||
while(mCurrentAudioSegment != null && (mNextAudioSegment == null || mNextAudioSegment.isLinked()) &&
|
||||
mCurrentBufferIndex < mCurrentAudioSegment.getAudioBufferCount() &&
|
||||
!mCurrentAudioSegment.isDoNotMonitor() && !(mCurrentAudioSegment.isDuplicate() &&
|
||||
mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled()))
|
||||
{
|
||||
float[] audioBuffer = mCurrentAudioSegment.getAudioBuffers().get(mCurrentBufferIndex++);
|
||||
|
||||
if(audioBuffer != null)
|
||||
{
|
||||
ByteBuffer audio = convert(audioBuffer);
|
||||
playAudio(audio);
|
||||
mLog.error("Error while processing audio for [" + mMixerChannel.name() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check for completed and fully-played audio segment to closeout
|
||||
if(mCurrentAudioSegment != null &&
|
||||
mCurrentAudioSegment.isComplete() &&
|
||||
(mCurrentBufferIndex >= mCurrentAudioSegment.getAudioBufferCount()))
|
||||
{
|
||||
disposeCurrentAudioSegment();
|
||||
}
|
||||
|
||||
checkStop();
|
||||
}
|
||||
|
||||
@@ -523,25 +449,7 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
}
|
||||
|
||||
mProcessorFuture = null;
|
||||
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
if(mNextAudioSegment != null)
|
||||
{
|
||||
mNextAudioSegment.decrementConsumerCount();
|
||||
mNextAudioSegment = null;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
dispose(mCurrentAudioSegment);
|
||||
mCurrentAudioSegment = null;
|
||||
|
||||
disposeCurrentAudioSegment();
|
||||
mAudioEventBroadcaster.clear();
|
||||
mIdentifierCollectionListener = null;
|
||||
|
||||
@@ -650,14 +558,28 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
if(mRunning)
|
||||
{
|
||||
//If the output buffer falls below the threshold then drain the output and stop playback
|
||||
if(mOutput.isRunning() && mOutput.available() >= mBufferStopThreshold)
|
||||
if(mOutput.isRunning())
|
||||
{
|
||||
mOutput.drain();
|
||||
mOutput.stop();
|
||||
mRunning = false;
|
||||
if(mOutput.available() >= mBufferStopThreshold)
|
||||
{
|
||||
mOutput.drain();
|
||||
mOutput.stop();
|
||||
mRunning = false;
|
||||
}
|
||||
//Detect when we have a partially filled output that will neither start nor stop and the last write
|
||||
//timestamp was more than the staleness threshold (500 ms) -- stop the output and discard stale contents
|
||||
else if(mOutput.available() < mBufferStopThreshold &&
|
||||
mOutput.available() > mBufferStartThreshold &&
|
||||
(System.currentTimeMillis() - mOutputLastTimestamp >= STALE_PLAYBACK_THRESHOLD_MS))
|
||||
{
|
||||
mOutput.stop();
|
||||
//Discard the stale buffer contents.
|
||||
mOutput.flush();
|
||||
mRunning = false;
|
||||
}
|
||||
}
|
||||
//If output playback stopped on its own because the buffer emptied, then cleanup
|
||||
else if(!mOutput.isRunning())
|
||||
else
|
||||
{
|
||||
mRunning = false;
|
||||
}
|
||||
@@ -735,16 +657,23 @@ public abstract class AudioOutput implements LineListener, Listener<IdentifierUp
|
||||
*/
|
||||
public class AudioSegmentProcessor implements Runnable
|
||||
{
|
||||
private AtomicBoolean mProcessing = new AtomicBoolean();
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
if(mProcessing.compareAndSet(false, true))
|
||||
{
|
||||
processAudio();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error while processing audio buffers", t);
|
||||
try
|
||||
{
|
||||
processAudio();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error while processing audio buffers", t);
|
||||
}
|
||||
|
||||
mProcessing.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.audio.playback;
|
||||
|
||||
@@ -24,6 +23,7 @@ import io.github.dsheirer.audio.AudioEvent;
|
||||
import io.github.dsheirer.audio.AudioException;
|
||||
import io.github.dsheirer.audio.AudioSegment;
|
||||
import io.github.dsheirer.audio.IAudioController;
|
||||
import io.github.dsheirer.controller.NamingThreadFactory;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
@@ -31,17 +31,18 @@ import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.mixer.MixerChannel;
|
||||
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Manages scheduling and playback of audio segments to the local users audio system.
|
||||
@@ -50,8 +51,6 @@ public class AudioPlaybackManager implements Listener<AudioSegment>, IAudioContr
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AudioPlaybackManager.class);
|
||||
|
||||
public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels";
|
||||
public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer";
|
||||
public static final AudioEvent CONFIGURATION_CHANGE_STARTED =
|
||||
new AudioEvent(AudioEvent.Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null);
|
||||
public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE =
|
||||
@@ -64,6 +63,8 @@ public class AudioPlaybackManager implements Listener<AudioSegment>, IAudioContr
|
||||
private List<AudioSegment> mAudioSegments = new ArrayList<>();
|
||||
private List<AudioSegment> mPendingAudioSegments = new ArrayList<>();
|
||||
private LinkedTransferQueue<AudioSegment> mNewAudioSegmentQueue = new LinkedTransferQueue<>();
|
||||
private ScheduledExecutorService mScheduledExecutorService =
|
||||
Executors.newSingleThreadScheduledExecutor(new NamingThreadFactory("sdrtrunk audio manager"));
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
@@ -202,29 +203,13 @@ public class AudioPlaybackManager implements Listener<AudioSegment>, IAudioContr
|
||||
//Sort audio segments by playback priority and assign to empty audio outputs
|
||||
if(!mAudioSegments.isEmpty())
|
||||
{
|
||||
//TODO: change this to take audio segment start time into account also
|
||||
mAudioSegments.sort(Comparator.comparingInt(o -> o.monitorPriorityProperty().get()));
|
||||
|
||||
//Assign empty audio outputs first
|
||||
for(AudioOutput audioOutput: mAudioOutputs)
|
||||
{
|
||||
if(audioOutput.emptyProperty().get())
|
||||
{
|
||||
audioOutput.play(mAudioSegments.remove(0));
|
||||
|
||||
if(mAudioSegments.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Preempt ongoing audio segment playback when higher priority audio segments are available
|
||||
if(!mAudioSegments.isEmpty())
|
||||
{
|
||||
for(AudioOutput audioOutput: mAudioOutputs)
|
||||
{
|
||||
if(mAudioSegments.get(0).monitorPriorityProperty().get() < audioOutput.audioPriorityProperty().get())
|
||||
if(audioOutput.isEmpty())
|
||||
{
|
||||
audioOutput.play(mAudioSegments.remove(0));
|
||||
|
||||
@@ -284,7 +269,7 @@ public class AudioPlaybackManager implements Listener<AudioSegment>, IAudioContr
|
||||
}
|
||||
catch(AudioException ae)
|
||||
{
|
||||
mLog.error("Error changing audio output to [" + configuration.toString() + "]", ae);
|
||||
mLog.error("Error changing audio output to [" + configuration + "]", ae);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -332,7 +317,7 @@ public class AudioPlaybackManager implements Listener<AudioSegment>, IAudioContr
|
||||
throw new AudioException("Unsupported mixer channel configuration: " + entry.getMixerChannel());
|
||||
}
|
||||
|
||||
mProcessingTask = ThreadPool.SCHEDULED.scheduleAtFixedRate(new AudioSegmentProcessor(),
|
||||
mProcessingTask = mScheduledExecutorService.scheduleAtFixedRate(new AudioSegmentProcessor(),
|
||||
0, 100, TimeUnit.MILLISECONDS);
|
||||
mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_COMPLETE);
|
||||
mMixerChannelConfiguration = entry;
|
||||
@@ -343,7 +328,7 @@ public class AudioPlaybackManager implements Listener<AudioSegment>, IAudioContr
|
||||
* Current audio playback mixer channel configuration setting.
|
||||
*/
|
||||
@Override
|
||||
public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException
|
||||
public MixerChannelConfiguration getMixerChannelConfiguration()
|
||||
{
|
||||
return mMixerChannelConfiguration;
|
||||
}
|
||||
@@ -384,16 +369,23 @@ public class AudioPlaybackManager implements Listener<AudioSegment>, IAudioContr
|
||||
*/
|
||||
public class AudioSegmentProcessor implements Runnable
|
||||
{
|
||||
private AtomicBoolean mProcessing = new AtomicBoolean();
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
if(mProcessing.compareAndSet(false, true))
|
||||
{
|
||||
processAudioSegments();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Encountered error while processing audio segments", t);
|
||||
try
|
||||
{
|
||||
processAudioSegments();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Encountered error while processing audio segments", t);
|
||||
}
|
||||
|
||||
mProcessing.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ public class BinaryMessage extends BitSet
|
||||
private static final int[] CHARACTER_7_BIT = new int[]{0, 1, 2, 3, 4, 5, 6};
|
||||
private static final int[] CHARACTER_8_BIT = new int[]{0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final String UTF_8 = "UTF-8";
|
||||
private static final String GB2312 = "GB2312";
|
||||
|
||||
|
||||
/**
|
||||
* Logical (ie constructed) size of this bitset, despite the actual size of
|
||||
@@ -274,8 +276,7 @@ public class BinaryMessage extends BitSet
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BitSetFullException("bitset is full -- contains " +
|
||||
(mPointer + 1) + "bits");
|
||||
throw new BitSetFullException("bitset is full -- contains [" + mPointer + "/" + size() + "] bits");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,6 +1237,26 @@ public class BinaryMessage extends BitSet
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse GB2312 Simplified Chinese Characters from the message starting at the offset.
|
||||
* @param offset where to start parsing.
|
||||
* @param characterCount number of characters to parse
|
||||
* @return parsed message.
|
||||
* @throws UnsupportedEncodingException if GB2312 encoding is not supported.
|
||||
*/
|
||||
public String parseGB2312(int offset, int characterCount) throws UnsupportedEncodingException
|
||||
{
|
||||
int length = characterCount * 2;
|
||||
byte[] bytes = new byte[length];
|
||||
|
||||
for(int x = 0; x < length; x++)
|
||||
{
|
||||
bytes[x] = getByte(CHARACTER_8_BIT, x * 8 + offset);
|
||||
}
|
||||
|
||||
return new String(bytes, GB2312);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 16-bit unicode characters from the message starting at the offset.
|
||||
* @param offset where to start parsing.
|
||||
|
||||
@@ -132,12 +132,10 @@ public enum SyncPattern
|
||||
true, true, false, false //1100 0xC
|
||||
}),
|
||||
|
||||
/* Sync (0x158) = 1101011000 */
|
||||
/* Sync (0x158) = 101011000 */
|
||||
PASSPORT( new boolean[]
|
||||
{
|
||||
true, //0001 0x1
|
||||
false, true, false, true, //0101 0x5
|
||||
true, false, false, false //1000 0x8
|
||||
true,false,true,false,true,true,false,false,false
|
||||
} ),
|
||||
|
||||
LTR_STANDARD_OSW( new boolean[]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -84,7 +84,7 @@ public class DcCorrectionManager
|
||||
{
|
||||
mDcCalculationsRemaining--;
|
||||
}
|
||||
//
|
||||
|
||||
// mLog.info("DC: " + mDecimalFormat.format(mAverageDc) +
|
||||
// " RES:" + mDecimalFormat.format(residualOffsetToRemove) +
|
||||
// " REM:" + mDcCalculationsRemaining);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -21,21 +21,16 @@ package io.github.dsheirer.buffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Circular sample buffer - allocates a buffer and stores samples in a circular
|
||||
* fashion, overwriting older samples with newly arrived samples. Initially
|
||||
* fills buffer with 0-valued samples
|
||||
*
|
||||
* Can be used as a delay-type buffer, to delay samples by the 'size' amount
|
||||
* Circular sample buffer - allocates a buffer and stores samples in a circular fashion, overwriting older samples with
|
||||
* newly arrived samples. Initially fills buffer with 0-valued (default) samples or with a specified value.
|
||||
*/
|
||||
public class FloatCircularBuffer
|
||||
{
|
||||
private float[] mBuffer;
|
||||
private int mBufferPointer = 0;
|
||||
private float mOldestSample;
|
||||
|
||||
/**
|
||||
* Creates a circular double buffer of the specified size and all entries filled with the specified initial value.
|
||||
*
|
||||
* @param size of the buffer
|
||||
* @param initialFillValue to fill the buffer entries
|
||||
*/
|
||||
@@ -47,7 +42,6 @@ public class FloatCircularBuffer
|
||||
|
||||
/**
|
||||
* Creates a circular double buffer of the specified size and all entries filled with an initial value of zero.
|
||||
*
|
||||
* @param size of the buffer
|
||||
*/
|
||||
public FloatCircularBuffer(int size)
|
||||
@@ -55,33 +49,34 @@ public class FloatCircularBuffer
|
||||
this(size, 0.0f);
|
||||
}
|
||||
|
||||
public float[] getBuffer()
|
||||
/**
|
||||
* Resets the delay buffer to an all zeros state
|
||||
*/
|
||||
public void reset(float fillValue)
|
||||
{
|
||||
return mBuffer;
|
||||
Arrays.fill(mBuffer, fillValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current buffer value and overwrites that value position in the buffer with the new value.
|
||||
* Puts the new value into the buffer and returns the oldest buffer value that it replaced
|
||||
*
|
||||
* @param newValue to add to the buffer
|
||||
* @return oldest value that was overwritten by the new value
|
||||
*/
|
||||
public float getAndPut(float newValue)
|
||||
public float get(float newValue)
|
||||
{
|
||||
mOldestSample = mBuffer[mBufferPointer];
|
||||
put(newValue);
|
||||
return mOldestSample;
|
||||
}
|
||||
float oldestSample = mBuffer[mBufferPointer];
|
||||
|
||||
/**
|
||||
* Puts the value into the buffer, updates the pointer and returns the buffer value at the pointer position.
|
||||
* @param sample to add
|
||||
* @return next pointed sample.
|
||||
*/
|
||||
public float putAndGet(float sample)
|
||||
{
|
||||
put(sample);
|
||||
return mBuffer[mBufferPointer];
|
||||
mBuffer[mBufferPointer] = newValue;
|
||||
|
||||
mBufferPointer++;
|
||||
|
||||
if(mBufferPointer >= mBuffer.length)
|
||||
{
|
||||
mBufferPointer = 0;
|
||||
}
|
||||
|
||||
return oldestSample;
|
||||
}
|
||||
|
||||
public float[] getAll()
|
||||
@@ -105,13 +100,70 @@ public class FloatCircularBuffer
|
||||
|
||||
/**
|
||||
* Places the new value into the buffer, overwriting the oldest value
|
||||
*
|
||||
* @param newValue to add to the buffer
|
||||
*/
|
||||
public void put(float newValue)
|
||||
{
|
||||
mBuffer[mBufferPointer] = newValue;
|
||||
|
||||
mBufferPointer++;
|
||||
mBufferPointer %= mBuffer.length;
|
||||
|
||||
if(mBufferPointer >= mBuffer.length)
|
||||
{
|
||||
mBufferPointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum from the values currently in the buffer
|
||||
*/
|
||||
public float max(float referenceValue)
|
||||
{
|
||||
float max = referenceValue;
|
||||
|
||||
for(int x = 0; x < mBuffer.length; x++)
|
||||
{
|
||||
if(mBuffer[x] > max)
|
||||
{
|
||||
max = mBuffer[x];
|
||||
}
|
||||
}
|
||||
|
||||
// System.out.println("Max [" + max + "] from " + Arrays.toString(mBuffer));
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum from the values currently in the buffer
|
||||
*/
|
||||
public float min()
|
||||
{
|
||||
float min = 0.0f;
|
||||
|
||||
for(int x = 0; x < mBuffer.length; x++)
|
||||
{
|
||||
if(mBuffer[x] < min)
|
||||
{
|
||||
min = mBuffer[x];
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the average/mean of the values contained in this buffer
|
||||
*/
|
||||
public float mean()
|
||||
{
|
||||
double accumulator = 0.0;
|
||||
|
||||
for(float value: mBuffer)
|
||||
{
|
||||
accumulator += value;
|
||||
}
|
||||
|
||||
return (float)(accumulator / mBuffer.length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ package io.github.dsheirer.buffer;
|
||||
import io.github.dsheirer.sample.SampleUtils;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
@@ -30,6 +32,7 @@ import java.util.Iterator;
|
||||
public class FloatNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private float[] mInterleavedComplexSamples;
|
||||
private static final int BUFFER_SIZE = 2048;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -81,48 +84,51 @@ public class FloatNativeBuffer extends AbstractNativeBuffer
|
||||
|
||||
private class ComplexSamplesIterator implements Iterator<ComplexSamples>
|
||||
{
|
||||
private boolean mEmpty;
|
||||
private int mBufferPointer = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return !mEmpty;
|
||||
return mBufferPointer < mInterleavedComplexSamples.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
if(mEmpty)
|
||||
if(!hasNext())
|
||||
{
|
||||
throw new IllegalStateException("No more samples");
|
||||
}
|
||||
|
||||
mEmpty = true;
|
||||
return SampleUtils.deinterleave(mInterleavedComplexSamples, getTimestamp());
|
||||
float[] chunk = Arrays.copyOfRange(mInterleavedComplexSamples, mBufferPointer, mBufferPointer + BUFFER_SIZE * 2);
|
||||
mBufferPointer += BUFFER_SIZE * 2;
|
||||
|
||||
return SampleUtils.deinterleave(chunk, getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
private class InterleavedComplexSamplesIterator implements Iterator<InterleavedComplexSamples>
|
||||
{
|
||||
private boolean mEmpty;
|
||||
|
||||
private int mBufferPointer = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return !mEmpty;
|
||||
return mBufferPointer < mInterleavedComplexSamples.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
if(mEmpty)
|
||||
if(!hasNext())
|
||||
{
|
||||
throw new IllegalStateException("No more samples");
|
||||
}
|
||||
|
||||
mEmpty = true;
|
||||
return new InterleavedComplexSamples(mInterleavedComplexSamples, getTimestamp());
|
||||
float[] chunk = Arrays.copyOfRange(mInterleavedComplexSamples, mBufferPointer, mBufferPointer + BUFFER_SIZE * 2);
|
||||
mBufferPointer += BUFFER_SIZE * 2;
|
||||
|
||||
return new InterleavedComplexSamples(chunk, getTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.buffer;
|
||||
|
||||
/**
|
||||
* Circular sample buffer - allocates a buffer and stores samples in a circular
|
||||
* fashion, overwriting older samples with newly arrived samples.
|
||||
*
|
||||
* Can be used as a delay-type buffer, to delay samples by the 'size' amount
|
||||
*/
|
||||
public class RealCircularBuffer
|
||||
{
|
||||
float[] mBuffer;
|
||||
int mBufferPointer = 0;
|
||||
|
||||
public RealCircularBuffer(int size)
|
||||
{
|
||||
mBuffer = new float[size];
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return mBuffer.length;
|
||||
}
|
||||
|
||||
public float[] getBuffer()
|
||||
{
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the new value into the buffer and returns the oldest buffer value
|
||||
* that it replaced
|
||||
*
|
||||
* @param newValue
|
||||
* @return
|
||||
*/
|
||||
public float putAndGet(float newValue)
|
||||
{
|
||||
float oldestSample = mBuffer[mBufferPointer];
|
||||
|
||||
put(newValue);
|
||||
|
||||
return oldestSample;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the new value to this buffer.
|
||||
*/
|
||||
public void put(float newValue)
|
||||
{
|
||||
mBuffer[mBufferPointer] = newValue;
|
||||
|
||||
mBufferPointer++;
|
||||
|
||||
if(mBufferPointer >= mBuffer.length)
|
||||
{
|
||||
mBufferPointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sample at the specified index, where index 0 is the oldest sample and index (length - 1) is the
|
||||
* newest sample.
|
||||
* @param index in range of 0 to length-1
|
||||
* @return value at indexed position
|
||||
*/
|
||||
|
||||
public float get(int index)
|
||||
{
|
||||
if(index < mBuffer.length)
|
||||
{
|
||||
int pointer = mBufferPointer + index;
|
||||
|
||||
if(pointer >= mBuffer.length)
|
||||
{
|
||||
pointer -= mBuffer.length;
|
||||
}
|
||||
|
||||
return mBuffer[pointer];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArrayIndexOutOfBoundsException("Index [" + index + "] is not valid for the buffer size of [" +
|
||||
mBuffer.length + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of the circular buffer unwrapped where index 0 is the oldest sample and index length-1
|
||||
* is the newest sample.
|
||||
*
|
||||
* @return array of samples
|
||||
*/
|
||||
public float[] get()
|
||||
{
|
||||
float[] samples = new float[mBuffer.length];
|
||||
|
||||
System.arraycopy(mBuffer, mBufferPointer, samples, 0, mBuffer.length - mBufferPointer);
|
||||
|
||||
if(mBufferPointer != 0)
|
||||
{
|
||||
System.arraycopy(mBuffer, 0, samples, mBuffer.length - mBufferPointer, mBufferPointer);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.hf;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.hf;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -35,11 +35,11 @@ import javax.swing.JPanel;
|
||||
|
||||
public class NowPlayingPanel extends JPanel
|
||||
{
|
||||
private ChannelMetadataPanel mChannelMetadataPanel;
|
||||
private ChannelDetailPanel mChannelDetailPanel;
|
||||
private DecodeEventPanel mDecodeEventPanel;
|
||||
private MessageActivityPanel mMessageActivityPanel;
|
||||
private ChannelPowerPanel mChannelPowerPanel;
|
||||
private final ChannelMetadataPanel mChannelMetadataPanel;
|
||||
private final ChannelDetailPanel mChannelDetailPanel;
|
||||
private final DecodeEventPanel mDecodeEventPanel;
|
||||
private final MessageActivityPanel mMessageActivityPanel;
|
||||
private final ChannelPowerPanel mChannelPowerPanel;
|
||||
|
||||
/**
|
||||
* GUI panel that combines the currently decoding channels metadata table and viewers for channel details,
|
||||
|
||||
@@ -73,7 +73,7 @@ public class DecoderStateEvent
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Decoder State Event - source[").append(mSource.getClass())
|
||||
sb.append("Decoder State Event - source[").append(mSource != null ? mSource.getClass() : "null")
|
||||
.append("] event[").append(mEvent)
|
||||
.append("] state[").append(mState)
|
||||
.append("] timeslot[").append(mTimeslot)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,22 +14,13 @@
|
||||
*
|
||||
* 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.controller.channel;
|
||||
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
@@ -41,12 +31,21 @@ import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
|
||||
public class ChannelAutoStartFrame extends JFrame
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelAutoStartFrame.class);
|
||||
|
||||
private static final int COUNTDOWN_SECONDS = 10;
|
||||
private Listener<ChannelEvent> mChannelEventListener;
|
||||
private List<Channel> mChannels;
|
||||
|
||||
@@ -55,7 +54,7 @@ public class ChannelAutoStartFrame extends JFrame
|
||||
private JButton mCancelButton;
|
||||
private JTable mChannelTable;
|
||||
private AtomicBoolean mChannelsStarted = new AtomicBoolean();
|
||||
|
||||
private int mAutoStartTimeoutSeconds;
|
||||
private ScheduledFuture<?> mTimerFuture;
|
||||
|
||||
/**
|
||||
@@ -66,10 +65,12 @@ public class ChannelAutoStartFrame extends JFrame
|
||||
* @param listener to receive channel start/enable request(s)
|
||||
* @param channels to auto-start
|
||||
*/
|
||||
public ChannelAutoStartFrame(Listener<ChannelEvent> listener, List<Channel> channels)
|
||||
public ChannelAutoStartFrame(Listener<ChannelEvent> listener, List<Channel> channels, UserPreferences userPreferences)
|
||||
{
|
||||
mChannelEventListener = listener;
|
||||
mChannels = channels;
|
||||
mAutoStartTimeoutSeconds = userPreferences.getApplicationPreference().getChannelAutoStartTimeout();
|
||||
|
||||
init();
|
||||
|
||||
EventQueue.invokeLater(() -> {
|
||||
@@ -100,7 +101,7 @@ public class ChannelAutoStartFrame extends JFrame
|
||||
"[][][grow,fill][]"));
|
||||
panel.add(new JLabel("The following channels will be automatically"), "span");
|
||||
|
||||
mCountdownLabel = new JLabel(getCountdownText(COUNTDOWN_SECONDS));
|
||||
mCountdownLabel = new JLabel(getCountdownText(mAutoStartTimeoutSeconds));
|
||||
panel.add(mCountdownLabel, "span");
|
||||
|
||||
panel.add(new JScrollPane(getChannelTable()), "span");
|
||||
@@ -220,7 +221,7 @@ public class ChannelAutoStartFrame extends JFrame
|
||||
*/
|
||||
public class CountdownTimer implements Runnable
|
||||
{
|
||||
private int mCount = COUNTDOWN_SECONDS;
|
||||
private int mCount = mAutoStartTimeoutSeconds;
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -623,30 +623,27 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
Platform.runLater(() -> channel.setProcessing(false));
|
||||
}
|
||||
|
||||
//Since the processing chain's source will block on stop(), throw it to the thread pool to run
|
||||
ThreadPool.CACHED.submit(() -> {
|
||||
try
|
||||
{
|
||||
processingChain.stop();
|
||||
processingChain.removeEventLoggingModules();
|
||||
processingChain.removeRecordingModules();
|
||||
try
|
||||
{
|
||||
processingChain.stop();
|
||||
processingChain.removeEventLoggingModules();
|
||||
processingChain.removeRecordingModules();
|
||||
|
||||
//Deregister channel from receive frequency correction events to show in the spectral display (hack!)
|
||||
processingChain.removeFrequencyChangeListener(channel);
|
||||
channel.resetFrequencyCorrection();
|
||||
//Deregister channel from receive frequency correction events to show in the spectral display (hack!)
|
||||
processingChain.removeFrequencyChangeListener(channel);
|
||||
channel.resetFrequencyCorrection();
|
||||
|
||||
mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_STOP));
|
||||
mChannelEventBroadcaster.removeListener(processingChain);
|
||||
mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_STOP));
|
||||
mChannelEventBroadcaster.removeListener(processingChain);
|
||||
|
||||
//Unregister for event bus requests and notifications
|
||||
processingChain.getEventBus().unregister(ChannelProcessingManager.this);
|
||||
processingChain.dispose();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error during shutdown of processing chain for channel [" + channel.getName() + "}", e);
|
||||
}
|
||||
});
|
||||
//Unregister for event bus requests and notifications
|
||||
processingChain.getEventBus().unregister(ChannelProcessingManager.this);
|
||||
processingChain.dispose();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error during shutdown of processing chain for channel [" + channel.getName() + "}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.am;
|
||||
|
||||
import io.github.dsheirer.dsp.magnitude.IMagnitudeCalculator;
|
||||
import io.github.dsheirer.dsp.magnitude.MagnitudeFactory;
|
||||
import io.github.dsheirer.vector.calibrate.Calibration;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationException;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationType;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import org.apache.commons.math3.stat.descriptive.moment.Mean;
|
||||
|
||||
/**
|
||||
* Calibrates AM demodulator options
|
||||
*/
|
||||
public class AmDemodulatorCalibration extends Calibration
|
||||
{
|
||||
private static final int BUFFER_SIZE = 2048;
|
||||
private static final int ITERATION_DURATION_MS = 1000;
|
||||
private static final int WARMUP_ITERATIONS = 5;
|
||||
private static final int TEST_ITERATIONS = 5;
|
||||
private IMagnitudeCalculator mMagnitudeCalculator = MagnitudeFactory.getMagnitudeCalculator();
|
||||
private IAmDemodulator mScalarDemodulator = new ScalarAMDemodulator(500.0f);
|
||||
private IAmDemodulator mVectorDemodulator64 = new VectorAMDemodulator64(500.0f);
|
||||
private IAmDemodulator mVectorDemodulator128 = new VectorAMDemodulator128(500.0f);
|
||||
private IAmDemodulator mVectorAMDemodulator256 = new VectorAMDemodulator256(500.0f);
|
||||
private IAmDemodulator mVectorAMDemodulator512 = new VectorAMDemodulator512(500.0f);
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public AmDemodulatorCalibration()
|
||||
{
|
||||
super(CalibrationType.AM_DEMODULATOR);
|
||||
}
|
||||
|
||||
@Override public void calibrate() throws CalibrationException
|
||||
{
|
||||
float[] magnitude = getFloatSamples(BUFFER_SIZE);
|
||||
|
||||
Mean scalarMean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = testScalar(magnitude);
|
||||
scalarMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
|
||||
|
||||
Mean vector64Mean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = testVector64(magnitude);
|
||||
vector64Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vector64Mean.getResult()));
|
||||
|
||||
Mean vector128Mean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = testVector128(magnitude);
|
||||
vector128Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vector128Mean.getResult()));
|
||||
|
||||
Mean vector256Mean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = testVector256(magnitude);
|
||||
vector256Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vector256Mean.getResult()));
|
||||
|
||||
Mean vector512Mean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = testVector512(magnitude);
|
||||
vector512Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vector512Mean.getResult()));
|
||||
|
||||
//Start tests
|
||||
scalarMean.clear();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = testScalar(magnitude);
|
||||
scalarMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
|
||||
|
||||
vector64Mean.clear();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = testVector64(magnitude);
|
||||
vector64Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR - VECTOR 64: " + DECIMAL_FORMAT.format(vector64Mean.getResult()));
|
||||
|
||||
vector128Mean.clear();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = testVector128(magnitude);
|
||||
vector128Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR - VECTOR 128: " + DECIMAL_FORMAT.format(vector128Mean.getResult()));
|
||||
|
||||
vector256Mean.clear();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = testVector256(magnitude);
|
||||
vector256Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR - VECTOR 256: " + DECIMAL_FORMAT.format(vector256Mean.getResult()));
|
||||
|
||||
vector512Mean.clear();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = testVector512(magnitude);
|
||||
vector512Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("AM DEMODULATOR - VECTOR 512: " + DECIMAL_FORMAT.format(vector512Mean.getResult()));
|
||||
|
||||
Implementation optimal = Implementation.SCALAR;
|
||||
double optimalResult = scalarMean.getResult();
|
||||
|
||||
if(vector64Mean.getResult() > optimalResult)
|
||||
{
|
||||
optimal = Implementation.VECTOR_SIMD_64;
|
||||
optimalResult = vector64Mean.getResult();
|
||||
}
|
||||
|
||||
if(vector128Mean.getResult() > optimalResult)
|
||||
{
|
||||
optimal = Implementation.VECTOR_SIMD_128;
|
||||
optimalResult = vector128Mean.getResult();
|
||||
}
|
||||
|
||||
if(vector256Mean.getResult() > optimalResult)
|
||||
{
|
||||
optimal = Implementation.VECTOR_SIMD_256;
|
||||
optimalResult = vector256Mean.getResult();
|
||||
}
|
||||
|
||||
if(vector512Mean.getResult() > optimalResult)
|
||||
{
|
||||
optimal = Implementation.VECTOR_SIMD_512;
|
||||
}
|
||||
|
||||
setImplementation(optimal);
|
||||
|
||||
mLog.info("AM DEMODULATOR - SET OPTIMAL IMPLEMENTATION TO:" + getImplementation());
|
||||
}
|
||||
|
||||
private long testScalar(float[] magnitude)
|
||||
{
|
||||
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
float[] demodulated = mScalarDemodulator.demodulateMagnitude(magnitude);
|
||||
accumulator += demodulated[1];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector64(float[] magnitude)
|
||||
{
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
float[] demodulated = mVectorDemodulator64.demodulateMagnitude(magnitude);
|
||||
accumulator += demodulated[1];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector128(float[] magnitude)
|
||||
{
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
float[] demodulated = mVectorDemodulator128.demodulateMagnitude(magnitude);
|
||||
accumulator += demodulated[1];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector256(float[] magnitude)
|
||||
{
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
float[] demodulated = mVectorAMDemodulator256.demodulateMagnitude(magnitude);
|
||||
accumulator += demodulated[1];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector512(float[] magnitude)
|
||||
{
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
float[] demodulated = mVectorAMDemodulator512.demodulateMagnitude(magnitude);
|
||||
accumulator += demodulated[1];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
AmDemodulatorCalibration calibration = new AmDemodulatorCalibration();
|
||||
|
||||
try
|
||||
{
|
||||
calibration.calibrate();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error during calibration", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.am;
|
||||
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationManager;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationType;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Factory for creating AM demodulators
|
||||
*/
|
||||
public class AmDemodulatorFactory
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AmDemodulatorFactory.class);
|
||||
|
||||
/**
|
||||
* Creates the optimal AM demodulator using calibration data to select the optimal
|
||||
* implementation from scalar and vector options.
|
||||
* @return demodulator instance
|
||||
*/
|
||||
public static IAmDemodulator getAmDemodulator(float gain)
|
||||
{
|
||||
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.AM_DEMODULATOR);
|
||||
|
||||
switch(implementation)
|
||||
{
|
||||
case VECTOR_SIMD_64:
|
||||
return new VectorAMDemodulator64(gain);
|
||||
case VECTOR_SIMD_128:
|
||||
return new VectorAMDemodulator128(gain);
|
||||
case VECTOR_SIMD_256:
|
||||
return new VectorAMDemodulator256(gain);
|
||||
case VECTOR_SIMD_512:
|
||||
return new VectorAMDemodulator512(gain);
|
||||
case VECTOR_SIMD_PREFERRED:
|
||||
return new VectorAMDemodulator64(gain);
|
||||
case SCALAR:
|
||||
case UNCALIBRATED:
|
||||
return new ScalarAMDemodulator(gain);
|
||||
default:
|
||||
mLog.warn("Unrecognized optimal operation for AM demodulator: " + implementation.name());
|
||||
return new ScalarAMDemodulator(gain);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.am;
|
||||
|
||||
/**
|
||||
@@ -6,10 +25,9 @@ package io.github.dsheirer.dsp.am;
|
||||
public interface IAmDemodulator
|
||||
{
|
||||
/**
|
||||
* Demodulate the complex I & Q sample arrays
|
||||
* @param i samples
|
||||
* @param q samples
|
||||
* @return demodulated samples.
|
||||
* Demodulates an array of vector squared magnitudes.
|
||||
* @param magnitude values that are squared.
|
||||
* @return demodulated samples
|
||||
*/
|
||||
float[] demodulate(float[] i, float[] q);
|
||||
float[] demodulateMagnitude(float[] magnitude);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -23,7 +23,7 @@ import org.apache.commons.math3.util.FastMath;
|
||||
/**
|
||||
* Performs AM demodulation on baseband I/Q samples to produce demodulated float output.
|
||||
*/
|
||||
public class AMDemodulator implements IAmDemodulator
|
||||
public class ScalarAMDemodulator implements IAmDemodulator
|
||||
{
|
||||
private float mGain;
|
||||
|
||||
@@ -32,7 +32,7 @@ public class AMDemodulator implements IAmDemodulator
|
||||
*
|
||||
* @param gain to apply to demodulated output samples.
|
||||
*/
|
||||
public AMDemodulator(float gain)
|
||||
public ScalarAMDemodulator(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
@@ -57,13 +57,13 @@ public class AMDemodulator implements IAmDemodulator
|
||||
return (float) FastMath.sqrt((inphase * inphase) + (quadrature * quadrature)) * mGain;
|
||||
}
|
||||
|
||||
@Override public float[] demodulate(float[] i, float[] q)
|
||||
@Override
|
||||
public float[] demodulateMagnitude(float[] magnitude)
|
||||
{
|
||||
float[] demodulated = new float[i.length];
|
||||
|
||||
for(int x = 0; x < i.length; x++)
|
||||
float[] demodulated = new float[magnitude.length];
|
||||
for(int x = 0; x < magnitude.length; x++)
|
||||
{
|
||||
demodulated[x] = (float)FastMath.sqrt((i[x] * i[x]) + (q[x] * q[x])) * mGain;
|
||||
demodulated[x] = (float)FastMath.sqrt(magnitude[x]) * mGain;
|
||||
}
|
||||
|
||||
return demodulated;
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.am;
|
||||
|
||||
import io.github.dsheirer.dsp.fm.ISquelchingDemodulator;
|
||||
import io.github.dsheirer.dsp.magnitude.IMagnitudeCalculator;
|
||||
import io.github.dsheirer.dsp.magnitude.MagnitudeFactory;
|
||||
import io.github.dsheirer.dsp.squelch.AdaptiveSquelch;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
|
||||
/**
|
||||
* AM demodulator with integrated squelch control.
|
||||
*/
|
||||
public class SquelchingAMDemodulator implements ISquelchingDemodulator, Listener<SourceEvent>
|
||||
{
|
||||
private static final float ZERO = 0.0f;
|
||||
private final IMagnitudeCalculator mMagnitudeCalculator = MagnitudeFactory.getMagnitudeCalculator();
|
||||
private final IAmDemodulator mAmDemodulator;
|
||||
private final AdaptiveSquelch mAdaptiveSquelch;
|
||||
private boolean mSquelchChanged = false;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param gain to apply to the demodulated AM samples (e.g. 500.0f)
|
||||
* @param alpha decay value of the single pole IIR filter in range: 0.0 - 1.0. The smaller the alpha value,
|
||||
* the slower the squelch response.
|
||||
* @param squelchThreshold in decibels. Signal power must exceed this threshold value for unsquelch.
|
||||
* @param squelchAutoTrack to enable the squelch noise floor auto tracking feature.
|
||||
*/
|
||||
public SquelchingAMDemodulator(float gain, float alpha, float squelchThreshold, boolean squelchAutoTrack)
|
||||
{
|
||||
mAmDemodulator = AmDemodulatorFactory.getAmDemodulator(gain);
|
||||
mAdaptiveSquelch = new AdaptiveSquelch(alpha, squelchThreshold, squelchAutoTrack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or update the sample rate for the squelch to adjust the power level notification rate.
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
public void setSampleRate(int sampleRate)
|
||||
{
|
||||
mAdaptiveSquelch.setSampleRate(sampleRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive notifications of squelch change events from the power squelch.
|
||||
*/
|
||||
public void setSourceEventListener(Listener<SourceEvent> listener)
|
||||
{
|
||||
mAdaptiveSquelch.setSourceEventListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Demodulates the complex sample arrays.
|
||||
* @param i inphase sample array
|
||||
* @param q quadrature sample array
|
||||
* @return demodulated AM samples with gain applied.
|
||||
*/
|
||||
public float[] demodulate(float[] i, float[] q)
|
||||
{
|
||||
mAdaptiveSquelch.setSquelchChanged(false);
|
||||
setSquelchChanged(false);
|
||||
|
||||
float[] magnitude = mMagnitudeCalculator.calculate(i, q);
|
||||
float[] demodulated = mAmDemodulator.demodulateMagnitude(magnitude);
|
||||
|
||||
for(int x = 0; x < i.length; x++)
|
||||
{
|
||||
mAdaptiveSquelch.process(magnitude[x]);
|
||||
|
||||
if(!(mAdaptiveSquelch.isUnmuted()))
|
||||
{
|
||||
demodulated[x] = ZERO;
|
||||
}
|
||||
|
||||
if(mAdaptiveSquelch.isSquelchChanged())
|
||||
{
|
||||
setSquelchChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
return demodulated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the threshold for squelch control
|
||||
* @param threshold (dB)
|
||||
*/
|
||||
public void setSquelchThreshold(float threshold)
|
||||
{
|
||||
mAdaptiveSquelch.setSquelchThreshold(threshold);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSquelchAutoTrack(boolean autoTrack)
|
||||
{
|
||||
mAdaptiveSquelch.setSquelchAutoTrack(autoTrack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state has changed during the processing of buffer(s)
|
||||
*/
|
||||
public boolean isSquelchChanged()
|
||||
{
|
||||
return mSquelchChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or resets the squelch changed flag.
|
||||
*/
|
||||
private void setSquelchChanged(boolean changed)
|
||||
{
|
||||
mSquelchChanged = changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state is currently muted
|
||||
*/
|
||||
public boolean isMuted()
|
||||
{
|
||||
return mAdaptiveSquelch.isMuted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process source events initiated by the timer and end-user.
|
||||
* @param sourceEvent to process.
|
||||
*/
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
switch(sourceEvent.getEvent())
|
||||
{
|
||||
//Only forward squelch threshold & auto-track request events
|
||||
case REQUEST_CURRENT_SQUELCH_THRESHOLD:
|
||||
case REQUEST_CHANGE_SQUELCH_THRESHOLD:
|
||||
case REQUEST_CURRENT_SQUELCH_AUTO_TRACK:
|
||||
case REQUEST_CHANGE_SQUELCH_AUTO_TRACK:
|
||||
{
|
||||
mAdaptiveSquelch.receive(sourceEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.am;
|
||||
|
||||
import io.github.dsheirer.dsp.magnitude.IMagnitudeCalculator;
|
||||
import io.github.dsheirer.dsp.magnitude.MagnitudeFactory;
|
||||
import io.github.dsheirer.vector.VectorUtilities;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Performs AM demodulation on baseband I/Q samples to produce demodulated float output.
|
||||
*/
|
||||
public class VectorAMDemodulator128 implements IAmDemodulator
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
|
||||
private float mGain;
|
||||
private IMagnitudeCalculator mMagnitudeCalculator = MagnitudeFactory.getMagnitudeCalculator();
|
||||
|
||||
/**
|
||||
* Constructs this demodulator where the specified gain is applied to demodulated output samples.
|
||||
*
|
||||
* @param gain to apply to demodulated output samples.
|
||||
*/
|
||||
public VectorAMDemodulator128(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the gain to the specified level.
|
||||
*/
|
||||
public void setGain(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Demodulates the comples I/Q sample and returns the demodulated output sample.
|
||||
*
|
||||
* @param inphase sample
|
||||
* @param quadrature sample
|
||||
* @return AM demodulated sample
|
||||
*/
|
||||
public float demodulate(float inphase, float quadrature)
|
||||
{
|
||||
return (float) FastMath.sqrt((inphase * inphase) + (quadrature * quadrature)) * mGain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] demodulateMagnitude(float[] magnitude)
|
||||
{
|
||||
VectorUtilities.checkArrayLength(magnitude, VECTOR_SPECIES);
|
||||
FloatVector gain = FloatVector.broadcast(VECTOR_SPECIES, mGain);
|
||||
float[] demodulated = new float[magnitude.length];
|
||||
|
||||
FloatVector vector;
|
||||
for(int x = 0; x < magnitude.length; x += VECTOR_SPECIES.length())
|
||||
{
|
||||
vector = FloatVector.fromArray(VECTOR_SPECIES, magnitude, x);
|
||||
vector.sqrt().mul(gain).intoArray(demodulated, x);
|
||||
}
|
||||
|
||||
return demodulated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.am;
|
||||
|
||||
import io.github.dsheirer.dsp.magnitude.IMagnitudeCalculator;
|
||||
import io.github.dsheirer.dsp.magnitude.MagnitudeFactory;
|
||||
import io.github.dsheirer.vector.VectorUtilities;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Performs AM demodulation on baseband I/Q samples to produce demodulated float output.
|
||||
*/
|
||||
public class VectorAMDemodulator256 implements IAmDemodulator
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
|
||||
private float mGain;
|
||||
private IMagnitudeCalculator mMagnitudeCalculator = MagnitudeFactory.getMagnitudeCalculator();
|
||||
|
||||
/**
|
||||
* Constructs this demodulator where the specified gain is applied to demodulated output samples.
|
||||
*
|
||||
* @param gain to apply to demodulated output samples.
|
||||
*/
|
||||
public VectorAMDemodulator256(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the gain to the specified level.
|
||||
*/
|
||||
public void setGain(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Demodulates the comples I/Q sample and returns the demodulated output sample.
|
||||
*
|
||||
* @param inphase sample
|
||||
* @param quadrature sample
|
||||
* @return AM demodulated sample
|
||||
*/
|
||||
public float demodulate(float inphase, float quadrature)
|
||||
{
|
||||
return (float) FastMath.sqrt((inphase * inphase) + (quadrature * quadrature)) * mGain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] demodulateMagnitude(float[] magnitude)
|
||||
{
|
||||
VectorUtilities.checkArrayLength(magnitude, VECTOR_SPECIES);
|
||||
FloatVector gain = FloatVector.broadcast(VECTOR_SPECIES, mGain);
|
||||
float[] demodulated = new float[magnitude.length];
|
||||
|
||||
FloatVector vector;
|
||||
for(int x = 0; x < magnitude.length; x += VECTOR_SPECIES.length())
|
||||
{
|
||||
vector = FloatVector.fromArray(VECTOR_SPECIES, magnitude, x);
|
||||
vector.sqrt().mul(gain).intoArray(demodulated, x);
|
||||
}
|
||||
|
||||
return demodulated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.am;
|
||||
|
||||
import io.github.dsheirer.dsp.magnitude.IMagnitudeCalculator;
|
||||
import io.github.dsheirer.dsp.magnitude.MagnitudeFactory;
|
||||
import io.github.dsheirer.vector.VectorUtilities;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Performs AM demodulation on baseband I/Q samples to produce demodulated float output.
|
||||
*/
|
||||
public class VectorAMDemodulator512 implements IAmDemodulator
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
|
||||
private float mGain;
|
||||
private IMagnitudeCalculator mMagnitudeCalculator = MagnitudeFactory.getMagnitudeCalculator();
|
||||
|
||||
/**
|
||||
* Constructs this demodulator where the specified gain is applied to demodulated output samples.
|
||||
*
|
||||
* @param gain to apply to demodulated output samples.
|
||||
*/
|
||||
public VectorAMDemodulator512(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the gain to the specified level.
|
||||
*/
|
||||
public void setGain(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Demodulates the comples I/Q sample and returns the demodulated output sample.
|
||||
*
|
||||
* @param inphase sample
|
||||
* @param quadrature sample
|
||||
* @return AM demodulated sample
|
||||
*/
|
||||
public float demodulate(float inphase, float quadrature)
|
||||
{
|
||||
return (float) FastMath.sqrt((inphase * inphase) + (quadrature * quadrature)) * mGain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] demodulateMagnitude(float[] magnitude)
|
||||
{
|
||||
VectorUtilities.checkArrayLength(magnitude, VECTOR_SPECIES);
|
||||
FloatVector gain = FloatVector.broadcast(VECTOR_SPECIES, mGain);
|
||||
float[] demodulated = new float[magnitude.length];
|
||||
|
||||
FloatVector vector;
|
||||
for(int x = 0; x < magnitude.length; x += VECTOR_SPECIES.length())
|
||||
{
|
||||
vector = FloatVector.fromArray(VECTOR_SPECIES, magnitude, x);
|
||||
vector.sqrt().mul(gain).intoArray(demodulated, x);
|
||||
}
|
||||
|
||||
return demodulated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.am;
|
||||
|
||||
import io.github.dsheirer.dsp.magnitude.IMagnitudeCalculator;
|
||||
import io.github.dsheirer.dsp.magnitude.MagnitudeFactory;
|
||||
import io.github.dsheirer.vector.VectorUtilities;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Performs AM demodulation on baseband I/Q samples to produce demodulated float output.
|
||||
*/
|
||||
public class VectorAMDemodulator64 implements IAmDemodulator
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
|
||||
private float mGain;
|
||||
private IMagnitudeCalculator mMagnitudeCalculator = MagnitudeFactory.getMagnitudeCalculator();
|
||||
|
||||
/**
|
||||
* Constructs this demodulator where the specified gain is applied to demodulated output samples.
|
||||
*
|
||||
* @param gain to apply to demodulated output samples.
|
||||
*/
|
||||
public VectorAMDemodulator64(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the gain to the specified level.
|
||||
*/
|
||||
public void setGain(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Demodulates the comples I/Q sample and returns the demodulated output sample.
|
||||
*
|
||||
* @param inphase sample
|
||||
* @param quadrature sample
|
||||
* @return AM demodulated sample
|
||||
*/
|
||||
public float demodulate(float inphase, float quadrature)
|
||||
{
|
||||
return (float) FastMath.sqrt((inphase * inphase) + (quadrature * quadrature)) * mGain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] demodulateMagnitude(float[] magnitude)
|
||||
{
|
||||
VectorUtilities.checkArrayLength(magnitude, VECTOR_SPECIES);
|
||||
FloatVector gain = FloatVector.broadcast(VECTOR_SPECIES, mGain);
|
||||
float[] demodulated = new float[magnitude.length];
|
||||
|
||||
FloatVector vector;
|
||||
for(int x = 0; x < magnitude.length; x += VECTOR_SPECIES.length())
|
||||
{
|
||||
vector = FloatVector.fromArray(VECTOR_SPECIES, magnitude, x);
|
||||
vector.sqrt().mul(gain).intoArray(demodulated, x);
|
||||
}
|
||||
|
||||
return demodulated;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -122,6 +122,11 @@ public abstract class AbstractComplexPolyphaseChannelizer implements Listener<In
|
||||
mChannels.add(polyphaseChannelSource);
|
||||
mSourceChangeBroadcaster.addListener(polyphaseChannelSource.getSourceEventListener());
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Error adding polyphase channel source - " + (polyphaseChannelSource == null ? "source is null" :
|
||||
"channel source is already added to this channelizer"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +141,11 @@ public abstract class AbstractComplexPolyphaseChannelizer implements Listener<In
|
||||
mChannels.remove(polyphaseChannelSource);
|
||||
mSourceChangeBroadcaster.removeListener(polyphaseChannelSource.getSourceEventListener());
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Error removing polyphase channel source - " + (polyphaseChannelSource == null ? "source is null" :
|
||||
"channel source was not previously added to this channelizer"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -24,7 +24,6 @@ import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import io.github.dsheirer.util.Dispatcher;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.jtransforms.fft.FloatFFT_1D;
|
||||
@@ -69,8 +68,8 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
|
||||
*/
|
||||
private static final int PROCESSED_CHANNEL_RESULTS_THRESHOLD = 1024;
|
||||
|
||||
//Sized at 152 buffers a second where max = 5 seconds
|
||||
private IFFTProcessorDispatcher mIFFTProcessorDispatcher = new IFFTProcessorDispatcher(5 * 152);
|
||||
//Sized to process 1/20th batch of 152 buffers a second, at 40 times per second
|
||||
private IFFTProcessorDispatcher mIFFTProcessorDispatcher = new IFFTProcessorDispatcher(152 / 20, 25);
|
||||
private FloatFFT_1D mFFT;
|
||||
private float[] mInlineSamples;
|
||||
private float[] mInlineFilter;
|
||||
@@ -407,25 +406,35 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
|
||||
*/
|
||||
public class IFFTProcessorDispatcher extends Dispatcher<List<float[]>>
|
||||
{
|
||||
public IFFTProcessorDispatcher(int maximumSize)
|
||||
public IFFTProcessorDispatcher(int batchSize, long interval)
|
||||
{
|
||||
super(maximumSize, "sdrtrunk polyphase ifft processor", Collections.emptyList());
|
||||
super("sdrtrunk polyphase ifft processor", batchSize, interval);
|
||||
|
||||
//We create a listener interface to receive the batched channel results arrays from the scheduled thread pool
|
||||
//dispatcher thread that is part of this continuous buffer processor. We perform an IFFT on each
|
||||
//channel results array contained in each results buffer and then dispatch the buffer
|
||||
//so that it can be distributed to each channel listener.
|
||||
setListener(list -> {
|
||||
List<float[]> processedChannelResults = new ArrayList<>();
|
||||
|
||||
for(float[] channelResults: list)
|
||||
try
|
||||
{
|
||||
//Rotate each of the channels to the correct phase using the IFFT
|
||||
mFFT.complexInverse(channelResults, true);
|
||||
processedChannelResults.add(channelResults);
|
||||
}
|
||||
List<float[]> processedChannelResults = new ArrayList<>();
|
||||
|
||||
dispatch(processedChannelResults);
|
||||
for(float[] channelResults: list)
|
||||
{
|
||||
if(channelResults != null)
|
||||
{
|
||||
//Rotate each of the channels to the correct phase using the IFFT
|
||||
mFFT.complexInverse(channelResults, true);
|
||||
processedChannelResults.add(channelResults);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(processedChannelResults);
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error during IFFT and dispatch of processed channel results", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,14 @@ package io.github.dsheirer.dsp.filter.channelizer;
|
||||
|
||||
import io.github.dsheirer.buffer.INativeBuffer;
|
||||
import io.github.dsheirer.buffer.INativeBufferProvider;
|
||||
import io.github.dsheirer.buffer.NativeBufferPoisonPill;
|
||||
import io.github.dsheirer.controller.channel.event.ChannelStopProcessingRequest;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.log.LoggingSuppressor;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import io.github.dsheirer.source.ISourceEventProcessor;
|
||||
import io.github.dsheirer.source.Source;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
@@ -44,7 +43,6 @@ import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -68,6 +66,7 @@ import org.slf4j.LoggerFactory;
|
||||
public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
{
|
||||
private static final DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
|
||||
private static final LoggingSuppressor LOGGING_SUPPRESSOR = new LoggingSuppressor(LoggerFactory.getLogger(PolyphaseChannelManager.class));
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelManager.class);
|
||||
private static final double MINIMUM_CHANNEL_BANDWIDTH = 25000.0;
|
||||
private static final double CHANNEL_OVERSAMPLING = 2.0;
|
||||
@@ -111,8 +110,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
}
|
||||
|
||||
mChannelCalculator = new ChannelCalculator(sampleRate, channelCount, frequency, CHANNEL_OVERSAMPLING);
|
||||
mBufferDispatcher = new Dispatcher(500, "sdrtrunk polyphase buffer processor",
|
||||
new NativeBufferPoisonPill());
|
||||
mBufferDispatcher = new Dispatcher("sdrtrunk polyphase buffer processor", 50, 10);
|
||||
mBufferDispatcher.setListener(mNativeBufferReceiver);
|
||||
}
|
||||
|
||||
@@ -178,14 +176,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Current channel sample rate which is (2 * channel bandwidth).
|
||||
*/
|
||||
public double getChannelSampleRate()
|
||||
{
|
||||
return mChannelCalculator.getChannelSampleRate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Current channel bandwidth/spacing.
|
||||
*/
|
||||
@@ -215,7 +205,8 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
{
|
||||
mLog.debug("Couldn't design final output low pass filter for polyphase channel source");
|
||||
LOGGING_SUPPRESSOR.error(iae.getMessage(), 3, "Couldn't allocate channel. " + iae.getMessage());
|
||||
channelSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,9 +431,10 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
}
|
||||
break;
|
||||
case REQUEST_STOP_SAMPLE_STREAM:
|
||||
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
|
||||
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource channelSource)
|
||||
{
|
||||
stopChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
|
||||
stopChannelSource(channelSource);
|
||||
channelSource.dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -450,14 +442,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
|
||||
}
|
||||
break;
|
||||
case REQUEST_SOURCE_DISPOSE:
|
||||
Source source = sourceEvent.getSource();
|
||||
|
||||
if(source instanceof PolyphaseChannelSource)
|
||||
{
|
||||
source.dispose();
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED:
|
||||
//Rebroadcast so that the tuner source can process this event
|
||||
mSourceEventBroadcaster.broadcast(sourceEvent);
|
||||
@@ -481,7 +465,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
public class NativeBufferReceiver implements Listener<INativeBuffer>
|
||||
{
|
||||
private boolean mOutputProcessorUpdateRequired = false;
|
||||
private ReentrantLock mUpdateFlagLock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Processes tuner center frequency change source events to flag when output processors need updating.
|
||||
@@ -493,18 +476,9 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
|
||||
if(mChannelCalculator.getCenterFrequency() != frequency)
|
||||
{
|
||||
mUpdateFlagLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
//Update the channel calculator frequency so that it's ready when the output processor update occurs
|
||||
mChannelCalculator.setCenterFrequency(frequency);
|
||||
mOutputProcessorUpdateRequired = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
mUpdateFlagLock.unlock();
|
||||
}
|
||||
//Update the channel calculator frequency so that it's ready when the output processor update occurs
|
||||
mChannelCalculator.setCenterFrequency(frequency);
|
||||
mOutputProcessorUpdateRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,19 +490,17 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
@Override
|
||||
public void receive(INativeBuffer nativeBuffer)
|
||||
{
|
||||
mUpdateFlagLock.lock();
|
||||
|
||||
try
|
||||
if(mOutputProcessorUpdateRequired)
|
||||
{
|
||||
if(mOutputProcessorUpdateRequired)
|
||||
try
|
||||
{
|
||||
updateOutputProcessors();
|
||||
mOutputProcessorUpdateRequired = false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mUpdateFlagLock.unlock();
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error updating polyphase channel output processors");
|
||||
}
|
||||
mOutputProcessorUpdateRequired = false;
|
||||
}
|
||||
|
||||
if(mPolyphaseChannelizer != null)
|
||||
|
||||
@@ -25,12 +25,10 @@ import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.tuner.channel.StreamProcessorWithHeartbeat;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -43,15 +41,13 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
{
|
||||
private Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class);
|
||||
private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor;
|
||||
private StreamProcessorWithHeartbeat<ComplexSamples> mStreamHeartbeatProcessor;
|
||||
private Listener<ComplexSamples> mSamplesListener;
|
||||
private double mChannelSampleRate;
|
||||
private long mIndexCenterFrequency;
|
||||
private ReentrantLock mOutputProcessorLock = new ReentrantLock();
|
||||
|
||||
private List<Integer> mOutputProcessorIndexes = new ArrayList<>();
|
||||
private double mTunerSampleRate;
|
||||
private double mTunerCenterFrequency;
|
||||
|
||||
private PendingOutputProcessorUpdate mPendingOutputProcessorUpdate;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -67,8 +63,7 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
{
|
||||
super(producerSourceEventListener, tunerChannel);
|
||||
mChannelSampleRate = channelCalculator.getChannelSampleRate();
|
||||
mStreamHeartbeatProcessor = new StreamProcessorWithHeartbeat<>(getHeartbeatManager(), HEARTBEAT_INTERVAL_MS);
|
||||
updateOutputProcessor(channelCalculator, filterManager);
|
||||
doUpdateOutputProcessor(channelCalculator, filterManager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,8 +106,6 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
{
|
||||
super.start();
|
||||
|
||||
mStreamHeartbeatProcessor.start();
|
||||
|
||||
if(mPolyphaseChannelOutputProcessor != null)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.start();
|
||||
@@ -128,8 +121,6 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.stop();
|
||||
}
|
||||
|
||||
mStreamHeartbeatProcessor.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,112 +129,129 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
@Override
|
||||
public void setListener(final Listener<ComplexSamples> listener)
|
||||
{
|
||||
mStreamHeartbeatProcessor.setListener(listener);
|
||||
mSamplesListener = listener;
|
||||
mPolyphaseChannelOutputProcessor.setListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ComplexSamples complexSamples)
|
||||
{
|
||||
mStreamHeartbeatProcessor.receive(complexSamples);
|
||||
if(mSamplesListener != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mSamplesListener.receive(complexSamples);
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error dispatching complex samples to listener [" + mSamplesListener.getClass() + "]", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the output processor whenever the source tuner's center frequency changes.
|
||||
* Queues a request to update the output processor whenever the source tuner's center frequency changes.
|
||||
* @param channelCalculator providing access to updated tuner center frequency, sample rate, etc.
|
||||
* @param filterManager for designing and caching synthesis filters
|
||||
*/
|
||||
public void updateOutputProcessor(ChannelCalculator channelCalculator, SynthesisFilterManager filterManager) throws IllegalArgumentException
|
||||
public void updateOutputProcessor(ChannelCalculator channelCalculator, SynthesisFilterManager filterManager)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
mPendingOutputProcessorUpdate = new PendingOutputProcessorUpdate(channelCalculator, filterManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the output processor update. This method must be invoked on the channel results calling thread to
|
||||
* ensure we don't have thread contention on the output processor.
|
||||
* @param channelCalculator for updates
|
||||
* @param filterManager for updates
|
||||
* @throws IllegalArgumentException if there's an issue.
|
||||
*/
|
||||
public void doUpdateOutputProcessor(ChannelCalculator channelCalculator, SynthesisFilterManager filterManager)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
String errorMessage = null;
|
||||
|
||||
mOutputProcessorLock.lock();
|
||||
//If a change in sample rate or center frequency makes this channel no longer viable, then the channel
|
||||
//calculator will throw an IllegalArgException ... handled below
|
||||
List<Integer> indexes = channelCalculator.getChannelIndexes(getTunerChannel());
|
||||
|
||||
try
|
||||
mOutputProcessorIndexes.clear();
|
||||
mOutputProcessorIndexes.addAll(indexes);
|
||||
mTunerCenterFrequency = channelCalculator.getCenterFrequency();
|
||||
mTunerSampleRate = channelCalculator.getSampleRate();
|
||||
|
||||
//The provided channels are necessarily aligned to the center frequency that this source is providing and an
|
||||
//oscillator will mix the provided channels to bring the desired center frequency to baseband.
|
||||
setFrequency(channelCalculator.getCenterFrequencyForIndexes(indexes));
|
||||
|
||||
if(mPolyphaseChannelOutputProcessor != null && mPolyphaseChannelOutputProcessor.getInputChannelCount() == indexes.size())
|
||||
{
|
||||
//If a change in sample rate or center frequency makes this channel no longer viable, then the channel
|
||||
//calculator will throw an IllegalArgException ... handled below
|
||||
List<Integer> indexes = channelCalculator.getChannelIndexes(getTunerChannel());
|
||||
mPolyphaseChannelOutputProcessor.setPolyphaseChannelIndices(indexes);
|
||||
|
||||
mOutputProcessorIndexes.clear();
|
||||
mOutputProcessorIndexes.addAll(indexes);
|
||||
mTunerCenterFrequency = channelCalculator.getCenterFrequency();
|
||||
mTunerSampleRate = channelCalculator.getSampleRate();
|
||||
|
||||
//The provided channels are necessarily aligned to the center frequency that this source is providing and an
|
||||
//oscillator will mix the provided channels to bring the desired center frequency to baseband.
|
||||
setFrequency(channelCalculator.getCenterFrequencyForIndexes(indexes));
|
||||
|
||||
if(mPolyphaseChannelOutputProcessor != null && mPolyphaseChannelOutputProcessor.getInputChannelCount() == indexes.size())
|
||||
if(indexes.size() > 1)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.setPolyphaseChannelIndices(indexes);
|
||||
|
||||
if(indexes.size() > 1)
|
||||
try
|
||||
{
|
||||
float[] filter = filterManager.getFilter(channelCalculator.getChannelSampleRate(),
|
||||
channelCalculator.getChannelBandwidth(), indexes.size());
|
||||
mPolyphaseChannelOutputProcessor.setSynthesisFilter(filter);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.error("Error creating an updated synthesis filter for the channel output processor");
|
||||
errorMessage ="Cannot update output processor - unable to design synthesis filter for [" +
|
||||
indexes.size() + "] channel indices - channel sample rate [" + channelCalculator.getChannelSampleRate() +
|
||||
"] channel bandwidth [" + channelCalculator.getChannelBandwidth() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
|
||||
}
|
||||
else //Create a new output processor.
|
||||
{
|
||||
if(mPolyphaseChannelOutputProcessor != null)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.setListener(null);
|
||||
mPolyphaseChannelOutputProcessor.stop();
|
||||
}
|
||||
|
||||
mPolyphaseChannelOutputProcessor = null;
|
||||
|
||||
switch(indexes.size())
|
||||
{
|
||||
case 1:
|
||||
mPolyphaseChannelOutputProcessor = new OneChannelOutputProcessor(channelCalculator.getChannelSampleRate(),
|
||||
indexes, channelCalculator.getChannelCount(), getHeartbeatManager());
|
||||
mPolyphaseChannelOutputProcessor.setListener(this);
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
|
||||
mPolyphaseChannelOutputProcessor.start();
|
||||
break;
|
||||
case 2:
|
||||
try
|
||||
{
|
||||
float[] filter = filterManager.getFilter(channelCalculator.getChannelSampleRate(),
|
||||
channelCalculator.getChannelBandwidth(), indexes.size());
|
||||
mPolyphaseChannelOutputProcessor.setSynthesisFilter(filter);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.error("Error creating an updated synthesis filter for the channel output processor");
|
||||
errorMessage ="Cannot update output processor - unable to design synthesis filter for [" +
|
||||
indexes.size() + "] channel indices";
|
||||
}
|
||||
}
|
||||
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
|
||||
}
|
||||
else //Create a new output processor.
|
||||
{
|
||||
if(mPolyphaseChannelOutputProcessor != null)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.setListener(null);
|
||||
}
|
||||
|
||||
mPolyphaseChannelOutputProcessor = null;
|
||||
|
||||
switch(indexes.size())
|
||||
{
|
||||
case 1:
|
||||
mPolyphaseChannelOutputProcessor = new OneChannelOutputProcessor(channelCalculator.getChannelSampleRate(),
|
||||
indexes, channelCalculator.getChannelCount());
|
||||
channelCalculator.getChannelBandwidth(), 2);
|
||||
mPolyphaseChannelOutputProcessor = new TwoChannelOutputProcessor(channelCalculator.getChannelSampleRate(),
|
||||
indexes, filter, channelCalculator.getChannelCount(), getHeartbeatManager());
|
||||
mPolyphaseChannelOutputProcessor.setListener(this);
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
|
||||
mPolyphaseChannelOutputProcessor.start();
|
||||
break;
|
||||
case 2:
|
||||
try
|
||||
{
|
||||
float[] filter = filterManager.getFilter(channelCalculator.getChannelSampleRate(),
|
||||
channelCalculator.getChannelBandwidth(), 2);
|
||||
mPolyphaseChannelOutputProcessor = new TwoChannelOutputProcessor(channelCalculator.getChannelSampleRate(),
|
||||
indexes, filter, channelCalculator.getChannelCount());
|
||||
mPolyphaseChannelOutputProcessor.setListener(this);
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
|
||||
mPolyphaseChannelOutputProcessor.start();
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
errorMessage = "Cannot create new output processor - unable to design synthesis filter for [" +
|
||||
indexes.size() + "] channel indices";
|
||||
mLog.error("Error creating a synthesis filter for a new channel output processor");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLog.error("Request to create an output processor for unexpected channel index size:" + indexes.size());
|
||||
mLog.info(channelCalculator.toString());
|
||||
errorMessage = "Unable to create new channel output processor - unexpected channel index size: " +
|
||||
indexes.size();
|
||||
}
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
errorMessage = "Cannot create new output processor - unable to design synthesis filter for [" +
|
||||
indexes.size() + "] channel indices";
|
||||
mLog.error("Error creating a synthesis filter for a new channel output processor");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLog.error("Request to create an output processor for unexpected channel index size:" + indexes.size());
|
||||
mLog.info(channelCalculator.toString());
|
||||
errorMessage = "Unable to create new channel output processor - unexpected channel index size: " +
|
||||
indexes.size();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mOutputProcessorLock.unlock();
|
||||
}
|
||||
|
||||
//Unlikely, but if we had an error designing a synthesis filter, throw an exception
|
||||
if(errorMessage != null)
|
||||
@@ -262,7 +270,13 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
*/
|
||||
public void receiveChannelResults(List<float[]> channelResultsList, long currentSamplesTimestamp)
|
||||
{
|
||||
mOutputProcessorLock.lock();
|
||||
if(mPendingOutputProcessorUpdate != null)
|
||||
{
|
||||
ChannelCalculator channelCalculator = mPendingOutputProcessorUpdate.getChannelCalculator();
|
||||
SynthesisFilterManager filterManager = mPendingOutputProcessorUpdate.getSynthesisFilterManager();
|
||||
mPendingOutputProcessorUpdate = null;
|
||||
doUpdateOutputProcessor(channelCalculator, filterManager);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -271,9 +285,9 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsList, currentSamplesTimestamp);
|
||||
}
|
||||
}
|
||||
finally
|
||||
catch(Exception e)
|
||||
{
|
||||
mOutputProcessorLock.unlock();
|
||||
mLog.error("Error processing channel results", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,4 +356,40 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
return "POLYPHASE [" + mPolyphaseChannelOutputProcessor.getInputChannelCount() + "] " +
|
||||
getTunerChannel().getFrequency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Support classes needed to update the output processor.
|
||||
*/
|
||||
public class PendingOutputProcessorUpdate
|
||||
{
|
||||
private final ChannelCalculator mChannelCalculator;
|
||||
private final SynthesisFilterManager mSynthesisFilterManager;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param channelCalculator for support
|
||||
* @param synthesisFilterManager for support
|
||||
*/
|
||||
public PendingOutputProcessorUpdate(ChannelCalculator channelCalculator, SynthesisFilterManager synthesisFilterManager)
|
||||
{
|
||||
mChannelCalculator = channelCalculator;
|
||||
mSynthesisFilterManager = synthesisFilterManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel calculator
|
||||
*/
|
||||
public ChannelCalculator getChannelCalculator()
|
||||
{
|
||||
return mChannelCalculator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel calculator
|
||||
*/
|
||||
public SynthesisFilterManager getSynthesisFilterManager()
|
||||
{
|
||||
return mSynthesisFilterManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -20,8 +20,8 @@ package io.github.dsheirer.dsp.filter.channelizer.output;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.heartbeat.HeartbeatManager;
|
||||
import io.github.dsheirer.util.Dispatcher;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -31,6 +31,7 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelOutputProcessor.class);
|
||||
|
||||
private Dispatcher<List<float[]>> mChannelResultsDispatcher;
|
||||
private HeartbeatManager mHeartbeatManager;
|
||||
protected Listener<ComplexSamples> mComplexSamplesListener;
|
||||
private int mInputChannelCount;
|
||||
private long mCurrentSampleTimestamp = System.currentTimeMillis();
|
||||
@@ -42,12 +43,25 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
* @param inputChannelCount is the number of input channels for this output processor
|
||||
* @param sampleRate of the output channel. This is used to match the oscillator's sample rate to the output
|
||||
* channel sample rate for frequency translation/correction.
|
||||
* @param heartbeatManager to receive pings on the dispatcher thread
|
||||
*/
|
||||
public ChannelOutputProcessor(int inputChannelCount, double sampleRate)
|
||||
public ChannelOutputProcessor(int inputChannelCount, double sampleRate, HeartbeatManager heartbeatManager)
|
||||
{
|
||||
mInputChannelCount = inputChannelCount;
|
||||
mChannelResultsDispatcher = new Dispatcher<>((int)sampleRate, "sdrtrunk polyphase channel", Collections.emptyList());
|
||||
mChannelResultsDispatcher.setListener(floats -> process(floats));
|
||||
//Process 1/10th of the sample rate per second at a rate of 20 times a second (200% of anticipated rate)
|
||||
mHeartbeatManager = heartbeatManager;
|
||||
mChannelResultsDispatcher = new Dispatcher("sdrtrunk polyphase channel", (int)(sampleRate / 10),
|
||||
50, mHeartbeatManager);
|
||||
mChannelResultsDispatcher.setListener(floats -> {
|
||||
try
|
||||
{
|
||||
process(floats);
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error processing channel results", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package io.github.dsheirer.dsp.filter.channelizer.output;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.heartbeat.HeartbeatManager;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,10 +37,12 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor
|
||||
* @param sampleRate of the output sample stream.
|
||||
* @param channelIndexes containing a single channel index.
|
||||
* @param gain value to apply. This is typically the same as the channelizer's channel count.
|
||||
* @param heartbeatManager to receive heartbeats on the dispatch thread
|
||||
*/
|
||||
public OneChannelOutputProcessor(double sampleRate, List<Integer> channelIndexes, float gain)
|
||||
public OneChannelOutputProcessor(double sampleRate, List<Integer> channelIndexes, float gain,
|
||||
HeartbeatManager heartbeatManager)
|
||||
{
|
||||
super(1, sampleRate);
|
||||
super(1, sampleRate, heartbeatManager);
|
||||
setPolyphaseChannelIndices(channelIndexes);
|
||||
mMixerAssembler = new OneChannelMixerAssembler(gain);
|
||||
mMixerAssembler.getMixer().setSampleRate(sampleRate);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package io.github.dsheirer.dsp.filter.channelizer.output;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.heartbeat.HeartbeatManager;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -37,12 +38,14 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
* @param sampleRate of the output sample stream.
|
||||
* @param channelIndexes containing two channel indices.
|
||||
* @param gain to apply to output. Typically this is equal to the channelizer's channel count.
|
||||
* @param heartbeatManager to be pinged on the dispatcher thread
|
||||
*/
|
||||
public TwoChannelOutputProcessor(double sampleRate, List<Integer> channelIndexes, float[] filter, float gain)
|
||||
public TwoChannelOutputProcessor(double sampleRate, List<Integer> channelIndexes, float[] filter, float gain,
|
||||
HeartbeatManager heartbeatManager)
|
||||
{
|
||||
//Set the frequency correction oscillator to 2 x output sample rate since we'll be correcting the frequency
|
||||
//after synthesizing both input channels
|
||||
super(2, sampleRate);
|
||||
super(2, sampleRate, heartbeatManager);
|
||||
setPolyphaseChannelIndices(channelIndexes);
|
||||
mMixerAssembler = new TwoChannelMixerAssembler(gain);
|
||||
mMixerAssembler.getMixer().setSampleRate(sampleRate);
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.filter.iir;
|
||||
|
||||
/**
|
||||
@@ -40,7 +59,7 @@ public class SinglePoleIirFilter
|
||||
* @param sample to process/filter
|
||||
* @return filtered output
|
||||
*/
|
||||
public double filter(float sample)
|
||||
public float filter(float sample)
|
||||
{
|
||||
mOutput = (mOutput * mOneMinusAlpha) + (mAlpha * sample);
|
||||
return mOutput;
|
||||
@@ -49,7 +68,7 @@ public class SinglePoleIirFilter
|
||||
/**
|
||||
* Current output value
|
||||
*/
|
||||
public double getValue()
|
||||
public float getValue()
|
||||
{
|
||||
return mOutput;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -37,40 +37,23 @@ public class FmDemodulatorFactory
|
||||
* implementation from scalar and vector options.
|
||||
* @return demodulator instance
|
||||
*/
|
||||
public static IFmDemodulator getFmDemodulator()
|
||||
public static IDemodulator getFmDemodulator()
|
||||
{
|
||||
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.FM_DEMODULATOR);
|
||||
|
||||
switch(implementation)
|
||||
{
|
||||
case VECTOR_SIMD_PREFERRED:
|
||||
return new VectorFMDemodulator();
|
||||
case VECTOR_SIMD_64:
|
||||
return new VectorFMDemodulator64();
|
||||
case VECTOR_SIMD_128:
|
||||
return new VectorFMDemodulator128();
|
||||
case VECTOR_SIMD_256:
|
||||
return new VectorFMDemodulator256();
|
||||
case VECTOR_SIMD_512:
|
||||
return new VectorFMDemodulator512();
|
||||
case SCALAR:
|
||||
return new ScalarFMDemodulator();
|
||||
default:
|
||||
mLog.warn("Unrecognized optimal operation for FM demodulator: " + implementation.name());
|
||||
return new ScalarFMDemodulator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the optimal Squelching FM demodulator using calibration data to select the optimal
|
||||
* implementation from scalar and vector options.
|
||||
* @return demodulator instance
|
||||
*/
|
||||
public static ISquelchingFmDemodulator getSquelchingFmDemodulator(float alpha, float threshold, int ramp)
|
||||
{
|
||||
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.FM_DEMODULATOR);
|
||||
|
||||
switch(implementation)
|
||||
{
|
||||
case VECTOR_SIMD_PREFERRED:
|
||||
return new VectorSquelchingFMDemodulator(alpha, threshold, ramp);
|
||||
case SCALAR:
|
||||
return new ScalarSquelchingFMDemodulator(alpha, threshold, ramp);
|
||||
default:
|
||||
mLog.warn("Unrecognized optimal operation for Squelching FM demodulator: " + implementation.name());
|
||||
return new ScalarSquelchingFMDemodulator(alpha, threshold, ramp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
src/main/java/io/github/dsheirer/dsp/fm/IDemodulator.java
Normal file
34
src/main/java/io/github/dsheirer/dsp/fm/IDemodulator.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.fm;
|
||||
|
||||
/**
|
||||
* Interface for demodulator of complex sample arrays.
|
||||
*/
|
||||
public interface IDemodulator
|
||||
{
|
||||
/**
|
||||
* Demodulate the complex sample array and return an array of real audio samples.
|
||||
* @param i samples to demodulate
|
||||
* @param q samples to demodulate
|
||||
* @return demodulated samples.
|
||||
*/
|
||||
float[] demodulate(float[] i, float[] q);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package io.github.dsheirer.dsp.fm;
|
||||
|
||||
/**
|
||||
* Interface for FM demodulator
|
||||
*/
|
||||
public interface IFmDemodulator
|
||||
{
|
||||
/**
|
||||
* Demodulate the complex sample array and return an array of real audio samples.
|
||||
* @param samples to demodulate
|
||||
* @return demodulated samples.
|
||||
*/
|
||||
float[] demodulate(float[] i, float[] q);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.fm;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
|
||||
/**
|
||||
* Interface for FM demodulator that provides squelch control.
|
||||
*/
|
||||
public interface ISquelchingDemodulator extends IDemodulator
|
||||
{
|
||||
/**
|
||||
* Sets the sample rate so that the squelch can adjust the channel power level broadcast notification rate
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
void setSampleRate(int sampleRate);
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state has changed.
|
||||
*/
|
||||
boolean isSquelchChanged();
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state is currently muted
|
||||
*/
|
||||
boolean isMuted();
|
||||
|
||||
/**
|
||||
* Sets the threshold to use for power squelch
|
||||
* @param threshold in decibels
|
||||
*/
|
||||
void setSquelchThreshold(float threshold);
|
||||
|
||||
/**
|
||||
* Enables or disables the squelch auto-tracking feature where the squelch threshold is auto-adjusted based on the
|
||||
* observed channel power when the signal is not present (ie squelch state).
|
||||
* @param autoTrack true to enable
|
||||
*/
|
||||
void setSquelchAutoTrack(boolean autoTrack);
|
||||
|
||||
/**
|
||||
* Registers the listener to receive notifications of squelch change events from the power squelch.
|
||||
*/
|
||||
void setSourceEventListener(Listener<SourceEvent> listener);
|
||||
|
||||
/**
|
||||
* Receives a source event
|
||||
*/
|
||||
void receive(SourceEvent sourceEvent);
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package io.github.dsheirer.dsp.fm;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
|
||||
/**
|
||||
* Interface for FM demodulator that provides squelch control.
|
||||
*/
|
||||
public interface ISquelchingFmDemodulator extends IFmDemodulator
|
||||
{
|
||||
/**
|
||||
* Indicates if the squelch state has changed.
|
||||
*/
|
||||
boolean isSquelchChanged();
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state is currently muted
|
||||
*/
|
||||
boolean isMuted();
|
||||
|
||||
/**
|
||||
* Sets the threshold to use for power squelch
|
||||
* @param threshold in decibels
|
||||
*/
|
||||
void setSquelchThreshold(double threshold);
|
||||
|
||||
/**
|
||||
* Resets the demodulator
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Registers the listener to receive notifications of squelch change events from the power squelch.
|
||||
*/
|
||||
void setSourceEventListener(Listener<SourceEvent> listener);
|
||||
|
||||
/**
|
||||
* Receives a source event
|
||||
*/
|
||||
void receive(SourceEvent sourceEvent);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -18,16 +18,15 @@
|
||||
*/
|
||||
package io.github.dsheirer.dsp.fm;
|
||||
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
/**
|
||||
* FM Demodulator for demodulating complex samples and producing demodulated floating point samples.
|
||||
*/
|
||||
public class ScalarFMDemodulator implements IFmDemodulator
|
||||
public class ScalarFMDemodulator implements IDemodulator
|
||||
{
|
||||
private float mPreviousI = 0.0f;
|
||||
private float mPreviousQ = 0.0f;
|
||||
protected float mPreviousI = 0.0f;
|
||||
protected float mPreviousQ = 0.0f;
|
||||
protected float mGain;
|
||||
|
||||
/**
|
||||
@@ -47,50 +46,6 @@ public class ScalarFMDemodulator implements IFmDemodulator
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Demodulates the I/Q sample via complex sample multiplication by the complex conjugates of the most recently
|
||||
* demodulated complex sample (ie previous sample). Each new sample overwrites the previously stored sample to
|
||||
* allow future invocations of the method to simply use a new sample value.
|
||||
*
|
||||
* @param currentI of the sample
|
||||
* @param currentQ of the sample
|
||||
* @return demodulated sample
|
||||
*/
|
||||
public float demodulate(float currentI, float currentQ)
|
||||
{
|
||||
/**
|
||||
* Multiply the current sample against the complex conjugate of the
|
||||
* previous sample to derive the phase delta between the two samples
|
||||
*
|
||||
* Negating the previous sample quadrature produces the conjugate
|
||||
*/
|
||||
double inphase = (currentI * mPreviousI) - (currentQ * -mPreviousQ);
|
||||
double quadrature = (currentQ * mPreviousI) + (currentI * -mPreviousQ);
|
||||
|
||||
double angle = 0.0f;
|
||||
|
||||
//Check for divide by zero
|
||||
if(inphase != 0)
|
||||
{
|
||||
/**
|
||||
* Use the arc-tangent of quadrature divided by inphase to
|
||||
* get the phase angle (+/-) which was directly manipulated by the
|
||||
* original message waveform during the modulation. This value now
|
||||
* serves as the instantaneous amplitude of the demodulated signal
|
||||
*/
|
||||
double denominator = 1.0d / inphase;
|
||||
angle = FastMath.atan((double)quadrature * denominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the current sample to use during the next iteration
|
||||
*/
|
||||
mPreviousI = currentI;
|
||||
mPreviousQ = currentQ;
|
||||
|
||||
return (float)(angle * mGain);
|
||||
}
|
||||
|
||||
public float[] demodulate(float[] i, float[] q)
|
||||
{
|
||||
float[] demodulated = new float[i.length];
|
||||
@@ -135,35 +90,6 @@ public class ScalarFMDemodulator implements IFmDemodulator
|
||||
return demodulated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Demodulates the complex samples and returns the demodulated value.
|
||||
* @param previous
|
||||
* @param current
|
||||
* @return
|
||||
*/
|
||||
public static double demodulate(Complex previous, Complex current)
|
||||
{
|
||||
double inphase = (current.inphase() * previous.inphase()) - (current.quadrature() * -previous.quadrature());
|
||||
double quadrature = (current.quadrature() * previous.inphase()) + (current.inphase() * -previous.quadrature());
|
||||
|
||||
double angle = 0.0f;
|
||||
|
||||
//Check for divide by zero
|
||||
if(inphase != 0)
|
||||
{
|
||||
/**
|
||||
* Use the arc-tangent of quadrature divided by inphase to
|
||||
* get the phase angle (+/-) which was directly manipulated by the
|
||||
* original message waveform during the modulation. This value now
|
||||
* serves as the instantaneous amplitude of the demodulated signal
|
||||
*/
|
||||
double denominator = 1.0d / inphase;
|
||||
angle = FastMath.atan((double)quadrature * denominator);
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
//no-op
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.dsp.fm;
|
||||
|
||||
import io.github.dsheirer.dsp.squelch.PowerSquelch;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
|
||||
/**
|
||||
* FM Demodulator for demodulating complex samples and producing demodulated floating point samples.
|
||||
*
|
||||
* Implements listener of source events to process runtime squelch threshold change request events
|
||||
* which are forwarded to the power squelch control.
|
||||
*/
|
||||
public class ScalarSquelchingFMDemodulator extends ScalarFMDemodulator implements ISquelchingFmDemodulator, Listener<SourceEvent>
|
||||
{
|
||||
private PowerSquelch mPowerSquelch;
|
||||
private boolean mSquelchChanged = false;
|
||||
|
||||
/**
|
||||
* Creates an FM demodulator instance with a default gain of 1.0.
|
||||
*/
|
||||
public ScalarSquelchingFMDemodulator(float alpha, float threshold, int ramp)
|
||||
{
|
||||
mPowerSquelch = new PowerSquelch(alpha, threshold, ramp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive notifications of squelch change events from the power squelch.
|
||||
*/
|
||||
public void setSourceEventListener(Listener<SourceEvent> listener)
|
||||
{
|
||||
mPowerSquelch.setSourceEventListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the threshold for squelch control
|
||||
* @param threshold (dB)
|
||||
*/
|
||||
public void setSquelchThreshold(double threshold)
|
||||
{
|
||||
mPowerSquelch.setSquelchThreshold(threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state has changed during the processing of buffer(s)
|
||||
*/
|
||||
public boolean isSquelchChanged()
|
||||
{
|
||||
return mSquelchChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or resets the squelch changed flag.
|
||||
*/
|
||||
private void setSquelchChanged(boolean changed)
|
||||
{
|
||||
mSquelchChanged = changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state is currently muted
|
||||
*/
|
||||
public boolean isMuted()
|
||||
{
|
||||
return mPowerSquelch.isMuted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
//Only forward squelch threshold change request events
|
||||
if(sourceEvent.getEvent() == SourceEvent.Event.REQUEST_CHANGE_SQUELCH_THRESHOLD)
|
||||
{
|
||||
mPowerSquelch.receive(sourceEvent);
|
||||
}
|
||||
else if(sourceEvent.getEvent() == SourceEvent.Event.REQUEST_CURRENT_SQUELCH_THRESHOLD)
|
||||
{
|
||||
mPowerSquelch.receive(sourceEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.fm;
|
||||
|
||||
import io.github.dsheirer.dsp.magnitude.IMagnitudeCalculator;
|
||||
import io.github.dsheirer.dsp.magnitude.MagnitudeFactory;
|
||||
import io.github.dsheirer.dsp.squelch.AdaptiveSquelch;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* FM Demodulator for demodulating complex samples and producing demodulated floating point samples.
|
||||
*
|
||||
* Implements listener of source events to process runtime squelch threshold change request events
|
||||
* which are forwarded to the power squelch control.
|
||||
*/
|
||||
public class SquelchingFMDemodulator implements ISquelchingDemodulator, Listener<SourceEvent>
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(SquelchingFMDemodulator.class);
|
||||
private static final float ZERO = 0.0f;
|
||||
private final AdaptiveSquelch mAdaptiveSquelch;
|
||||
private final IMagnitudeCalculator mMagnitude = MagnitudeFactory.getMagnitudeCalculator();
|
||||
private final IDemodulator mFmDemodulator = FmDemodulatorFactory.getFmDemodulator();
|
||||
private boolean mSquelchChanged = false;
|
||||
|
||||
/**
|
||||
* Creates an FM demodulator instance with a default gain of 1.0.
|
||||
*
|
||||
* @param alpha decay value of the single pole IIR filter in range: 0.0 - 1.0. The smaller the alpha value,
|
||||
* the slower the squelch response.
|
||||
* @param squelchThreshold in decibels. Signal power must exceed this threshold value for unsquelch.
|
||||
* causes immediate mute and unmute. Set to higher count to prevent mute/unmute flapping.
|
||||
* @param squelchAutoTrack to enable the squelch noise floor auto-tracking feature.
|
||||
*/
|
||||
public SquelchingFMDemodulator(float alpha, float squelchThreshold, boolean squelchAutoTrack)
|
||||
{
|
||||
mAdaptiveSquelch = new AdaptiveSquelch(alpha, squelchThreshold, squelchAutoTrack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or update the sample rate for the squelch to adjust the power level notification rate.
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
public void setSampleRate(int sampleRate)
|
||||
{
|
||||
mAdaptiveSquelch.setSampleRate(sampleRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive notifications of squelch change events from the power squelch.
|
||||
*/
|
||||
public void setSourceEventListener(Listener<SourceEvent> listener)
|
||||
{
|
||||
mAdaptiveSquelch.setSourceEventListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Demodulates the complex (I/Q) sample arrays
|
||||
* @param i inphase samples
|
||||
* @param q quadrature samples
|
||||
* @return demodulated real samples
|
||||
*/
|
||||
@Override
|
||||
public float[] demodulate(float[] i, float[] q)
|
||||
{
|
||||
setSquelchChanged(false);
|
||||
|
||||
float[] demodulated = mFmDemodulator.demodulate(i, q);
|
||||
float[] magnitude = mMagnitude.calculate(i, q);
|
||||
|
||||
for(int x = 0; x < magnitude.length; x++)
|
||||
{
|
||||
mAdaptiveSquelch.process(magnitude[x]);
|
||||
|
||||
if(!(mAdaptiveSquelch.isUnmuted()))
|
||||
{
|
||||
demodulated[x] = ZERO;
|
||||
}
|
||||
|
||||
if(mAdaptiveSquelch.isSquelchChanged())
|
||||
{
|
||||
setSquelchChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
return demodulated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the threshold for squelch control
|
||||
* @param threshold (dB)
|
||||
*/
|
||||
public void setSquelchThreshold(float threshold)
|
||||
{
|
||||
mAdaptiveSquelch.setSquelchThreshold(threshold);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSquelchAutoTrack(boolean autoTrack)
|
||||
{
|
||||
mAdaptiveSquelch.setSquelchAutoTrack(autoTrack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state has changed during the processing of buffer(s)
|
||||
*/
|
||||
public boolean isSquelchChanged()
|
||||
{
|
||||
return mSquelchChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or resets the squelch changed flag.
|
||||
*/
|
||||
private void setSquelchChanged(boolean changed)
|
||||
{
|
||||
mSquelchChanged = changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the squelch state is currently muted
|
||||
*/
|
||||
public boolean isMuted()
|
||||
{
|
||||
return mAdaptiveSquelch.isMuted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
switch(sourceEvent.getEvent())
|
||||
{
|
||||
//Only forward squelch threshold and auto-track current & change requests
|
||||
case REQUEST_CURRENT_SQUELCH_THRESHOLD:
|
||||
case REQUEST_CHANGE_SQUELCH_THRESHOLD:
|
||||
case REQUEST_CURRENT_SQUELCH_AUTO_TRACK:
|
||||
case REQUEST_CHANGE_SQUELCH_AUTO_TRACK:
|
||||
mAdaptiveSquelch.receive(sourceEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -26,17 +26,12 @@ import jdk.incubator.vector.VectorSpecies;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* FM demodulator that uses JDK 17+ SIMD vector intrinsics
|
||||
*/
|
||||
public class VectorFMDemodulator implements IFmDemodulator
|
||||
public abstract class VectorFMDemodulator implements IDemodulator
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(VectorFMDemodulator.class);
|
||||
protected static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_PREFERRED;
|
||||
protected static final float ZERO = 0.0f;
|
||||
private static final int BUFFER_OVERLAP = 1;
|
||||
|
||||
@@ -48,9 +43,16 @@ public class VectorFMDemodulator implements IFmDemodulator
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector species as specified by the implementing sub-class.
|
||||
*/
|
||||
protected abstract VectorSpecies<Float> getVectorSpecies();
|
||||
|
||||
@Override public float[] demodulate(float[] i, float[] q)
|
||||
{
|
||||
VectorUtilities.checkComplexArrayLength(i, q, VECTOR_SPECIES);
|
||||
// VectorSpecies<Float> species= getVectorSpecies();
|
||||
|
||||
VectorUtilities.checkComplexArrayLength(i, q, getVectorSpecies());
|
||||
|
||||
if(mIBuffer.length != (i.length + BUFFER_OVERLAP))
|
||||
{
|
||||
@@ -70,12 +72,12 @@ public class VectorFMDemodulator implements IFmDemodulator
|
||||
|
||||
FloatVector currentI, currentQ, previousI, previousQ, demod, demodI, demodQ;
|
||||
|
||||
for(int bufferPointer = 0; bufferPointer < mIBuffer.length - 1; bufferPointer += VECTOR_SPECIES.length())
|
||||
for(int bufferPointer = 0; bufferPointer < mIBuffer.length - 1; bufferPointer += getVectorSpecies().length())
|
||||
{
|
||||
previousI = FloatVector.fromArray(VECTOR_SPECIES, mIBuffer, bufferPointer);
|
||||
previousQ = FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, bufferPointer);
|
||||
currentI = FloatVector.fromArray(VECTOR_SPECIES, mIBuffer, bufferPointer + 1);
|
||||
currentQ = FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, bufferPointer + 1);
|
||||
previousI = FloatVector.fromArray(getVectorSpecies(), mIBuffer, bufferPointer);
|
||||
previousQ = FloatVector.fromArray(getVectorSpecies(), mQBuffer, bufferPointer);
|
||||
currentI = FloatVector.fromArray(getVectorSpecies(), mIBuffer, bufferPointer + 1);
|
||||
currentQ = FloatVector.fromArray(getVectorSpecies(), mQBuffer, bufferPointer + 1);
|
||||
|
||||
demodI = currentI.mul(previousI).sub(currentQ.mul(previousQ.neg()));
|
||||
demodQ = currentQ.mul(previousI).add(currentI.mul(previousQ.neg()));
|
||||
@@ -89,53 +91,4 @@ public class VectorFMDemodulator implements IFmDemodulator
|
||||
|
||||
return demodulated;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
int iterations = 3_000_000;
|
||||
int sampleSize = 2048;
|
||||
|
||||
Random random = new Random();
|
||||
|
||||
float[] iSamples = new float[sampleSize];
|
||||
float[] qSamples = new float[sampleSize];
|
||||
for(int x = 0; x < iSamples.length; x++)
|
||||
{
|
||||
iSamples[x] = random.nextFloat() * 2.0f - 1.0f;
|
||||
qSamples[x] = random.nextFloat() * 2.0f - 1.0f;
|
||||
}
|
||||
|
||||
boolean validation = false;
|
||||
|
||||
ScalarFMDemodulator legacy = new ScalarFMDemodulator();
|
||||
VectorFMDemodulator vector = new VectorFMDemodulator();
|
||||
|
||||
if(validation)
|
||||
{
|
||||
float[] legacySamples = legacy.demodulate(iSamples, qSamples);
|
||||
float[] vectorSamples = vector.demodulate(iSamples, qSamples);
|
||||
System.out.println("LEGACY:" + Arrays.toString(legacySamples));
|
||||
System.out.println("VECTOR:" + Arrays.toString(vectorSamples));
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Test Starting ...");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
double accumulator = 0.0;
|
||||
|
||||
for(int i = 0; i < iterations; i++)
|
||||
{
|
||||
float[] samples = legacy.demodulate(iSamples, qSamples);
|
||||
// float[] samples = vector.demodulate(iSamples, qSamples);
|
||||
accumulator += samples[3];
|
||||
}
|
||||
|
||||
double elapsed = System.currentTimeMillis() - start;
|
||||
|
||||
DecimalFormat df = new DecimalFormat("0.000");
|
||||
System.out.println("Accumulator: " + accumulator);
|
||||
System.out.println("Test Complete. Elapsed Time: " + df.format(elapsed / 1000.0d) + " seconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.dsp.fm;
|
||||
|
||||
import io.github.dsheirer.vector.VectorUtilities;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* FM demodulator that uses JDK 17+ SIMD vector intrinsics
|
||||
*/
|
||||
public class VectorFMDemodulator128 implements IDemodulator
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(VectorFMDemodulator128.class);
|
||||
protected static final float ZERO = 0.0f;
|
||||
private static final int BUFFER_OVERLAP = 1;
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
|
||||
|
||||
//Initialize buffers to be non-null and resize once the first buffer arrives
|
||||
private float[] mIBuffer = new float[1];
|
||||
private float[] mQBuffer = new float[1];
|
||||
|
||||
public VectorFMDemodulator128()
|
||||
{
|
||||
}
|
||||
|
||||
@Override public float[] demodulate(float[] i, float[] q)
|
||||
{
|
||||
VectorUtilities.checkComplexArrayLength(i, q, VECTOR_SPECIES);
|
||||
|
||||
if(mIBuffer.length != (i.length + BUFFER_OVERLAP))
|
||||
{
|
||||
mIBuffer = new float[i.length + BUFFER_OVERLAP];
|
||||
mQBuffer = new float[q.length + BUFFER_OVERLAP];
|
||||
}
|
||||
|
||||
//Copy last sample to beginning of buffer arrays
|
||||
mIBuffer[0] = mIBuffer[mIBuffer.length - 1];
|
||||
mQBuffer[0] = mQBuffer[mQBuffer.length - 1];
|
||||
|
||||
//Copy new samples to end of buffer arrays
|
||||
System.arraycopy(i, 0, mIBuffer, 1, i.length);
|
||||
System.arraycopy(q, 0, mQBuffer, 1, q.length);
|
||||
|
||||
float[] demodulated = new float[i.length];
|
||||
|
||||
FloatVector currentI, currentQ, previousI, previousQ, demod, demodI, demodQ;
|
||||
|
||||
for(int bufferPointer = 0; bufferPointer < mIBuffer.length - 1; bufferPointer += VECTOR_SPECIES.length())
|
||||
{
|
||||
previousI = FloatVector.fromArray(VECTOR_SPECIES, mIBuffer, bufferPointer);
|
||||
previousQ = FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, bufferPointer);
|
||||
currentI = FloatVector.fromArray(VECTOR_SPECIES, mIBuffer, bufferPointer + 1);
|
||||
currentQ = FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, bufferPointer + 1);
|
||||
|
||||
demodI = currentI.mul(previousI).sub(currentQ.mul(previousQ.neg()));
|
||||
demodQ = currentQ.mul(previousI).add(currentI.mul(previousQ.neg()));
|
||||
|
||||
//Replace any zero-values in the I vector by adding Float.MIN_VALUE to side-step divide by zero errors
|
||||
demodI = demodI.add(Float.MIN_VALUE, demodQ.eq(ZERO));
|
||||
|
||||
demod = demodQ.div(demodI).lanewise(VectorOperators.ATAN);
|
||||
demod.intoArray(demodulated, bufferPointer);
|
||||
}
|
||||
|
||||
return demodulated;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user