mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-11 08:07:24 -05:00
Compare commits
11 Commits
v0.5.1-bet
...
v0.6.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc13777211 | ||
|
|
75347f9d0a | ||
|
|
9abf64c6ab | ||
|
|
1702103f5d | ||
|
|
8f68a989b9 | ||
|
|
b51a0102ef | ||
|
|
e177c2c6b0 | ||
|
|
3567fdf51d | ||
|
|
323097ba65 | ||
|
|
33f2bab7ac | ||
|
|
c063cfb201 |
4
.github/workflows/gradle.yml
vendored
4
.github/workflows/gradle.yml
vendored
@@ -22,10 +22,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 17
|
||||
- name: Set up JDK 19
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
java-version: '19'
|
||||
distribution: 'liberica'
|
||||
java-package: 'jdk+fx'
|
||||
- name: Build with Gradle
|
||||
|
||||
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_19_PREVIEW" project-jdk-name="liberica-19.0.1.fx" 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_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
|
||||
39
build.gradle
39
build.gradle
@@ -1,3 +1,5 @@
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
@@ -28,8 +30,14 @@ repositories {
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
|
||||
version = '0.5.1-beta1'
|
||||
sourceCompatibility = '17'
|
||||
version = '0.6.0-alpha2'
|
||||
|
||||
//Java 19 is required for this version of the Project Panama preview/incubator feature
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(19)
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs 'src/main'
|
||||
@@ -91,9 +99,10 @@ dependencies {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
@@ -101,8 +110,21 @@ 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
|
||||
'--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'] //Allows Project Panama Foreign Function access to system library
|
||||
|
||||
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
|
||||
if(os.isWindows()) {
|
||||
//Replace the default Java system library path with an explicit location for the SDRplay API library (Windows)
|
||||
applicationDefaultJvmArgs.add('-Djava.library.path=c:\\Program Files\\SDRplay\\API\\x64')
|
||||
//On windows, we also need to export the windows swing look & feel module
|
||||
applicationDefaultJvmArgs.add('--add-exports=java.desktop/com.sun.java.swing.plaf.windows=ALL-UNNAMED')
|
||||
}
|
||||
else {
|
||||
//Replace the default Java system library path with an explicit location for the SDRplay API library (Linux/OS-X)
|
||||
applicationDefaultJvmArgs.add("-Djava.library.path=/usr/local/lib")
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
@@ -111,7 +133,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']}"
|
||||
)
|
||||
@@ -212,7 +234,10 @@ runtime {
|
||||
}
|
||||
|
||||
//Additional modules to include with auto-detected modules.
|
||||
modules = ['jdk.crypto.ec'] //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.incubator.vector','jdk.accessibility']
|
||||
|
||||
//Use auto-detected modules and 'add' any specified modules.
|
||||
additive = true
|
||||
|
||||
@@ -68,7 +68,7 @@ public enum BroadcastState
|
||||
/**
|
||||
* Specified mount point is already in use
|
||||
*/
|
||||
MOUNT_POINT_IN_USE("Mount Point In Use", true),
|
||||
MOUNT_POINT_IN_USE("Mount Point In Use", false),
|
||||
|
||||
/**
|
||||
* Network is unavailable or the server address cannot be resolved
|
||||
|
||||
@@ -19,13 +19,20 @@
|
||||
package io.github.dsheirer.bits;
|
||||
|
||||
import io.github.dsheirer.edac.CRC;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.BitSet;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class BinaryMessage extends BitSet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BinaryMessage.class);
|
||||
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";
|
||||
|
||||
/**
|
||||
* Logical (ie constructed) size of this bitset, despite the actual size of
|
||||
@@ -1174,19 +1181,101 @@ public class BinaryMessage extends BitSet
|
||||
this.xor(mask);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
/**
|
||||
* Parse ISO-7 bit characters from the message starting at the offset.
|
||||
* @param offset where to start parsing.
|
||||
* @param characterCount number of characters to parse
|
||||
* @return parsed message.
|
||||
*/
|
||||
public String parseISO7(int offset, int characterCount)
|
||||
{
|
||||
BinaryMessage message = new BinaryMessage(10);
|
||||
message.set(1);
|
||||
message.set(4);
|
||||
message.set(5);
|
||||
message.set(8);
|
||||
message.set(9);
|
||||
byte[] bytes = new byte[characterCount];
|
||||
|
||||
System.out.println("Hex: " + message.toHexString() + " Size:" + message.size());
|
||||
for(int x = 0; x < 10; x++)
|
||||
for(int x = 0; x < characterCount; x++)
|
||||
{
|
||||
System.out.println(x + ": " + message.getHex(x, x + 10));
|
||||
bytes[x] = getByte(CHARACTER_7_BIT, x * 7 + offset);
|
||||
}
|
||||
|
||||
return new String(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse ISO-8 bit characters from the message starting at the offset.
|
||||
* @param offset where to start parsing.
|
||||
* @param characterCount number of characters to parse
|
||||
* @return parsed message.
|
||||
*/
|
||||
public String parseISO8(int offset, int characterCount)
|
||||
{
|
||||
byte[] bytes = new byte[characterCount];
|
||||
|
||||
for(int x = 0; x < characterCount; x++)
|
||||
{
|
||||
bytes[x] = getByte(CHARACTER_8_BIT, x * 8 + offset);
|
||||
}
|
||||
|
||||
return new String(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse UTF-8 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 UTF-8 encoding is not supported.
|
||||
*/
|
||||
public String parseUTF8(int offset, int characterCount) throws UnsupportedEncodingException
|
||||
{
|
||||
byte[] bytes = new byte[characterCount];
|
||||
|
||||
for(int x = 0; x < characterCount; x++)
|
||||
{
|
||||
bytes[x] = getByte(CHARACTER_8_BIT, x * 8 + offset);
|
||||
}
|
||||
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 16-bit unicode characters from the message starting at the offset.
|
||||
* @param offset where to start parsing.
|
||||
* @param characterCount number of characters to parse
|
||||
* @return parsed message.
|
||||
*/
|
||||
public String parseUnicode(int offset, int characterCount)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 4-bit Binary Coded Decimal (BCD) characters from the message starting at the offset.
|
||||
* @param offset where to start parsing.
|
||||
* @param characterCount number of characters to parse
|
||||
* @return parsed message.
|
||||
*/
|
||||
public String parseBCD4(int offset, int characterCount)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int count = 0;
|
||||
|
||||
while(count < characterCount)
|
||||
{
|
||||
int nibble = getNibble(offset);
|
||||
//This will parse digits 0-9 and anything higher will parse as hexadecimal A-F
|
||||
sb.append(Integer.toHexString(nibble).toUpperCase());
|
||||
offset += 4;
|
||||
count++;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.edac;
|
||||
@@ -34,7 +31,7 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
public class BerlekempMassey
|
||||
{
|
||||
public final static Logger mLog = LoggerFactory.getLogger(BerlekempMassey.class);
|
||||
private final static Logger mLog = LoggerFactory.getLogger(BerlekempMassey.class);
|
||||
|
||||
/* Golay field size GF( 2 ** MM ) */
|
||||
private int MM;
|
||||
|
||||
66
src/main/java/io/github/dsheirer/edac/Checksum_5_DMR.java
Normal file
66
src/main/java/io/github/dsheirer/edac/Checksum_5_DMR.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.edac;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Implements the 5-bit Checksum error detection algorithm specified in TS 102 361-1 paragraph B.3.11
|
||||
*/
|
||||
public class Checksum_5_DMR
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Checksum_5_DMR.class);
|
||||
|
||||
private static final int[] LC_0 = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] LC_1 = {8, 9, 10, 11, 12, 13, 14, 15};
|
||||
private static final int[] LC_2 = {16, 17, 18, 19, 20, 21, 22, 23};
|
||||
private static final int[] LC_3 = {24, 25, 26, 27, 28, 29, 30, 31};
|
||||
private static final int[] LC_4 = {32, 33, 34, 35, 36, 37, 38, 39};
|
||||
private static final int[] LC_5 = {40, 41, 42, 43, 44, 45, 46, 47};
|
||||
private static final int[] LC_6 = {48, 49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] LC_7 = {56, 57, 58, 59, 60, 61, 62, 63};
|
||||
private static final int[] LC_8 = {64, 65, 66, 67, 68, 69, 70, 71};
|
||||
private static final int[] CHECKSUM = {72, 73, 74, 75, 76};
|
||||
|
||||
/**
|
||||
* Indicates if the 77-bit Full Link Control message is valid.
|
||||
* @param message to check
|
||||
* @return true if the checksum matches.
|
||||
*/
|
||||
public static boolean isValid(CorrectedBinaryMessage message)
|
||||
{
|
||||
int accumulator = message.getInt(LC_0);
|
||||
accumulator += message.getInt(LC_1);
|
||||
accumulator += message.getInt(LC_2);
|
||||
accumulator += message.getInt(LC_3);
|
||||
accumulator += message.getInt(LC_4);
|
||||
accumulator += message.getInt(LC_5);
|
||||
accumulator += message.getInt(LC_6);
|
||||
accumulator += message.getInt(LC_7);
|
||||
accumulator += message.getInt(LC_8);
|
||||
|
||||
accumulator = accumulator % 31;
|
||||
int checksum = message.getInt(CHECKSUM);
|
||||
|
||||
return accumulator == checksum;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -20,12 +20,16 @@
|
||||
package io.github.dsheirer.edac;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Berlecamp-Massey decoder for Reed Solomon RS(12,9,4) protected messages using DMR generator polynomial
|
||||
*/
|
||||
public class ReedSolomon_12_9_4_DMR extends ReedSolomon_255_DMR
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ReedSolomon_12_9_4_DMR.class);
|
||||
|
||||
private static final int[] FLC_CODEWORD_0 = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] FLC_CODEWORD_1 = {8, 9, 10, 11, 12, 13, 14, 15};
|
||||
private static final int[] FLC_CODEWORD_2 = {16, 17, 18, 19, 20, 21, 22, 23};
|
||||
@@ -56,16 +60,16 @@ public class ReedSolomon_12_9_4_DMR extends ReedSolomon_255_DMR
|
||||
*
|
||||
* @param message to be corrected.
|
||||
* @param crcMask as defined in the TS 102-361-1 Table B.21
|
||||
* @return true if the message was corrected.
|
||||
* @return true if the message is correct or if the message was corrected.
|
||||
*/
|
||||
public boolean correctFullLinkControl(CorrectedBinaryMessage message, int crcMask)
|
||||
{
|
||||
int[] input = new int[255];
|
||||
int[] output = new int[255];
|
||||
|
||||
input[0] = message.getInt(FLC_PARITY_2) ^ crcMask;
|
||||
input[1] = message.getInt(FLC_PARITY_1) ^ crcMask;
|
||||
input[2] = message.getInt(FLC_PARITY_0) ^ crcMask;
|
||||
input[0] = (message.getInt(FLC_PARITY_2) ^ crcMask);
|
||||
input[1] = (message.getInt(FLC_PARITY_1) ^ crcMask);
|
||||
input[2] = (message.getInt(FLC_PARITY_0) ^ crcMask);
|
||||
input[3] = message.getInt(FLC_CODEWORD_8);
|
||||
input[4] = message.getInt(FLC_CODEWORD_7);
|
||||
input[5] = message.getInt(FLC_CODEWORD_6);
|
||||
@@ -106,4 +110,40 @@ public class ReedSolomon_12_9_4_DMR extends ReedSolomon_255_DMR
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
int[] output = new int[255];
|
||||
int[] input = new int[255];
|
||||
input[0] = 247;
|
||||
input[1] = 236;
|
||||
input[2] = 171;
|
||||
input[3] = 24;
|
||||
input[4] = 46;
|
||||
input[5] = 177;
|
||||
input[6] = 246;
|
||||
input[7] = 205;
|
||||
input[8] = 232;
|
||||
input[9] = 32;
|
||||
input[10] = 124;
|
||||
input[11] = 0;
|
||||
|
||||
// input[254] = 247;
|
||||
// input[253] = 236;
|
||||
// input[252] = 171;
|
||||
// input[251] = 24;
|
||||
// input[250] = 46;
|
||||
// input[249] = 177;
|
||||
// input[248] = 246;
|
||||
// input[247] = 205;
|
||||
// input[246] = 232;
|
||||
// input[245] = 32;
|
||||
// input[244] = 124;
|
||||
// input[243] = 0;
|
||||
|
||||
ReedSolomon_12_9_4_DMR rs = new ReedSolomon_12_9_4_DMR();
|
||||
boolean irrecoverable = rs.decode(input, output);
|
||||
|
||||
mLog.info("Irrecoverable: " + irrecoverable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,6 +24,8 @@ import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.controller.channel.map.ChannelMap;
|
||||
import io.github.dsheirer.controller.channel.map.ChannelRange;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.gui.dmr.DMRRecordingViewer;
|
||||
import io.github.dsheirer.gui.dmr.ViewDmrRecordingViewerRequest;
|
||||
import io.github.dsheirer.gui.icon.IconManager;
|
||||
import io.github.dsheirer.gui.icon.ViewIconManagerRequest;
|
||||
import io.github.dsheirer.gui.playlist.PlaylistEditor;
|
||||
@@ -65,6 +67,7 @@ public class JavaFxWindowManager extends Application
|
||||
public static final String USER_PREFERENCES_EDITOR = "preferences";
|
||||
public static final String STAGE_MONITOR_KEY_CALIBRATION_DIALOG = "calibration.dialog";
|
||||
public static final String STAGE_MONITOR_KEY_CHANNEL_MAP_EDITOR = "channel.map";
|
||||
public static final String STAGE_MONITOR_KEY_DMR_MESSAGE_VIEWER = "dmr.message.viewer";
|
||||
public static final String STAGE_MONITOR_KEY_ICON_MANAGER_EDITOR = "icon.manager";
|
||||
public static final String STAGE_MONITOR_KEY_JMBE_EDITOR = "jmbe.editor";
|
||||
public static final String STAGE_MONITOR_KEY_PLAYLIST_EDITOR = "playlist";
|
||||
@@ -79,12 +82,14 @@ public class JavaFxWindowManager extends Application
|
||||
private TunerManager mTunerManager;
|
||||
private UserPreferences mUserPreferences;
|
||||
private UserPreferencesEditor mUserPreferencesEditor;
|
||||
private DMRRecordingViewer mDmrRecordingViewer;
|
||||
|
||||
private Stage mChannelMapStage;
|
||||
private Stage mIconManagerStage;
|
||||
private Stage mJmbeEditorStage;
|
||||
private Stage mPlaylistStage;
|
||||
private Stage mUserPreferencesStage;
|
||||
private Stage mDmrRecordingViewerStage;
|
||||
|
||||
/**
|
||||
* Constructs an instance. Note: this constructor is used for Swing applications.
|
||||
@@ -189,6 +194,34 @@ public class JavaFxWindowManager extends Application
|
||||
return new CalibrationDialog(userPreferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stage for the DMR Message Viewer
|
||||
*/
|
||||
public Stage getDmrRecordingViewerStage()
|
||||
{
|
||||
if(mDmrRecordingViewerStage == null)
|
||||
{
|
||||
createJFXPanel();
|
||||
Scene scene = new Scene(getDmrRecordingViewer(), 1100, 800);
|
||||
mDmrRecordingViewerStage = new Stage();
|
||||
mDmrRecordingViewerStage.setTitle("sdrtrunk - DMR Recording Viewer");
|
||||
mDmrRecordingViewerStage.setScene(scene);
|
||||
mUserPreferences.getJavaFxPreferences().monitor(mDmrRecordingViewerStage, STAGE_MONITOR_KEY_DMR_MESSAGE_VIEWER);
|
||||
}
|
||||
|
||||
return mDmrRecordingViewerStage;
|
||||
}
|
||||
|
||||
public DMRRecordingViewer getDmrRecordingViewer()
|
||||
{
|
||||
if(mDmrRecordingViewer == null)
|
||||
{
|
||||
mDmrRecordingViewer = new DMRRecordingViewer();
|
||||
}
|
||||
|
||||
return mDmrRecordingViewer;
|
||||
}
|
||||
|
||||
public Stage getIconManagerStage()
|
||||
{
|
||||
if(mIconManagerStage == null)
|
||||
@@ -416,6 +449,19 @@ public class JavaFxWindowManager extends Application
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a channel map editor request
|
||||
*/
|
||||
@Subscribe
|
||||
public void process(final ViewDmrRecordingViewerRequest request)
|
||||
{
|
||||
execute(() -> {
|
||||
getDmrRecordingViewerStage().show();
|
||||
getDmrRecordingViewerStage().requestFocus();
|
||||
getDmrRecordingViewerStage().toFront();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -32,6 +32,7 @@ import io.github.dsheirer.controller.channel.ChannelAutoStartFrame;
|
||||
import io.github.dsheirer.controller.channel.ChannelException;
|
||||
import io.github.dsheirer.controller.channel.ChannelSelectionManager;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.gui.dmr.ViewDmrRecordingViewerRequest;
|
||||
import io.github.dsheirer.gui.icon.ViewIconManagerRequest;
|
||||
import io.github.dsheirer.gui.playlist.ViewPlaylistRequest;
|
||||
import io.github.dsheirer.gui.preference.CalibrateRequest;
|
||||
@@ -59,6 +60,26 @@ import io.github.dsheirer.spectrum.SpectralDisplayPanel;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import io.github.dsheirer.util.TimeStamp;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationManager;
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Point;
|
||||
import java.awt.Robot;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import jiconfont.icons.font_awesome.FontAwesome;
|
||||
@@ -80,26 +101,6 @@ import javax.swing.UIManager;
|
||||
import javax.swing.event.MenuEvent;
|
||||
import javax.swing.event.MenuListener;
|
||||
import javax.swing.plaf.metal.MetalLookAndFeel;
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Point;
|
||||
import java.awt.Robot;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public class SDRTrunk implements Listener<TunerEvent>
|
||||
{
|
||||
@@ -247,7 +248,11 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
{
|
||||
mMainGui.setVisible(true);
|
||||
Tuner tuner = tunerSpectralDisplayManager.showFirstTuner();
|
||||
updateTitle(tuner);
|
||||
|
||||
if(tuner != null)
|
||||
{
|
||||
updateTitle(tuner.getPreferredName());
|
||||
}
|
||||
}
|
||||
|
||||
if(calibrating && !GraphicsEnvironment.isHeadless())
|
||||
@@ -413,9 +418,11 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
viewPlaylistItem.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewPlaylistRequest()));
|
||||
viewMenu.add(viewPlaylistItem);
|
||||
|
||||
JMenuItem preferencesItem = new JMenuItem("User Preferences");
|
||||
preferencesItem.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewUserPreferenceEditorRequest()));
|
||||
viewMenu.add(preferencesItem);
|
||||
viewMenu.add(new JSeparator());
|
||||
|
||||
JMenuItem dmrMessageViewerMenu = new JMenuItem("DMR Recording Viewer");
|
||||
dmrMessageViewerMenu.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewDmrRecordingViewerRequest()));
|
||||
viewMenu.add(dmrMessageViewerMenu);
|
||||
|
||||
JMenuItem settingsMenu = new JMenuItem("Icon Manager");
|
||||
settingsMenu.addActionListener(arg0 -> MyEventBus.getGlobalEventBus().post(new ViewIconManagerRequest()));
|
||||
@@ -436,15 +443,19 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
mLog.error("Couldn't open file explorer");
|
||||
|
||||
JOptionPane.showMessageDialog(mMainGui,
|
||||
"Can't launch file explorer - files are located at: " +
|
||||
getHomePath().toString(),
|
||||
"Can't launch file explorer",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
"Can't launch file explorer - files are located at: " +
|
||||
getHomePath().toString(),
|
||||
"Can't launch file explorer",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
viewMenu.add(logFilesMenu);
|
||||
|
||||
JMenuItem preferencesItem = new JMenuItem("User Preferences");
|
||||
preferencesItem.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewUserPreferenceEditorRequest()));
|
||||
viewMenu.add(preferencesItem);
|
||||
|
||||
viewMenu.add(new JSeparator());
|
||||
viewMenu.add(new TunersMenu());
|
||||
viewMenu.add(new JSeparator());
|
||||
@@ -623,21 +634,34 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
@Override
|
||||
public void receive(TunerEvent event)
|
||||
{
|
||||
if(event.getEvent() == TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY)
|
||||
switch(event.getEvent())
|
||||
{
|
||||
updateTitle(event.getTuner());
|
||||
case REQUEST_MAIN_SPECTRAL_DISPLAY:
|
||||
updateTitle(event.getTuner().getPreferredName());
|
||||
break;
|
||||
case REQUEST_CLEAR_MAIN_SPECTRAL_DISPLAY:
|
||||
updateTitle(null);
|
||||
break;
|
||||
case NOTIFICATION_SHUTTING_DOWN:
|
||||
Tuner currentTuner = mSpectralPanel.getTuner();
|
||||
|
||||
if(event.hasTuner() && event.getTuner().equals(currentTuner) || currentTuner == null)
|
||||
{
|
||||
updateTitle(null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the title bar with the tuner name
|
||||
* @param tuner optional
|
||||
* @param tunerName optional
|
||||
*/
|
||||
private void updateTitle(Tuner tuner)
|
||||
private void updateTitle(String tunerName)
|
||||
{
|
||||
if(tuner != null)
|
||||
if(tunerName != null)
|
||||
{
|
||||
mMainGui.setTitle(mTitle + " - " + tuner.getPreferredName());
|
||||
mMainGui.setTitle(mTitle + " - " + tunerName);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
479
src/main/java/io/github/dsheirer/gui/dmr/DMRRecordingViewer.java
Normal file
479
src/main/java/io/github/dsheirer/gui/dmr/DMRRecordingViewer.java
Normal file
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.gui.dmr;
|
||||
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRMessageFramer;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRMessageProcessor;
|
||||
import io.github.dsheirer.module.decode.dmr.DecodeConfigDMR;
|
||||
import io.github.dsheirer.record.binary.BinaryReader;
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.prefs.Preferences;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.scene.control.TableView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Callback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Utility application to load and view a DMR .bits recording file with the messages fully parsed.
|
||||
*/
|
||||
public class DMRRecordingViewer extends VBox
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(DMRRecordingViewer.class);
|
||||
private static final String LAST_SELECTED_DIRECTORY = "last.selected.directory";
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(DMRRecordingViewer.class);
|
||||
private Scene mScene;
|
||||
private Button mSelectFileButton;
|
||||
private Label mSelectedFileLabel;
|
||||
private TableView<IMessage> mMessageTableView;
|
||||
private ObservableList<IMessage> mMessages = FXCollections.observableArrayList();
|
||||
private FilteredList<IMessage> mFilteredMessages = new FilteredList<>(mMessages);
|
||||
private CheckBox mShowTS0;
|
||||
private CheckBox mShowTS1;
|
||||
private CheckBox mShowTS2;
|
||||
private CheckBox mUseCompressedTalkgroups;
|
||||
private TextField mSearchText;
|
||||
private TextField mFindText;
|
||||
private Button mFindButton;
|
||||
private Button mFindNextButton;
|
||||
|
||||
public DMRRecordingViewer()
|
||||
{
|
||||
setPadding(new Insets(5));
|
||||
setSpacing(5);
|
||||
|
||||
HBox fileBox = new HBox();
|
||||
fileBox.setMaxWidth(Double.MAX_VALUE);
|
||||
fileBox.setAlignment(Pos.CENTER_LEFT);
|
||||
fileBox.setSpacing(5);
|
||||
HBox.setHgrow(getSelectFileButton(), Priority.NEVER);
|
||||
HBox.setHgrow(getSelectedFileLabel(), Priority.ALWAYS);
|
||||
getSelectedFileLabel().setAlignment(Pos.BASELINE_CENTER);
|
||||
|
||||
HBox compressedBox = new HBox();
|
||||
compressedBox.setAlignment(Pos.CENTER_RIGHT);
|
||||
compressedBox.setMaxWidth(Double.MAX_VALUE);
|
||||
compressedBox.getChildren().addAll(getUseCompressedTalkgroups());
|
||||
HBox.setHgrow(compressedBox, Priority.ALWAYS);
|
||||
|
||||
fileBox.getChildren().addAll(getSelectFileButton(), getSelectedFileLabel(), compressedBox);
|
||||
|
||||
HBox filterBox = new HBox();
|
||||
filterBox.setMaxWidth(Double.MAX_VALUE);
|
||||
filterBox.setAlignment(Pos.BASELINE_CENTER);
|
||||
filterBox.setSpacing(5);
|
||||
|
||||
Label showLabel = new Label("Show:");
|
||||
HBox.setMargin(showLabel, new Insets(0,0,0,15));
|
||||
Label searchLabel = new Label("Message Filter:");
|
||||
HBox.setMargin(searchLabel, new Insets(0,0,0,15));
|
||||
Label findLabel = new Label("Find:");
|
||||
|
||||
HBox.setHgrow(getFindText(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getSearchText(), Priority.ALWAYS);
|
||||
|
||||
filterBox.getChildren().addAll(findLabel, getFindText(), getFindButton(), getFindNextButton(), searchLabel,
|
||||
getSearchText(), showLabel, getShowTS0(), getShowTS1(), getShowTS2());
|
||||
|
||||
VBox.setVgrow(fileBox, Priority.NEVER);
|
||||
VBox.setVgrow(filterBox, Priority.NEVER);
|
||||
VBox.setVgrow(getMessageTableView(), Priority.ALWAYS);
|
||||
|
||||
getChildren().addAll(fileBox, filterBox, getMessageTableView());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the recording file and loads the content into the viewer
|
||||
* @param file containing a .bits recording of decoded DMR data.
|
||||
*/
|
||||
private void load(File file)
|
||||
{
|
||||
if(file != null && file.exists())
|
||||
{
|
||||
getSelectedFileLabel().setText(file.getName());
|
||||
mMessages.clear();
|
||||
|
||||
DMRMessageFramer messageFramer = new DMRMessageFramer(null);
|
||||
DecodeConfigDMR config = new DecodeConfigDMR();
|
||||
if(getUseCompressedTalkgroups().isSelected())
|
||||
{
|
||||
config.setUseCompressedTalkgroups(true);
|
||||
}
|
||||
DMRMessageProcessor messageProcessor = new DMRMessageProcessor(config);
|
||||
messageFramer.setListener(messageProcessor);
|
||||
messageProcessor.setMessageListener(message -> mMessages.add(message));
|
||||
|
||||
try(BinaryReader reader = new BinaryReader(file.toPath(), 200))
|
||||
{
|
||||
while(reader.hasNext())
|
||||
{
|
||||
ByteBuffer buffer = reader.next();
|
||||
messageFramer.receive(buffer);
|
||||
}
|
||||
}
|
||||
catch(Exception ioe)
|
||||
{
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the filter(s) applies to the list of messages
|
||||
*/
|
||||
private void updateFilters()
|
||||
{
|
||||
Predicate<IMessage> timeslotPredicate = message ->
|
||||
(getShowTS0().isSelected() && (message.getTimeslot() == 0)) ||
|
||||
(getShowTS1().isSelected() && (message.getTimeslot() == 1)) ||
|
||||
(getShowTS2().isSelected() && (message.getTimeslot() == 2));
|
||||
|
||||
String filterText = getSearchText().getText();
|
||||
|
||||
if(filterText == null || filterText.isEmpty())
|
||||
{
|
||||
mFilteredMessages.setPredicate(timeslotPredicate);
|
||||
}
|
||||
else
|
||||
{
|
||||
Predicate<IMessage> textPredicate = message -> message.toString().toLowerCase().contains(filterText.toLowerCase());
|
||||
mFilteredMessages.setPredicate(timeslotPredicate.and(textPredicate));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and selects the first row containing the text argument.
|
||||
* @param text to search for.
|
||||
*/
|
||||
private void find(String text)
|
||||
{
|
||||
if(text != null && !text.isEmpty())
|
||||
{
|
||||
for(IMessage message: mFilteredMessages)
|
||||
{
|
||||
if(message.toString().toLowerCase().contains(text.toLowerCase()))
|
||||
{
|
||||
getMessageTableView().getSelectionModel().select(message);
|
||||
getMessageTableView().scrollTo(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and selects the first row containing the text argument, after the currently selected row.
|
||||
* @param text to search for.
|
||||
*/
|
||||
private void findNext(String text)
|
||||
{
|
||||
if(text != null && !text.isEmpty())
|
||||
{
|
||||
IMessage selected = getMessageTableView().getSelectionModel().getSelectedItem();
|
||||
|
||||
if(selected == null)
|
||||
{
|
||||
find(text);
|
||||
return;
|
||||
}
|
||||
|
||||
int row = mFilteredMessages.indexOf(selected);
|
||||
|
||||
for(int x = row + 1; x < mFilteredMessages.size(); x++)
|
||||
{
|
||||
if(x < mFilteredMessages.size())
|
||||
{
|
||||
IMessage message = mFilteredMessages.get(x);
|
||||
|
||||
if(message.toString().toLowerCase().contains(text.toLowerCase()))
|
||||
{
|
||||
getMessageTableView().getSelectionModel().select(message);
|
||||
getMessageTableView().scrollTo(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List view control with DMR messages
|
||||
*/
|
||||
private TableView<IMessage> getMessageTableView()
|
||||
{
|
||||
if(mMessageTableView == null)
|
||||
{
|
||||
mMessageTableView = new TableView<>(mFilteredMessages);
|
||||
|
||||
TableColumn timestampColumn = new TableColumn();
|
||||
timestampColumn.setPrefWidth(110);
|
||||
timestampColumn.setText("Time");
|
||||
timestampColumn.setCellValueFactory(new PropertyValueFactory<>("timestamp"));
|
||||
|
||||
TableColumn timeslotColumn = new TableColumn();
|
||||
timeslotColumn.setPrefWidth(35);
|
||||
timeslotColumn.setText("TS");
|
||||
timeslotColumn.setCellValueFactory(new PropertyValueFactory<>("timeslot"));
|
||||
|
||||
TableColumn validColumn = new TableColumn();
|
||||
validColumn.setPrefWidth(50);
|
||||
validColumn.setText("Valid");
|
||||
validColumn.setCellValueFactory(new PropertyValueFactory<>("valid"));
|
||||
|
||||
TableColumn messageColumn = new TableColumn();
|
||||
messageColumn.setPrefWidth(1000);
|
||||
messageColumn.setText("Message");
|
||||
messageColumn.setCellValueFactory((Callback<TableColumn.CellDataFeatures, ObservableValue>) param -> {
|
||||
SimpleStringProperty property = new SimpleStringProperty();
|
||||
if(param.getValue() instanceof IMessage message)
|
||||
{
|
||||
property.set(message.toString());
|
||||
}
|
||||
|
||||
return property;
|
||||
});
|
||||
|
||||
mMessageTableView.getColumns().addAll(timestampColumn, validColumn, timeslotColumn, messageColumn);
|
||||
}
|
||||
|
||||
return mMessageTableView;
|
||||
}
|
||||
|
||||
/**
|
||||
* File selection button
|
||||
* @return button
|
||||
*/
|
||||
private Button getSelectFileButton()
|
||||
{
|
||||
if(mSelectFileButton == null)
|
||||
{
|
||||
mSelectFileButton = new Button("Select ...");
|
||||
mSelectFileButton.onActionProperty().set(event -> {
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("Select DMR .bits Recording");
|
||||
String lastDirectory = mPreferences.get(LAST_SELECTED_DIRECTORY, null);
|
||||
if(lastDirectory != null)
|
||||
{
|
||||
File file = new File(lastDirectory);
|
||||
if(file.exists() && file.isDirectory())
|
||||
{
|
||||
fileChooser.setInitialDirectory(file);
|
||||
}
|
||||
}
|
||||
fileChooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter("sdrtrunk bits recording", "*.bits"));
|
||||
File selected = fileChooser.showOpenDialog(getScene().getWindow());
|
||||
|
||||
if(selected != null)
|
||||
{
|
||||
mLog.info("Last Selected Directory updated to [" + selected.getParent() +"]");
|
||||
mPreferences.put(LAST_SELECTED_DIRECTORY, selected.getParent());
|
||||
load(selected);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mSelectFileButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selected file path label.
|
||||
*/
|
||||
private Label getSelectedFileLabel()
|
||||
{
|
||||
if(mSelectedFileLabel == null)
|
||||
{
|
||||
mSelectedFileLabel = new Label(" ");
|
||||
}
|
||||
|
||||
return mSelectedFileLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check box to apply filter to show/hide TS0 messages
|
||||
* @return check box control
|
||||
*/
|
||||
private CheckBox getShowTS0()
|
||||
{
|
||||
if(mShowTS0 == null)
|
||||
{
|
||||
mShowTS0 = new CheckBox("TS0");
|
||||
mShowTS0.setSelected(true);
|
||||
mShowTS0.setOnAction(event -> updateFilters());
|
||||
}
|
||||
|
||||
return mShowTS0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check box to apply filter to show/hide TS0 messages
|
||||
* @return check box control
|
||||
*/
|
||||
private CheckBox getShowTS1()
|
||||
{
|
||||
if(mShowTS1 == null)
|
||||
{
|
||||
mShowTS1 = new CheckBox("TS1");
|
||||
mShowTS1.setSelected(true);
|
||||
mShowTS1.setOnAction(event -> updateFilters());
|
||||
}
|
||||
|
||||
return mShowTS1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check box to apply filter to show/hide TS0 messages
|
||||
* @return check box control
|
||||
*/
|
||||
private CheckBox getShowTS2()
|
||||
{
|
||||
if(mShowTS2 == null)
|
||||
{
|
||||
mShowTS2 = new CheckBox("TS2");
|
||||
mShowTS2.setSelected(true);
|
||||
mShowTS2.setOnAction(event -> updateFilters());
|
||||
}
|
||||
|
||||
return mShowTS2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search text filter box
|
||||
* @return text control for entering search text
|
||||
*/
|
||||
private TextField getSearchText()
|
||||
{
|
||||
if(mSearchText == null)
|
||||
{
|
||||
mSearchText = new TextField();
|
||||
mSearchText.textProperty().addListener((observable, oldValue, newValue) -> updateFilters());
|
||||
}
|
||||
|
||||
return mSearchText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text box for find text
|
||||
*/
|
||||
private TextField getFindText()
|
||||
{
|
||||
if(mFindText == null)
|
||||
{
|
||||
mFindText = new TextField();
|
||||
mFindText.textProperty().addListener((observable, oldValue, newValue) -> updateFilters());
|
||||
}
|
||||
|
||||
return mFindText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find button to search for the text in the find text box.
|
||||
* @return button
|
||||
*/
|
||||
private Button getFindButton()
|
||||
{
|
||||
if(mFindButton == null)
|
||||
{
|
||||
mFindButton = new Button("Find");
|
||||
mFindButton.setOnAction(event -> find(getFindText().getText()));
|
||||
}
|
||||
|
||||
return mFindButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find next button to search for the text in the find text box.
|
||||
* @return button
|
||||
*/
|
||||
private Button getFindNextButton()
|
||||
{
|
||||
if(mFindNextButton == null)
|
||||
{
|
||||
mFindNextButton = new Button("Next");
|
||||
mFindNextButton.setOnAction(event -> findNext(getFindText().getText()));
|
||||
}
|
||||
|
||||
return mFindNextButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Hytera Tier 3 Compressed Talkgroups
|
||||
* @return
|
||||
*/
|
||||
private CheckBox getUseCompressedTalkgroups()
|
||||
{
|
||||
if(mUseCompressedTalkgroups == null)
|
||||
{
|
||||
mUseCompressedTalkgroups = new CheckBox("Use Compressed Talkgroups");
|
||||
}
|
||||
|
||||
return mUseCompressedTalkgroups;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Application viewer = new Application()
|
||||
{
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception
|
||||
{
|
||||
Scene scene = new Scene(new DMRRecordingViewer(), 1100, 800);
|
||||
primaryStage.setTitle("DMR Recording Viewer");
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show();
|
||||
}
|
||||
};
|
||||
|
||||
Runnable r = () -> {
|
||||
try
|
||||
{
|
||||
viewer.start(new Stage());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error starting DMR recording viewer application", e);
|
||||
}
|
||||
};
|
||||
|
||||
Platform.startup(r);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.gui.dmr;
|
||||
|
||||
import io.github.dsheirer.gui.JavaFxWindowRequest;
|
||||
|
||||
/**
|
||||
* Request to show the DMR recording viewer
|
||||
*/
|
||||
public class ViewDmrRecordingViewerRequest extends JavaFxWindowRequest
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public ViewDmrRecordingViewerRequest() {}
|
||||
}
|
||||
@@ -217,6 +217,9 @@ public class RadioIdEditor extends IdentifierEditor<Radio>
|
||||
}
|
||||
}
|
||||
|
||||
mLog.warn("Unable to find radio id editor for protocol [" + protocol + "] and format [" + integerFormat +
|
||||
"] - using default editor");
|
||||
|
||||
//Use a default instance
|
||||
for(RadioDetail detail: mRadioDetails)
|
||||
{
|
||||
|
||||
@@ -159,7 +159,7 @@ public class RadioIdRangeEditor extends IdentifierEditor<RadioRange>
|
||||
else
|
||||
{
|
||||
mLog.warn("Couldn't find radio ID detail for protocol [" + getItem().getProtocol() +
|
||||
"] and format [" + format + "]");
|
||||
"] and format [" + format + "]");
|
||||
getFormatLabel().setText(" ");
|
||||
}
|
||||
}
|
||||
@@ -267,6 +267,9 @@ public class RadioIdRangeEditor extends IdentifierEditor<RadioRange>
|
||||
}
|
||||
}
|
||||
|
||||
mLog.warn("Unable to find radio id range editor for protocol [" + protocol + "] and format [" + integerFormat +
|
||||
"] - using default editor");
|
||||
|
||||
//Use a default instance
|
||||
for(RadioIdDetail detail: mRadioDetails)
|
||||
{
|
||||
@@ -287,20 +290,23 @@ public class RadioIdRangeEditor extends IdentifierEditor<RadioRange>
|
||||
{
|
||||
List<RadioIdDetail> details = new ArrayList<>();
|
||||
details.add(new RadioIdDetail(Protocol.APCO25, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFFFF),
|
||||
new IntegerFormatter(0,0xFFFFFF), "Format: 0 - 16777215"));
|
||||
new IntegerFormatter(0,0xFFFFFF), "Format: 0 - 16777215"));
|
||||
details.add(new RadioIdDetail(Protocol.APCO25, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFFFF),
|
||||
new HexFormatter(0,0xFFFFFF), "Format: 0 - FFFFFF"));
|
||||
new HexFormatter(0,0xFFFFFF), "Format: 0 - FFFFFF"));
|
||||
details.add(new RadioIdDetail(Protocol.PASSPORT, IntegerFormat.DECIMAL, new IntegerFormatter(0,0x7FFFFF),
|
||||
new IntegerFormatter(0,0x7FFFFF), "Format: 0 - 8388607"));
|
||||
new IntegerFormatter(0,0x7FFFFF), "Format: 0 - 8388607"));
|
||||
details.add(new RadioIdDetail(Protocol.PASSPORT, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0x7FFFFF),
|
||||
new HexFormatter(0,0x7FFFFF), "Format: 0 - 7FFFFF"));
|
||||
new HexFormatter(0,0x7FFFFF), "Format: 0 - 7FFFFF"));
|
||||
details.add(new RadioIdDetail(Protocol.UNKNOWN, IntegerFormat.DECIMAL, new IntegerFormatter(0,16777215),
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
details.add(new RadioIdDetail(Protocol.UNKNOWN, IntegerFormat.FORMATTED, new IntegerFormatter(0,16777215),
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
details.add(new RadioIdDetail(Protocol.UNKNOWN, IntegerFormat.HEXADECIMAL, new HexFormatter(0,16777215),
|
||||
new HexFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
|
||||
new HexFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
details.add(new RadioIdDetail(Protocol.DMR, IntegerFormat.DECIMAL, new IntegerFormatter(0,16777215),
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - 16777215"));
|
||||
details.add(new RadioIdDetail(Protocol.DMR, IntegerFormat.HEXADECIMAL, new HexFormatter(0,16777215),
|
||||
new HexFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
return details;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ public class TalkgroupEditor extends IdentifierEditor<Talkgroup>
|
||||
else
|
||||
{
|
||||
mLog.warn("Couldn't find talkgroup detail for protocol [" + getItem().getProtocol() +
|
||||
"] and format [" + format + "]");
|
||||
"] and format [" + format + "]");
|
||||
}
|
||||
|
||||
getFormatLabel().setText(talkgroupDetail.getTooltip());
|
||||
@@ -219,6 +219,9 @@ public class TalkgroupEditor extends IdentifierEditor<Talkgroup>
|
||||
}
|
||||
}
|
||||
|
||||
mLog.warn("Unable to find talkgroup editor for protocol [" + protocol + "] and format [" + integerFormat +
|
||||
"] - using default editor");
|
||||
|
||||
//Use a default instance
|
||||
for(TalkgroupDetail detail: mTalkgroupDetails)
|
||||
{
|
||||
@@ -236,37 +239,37 @@ public class TalkgroupEditor extends IdentifierEditor<Talkgroup>
|
||||
{
|
||||
mTalkgroupDetails.clear();
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.APCO25, IntegerFormat.DECIMAL, new IntegerFormatter(0,65535),
|
||||
"Format: 0 - 65535"));
|
||||
"Format: 0 - 65535"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.APCO25, IntegerFormat.HEXADECIMAL, new HexFormatter(0,65535),
|
||||
"Format: 0 - FFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.DMR, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFFFF),
|
||||
"Format: 0 - 16,777,215"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.DMR, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFFFF),
|
||||
"Format: 0 - FFFFFF"));
|
||||
"Format: 0 - FFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.DMR, IntegerFormat.DECIMAL, new IntegerFormatter(1,0xFFFFFF),
|
||||
"Format: 1 - 16,777,215"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.DMR, IntegerFormat.HEXADECIMAL, new HexFormatter(1,0xFFFFFF),
|
||||
"Format: 1 - FFFFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.FLEETSYNC, IntegerFormat.FORMATTED,
|
||||
new PrefixIdentFormatter(0,0xFFFFF), "Format: PPP-IIII = Prefix (0-127), Ident (0-8191)"));
|
||||
new PrefixIdentFormatter(0,0xFFFFF), "Format: PPP-IIII = Prefix (0-127), Ident (0-8191)"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.LTR, IntegerFormat.FORMATTED, new LtrFormatter(0,0x3FFF),
|
||||
"Format: A-HH-TTT = Area (0-1), Home (1-31), Talkgroup (1-255)"));
|
||||
"Format: A-HH-TTT = Area (0-1), Home (1-31), Talkgroup (1-255)"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.MDC1200, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFF),
|
||||
"Format: 0 - 65535"));
|
||||
"Format: 0 - 65535"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.MDC1200, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFF),
|
||||
"Format: 0 - FFFF"));
|
||||
"Format: 0 - FFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.MPT1327, IntegerFormat.FORMATTED,
|
||||
new PrefixIdentFormatter(0,0xFFFFF), "Format: PPP-IIII = Prefix (0-127), Ident (0-8191)"));
|
||||
new PrefixIdentFormatter(0,0xFFFFF), "Format: PPP-IIII = Prefix (0-127), Ident (0-8191)"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.NBFM, IntegerFormat.DECIMAL, new IntegerFormatter(1,0xFFFF),
|
||||
"Format: 1 - 65535"));
|
||||
"Format: 1 - 65535"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.NBFM, IntegerFormat.HEXADECIMAL, new HexFormatter(1,0xFFFF),
|
||||
"Format: 1 - FFFF"));
|
||||
"Format: 1 - FFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.PASSPORT, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFF),
|
||||
"Format: 0 - 65535"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.PASSPORT, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFF),
|
||||
"Format: 0 - FFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.UNKNOWN, IntegerFormat.DECIMAL, new IntegerFormatter(0,16777215),
|
||||
"Format: 0 - FFFFFF"));
|
||||
"Format: 0 - FFFFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.UNKNOWN, IntegerFormat.FORMATTED, new IntegerFormatter(0,16777215),
|
||||
"Format: 0 - FFFFFF"));
|
||||
"Format: 0 - FFFFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.UNKNOWN, IntegerFormat.HEXADECIMAL, new HexFormatter(0,16777215),
|
||||
"Format: 0 - FFFFFF"));
|
||||
"Format: 0 - FFFFFF"));
|
||||
}
|
||||
|
||||
public static class TalkgroupDetail
|
||||
|
||||
@@ -161,7 +161,7 @@ public class TalkgroupRangeEditor extends IdentifierEditor<TalkgroupRange>
|
||||
else
|
||||
{
|
||||
mLog.warn("Couldn't find talkgroup detail for protocol [" + getItem().getProtocol() +
|
||||
"] and format [" + format + "]");
|
||||
"] and format [" + format + "]");
|
||||
getFormatLabel().setText(" ");
|
||||
}
|
||||
}
|
||||
@@ -269,6 +269,9 @@ public class TalkgroupRangeEditor extends IdentifierEditor<TalkgroupRange>
|
||||
}
|
||||
}
|
||||
|
||||
mLog.warn("Unable to find talkgroup range editor for protocol [" + protocol + "] and format [" + integerFormat +
|
||||
"] - using default editor");
|
||||
|
||||
//Use a default instance
|
||||
for(TalkgroupDetail detail: mTalkgroupDetails)
|
||||
{
|
||||
@@ -289,31 +292,35 @@ public class TalkgroupRangeEditor extends IdentifierEditor<TalkgroupRange>
|
||||
{
|
||||
List<TalkgroupDetail> details = new ArrayList<>();
|
||||
details.add(new TalkgroupDetail(Protocol.APCO25, IntegerFormat.DECIMAL, new IntegerFormatter(0,65535),
|
||||
new IntegerFormatter(0,65535), "Format: 0 - 65535"));
|
||||
new IntegerFormatter(0,65535), "Format: 0 - 65535"));
|
||||
details.add(new TalkgroupDetail(Protocol.APCO25, IntegerFormat.HEXADECIMAL, new HexFormatter(0,65535),
|
||||
new HexFormatter(0,65535), "Format: 0 - FFFF"));
|
||||
new HexFormatter(0,65535), "Format: 0 - FFFF"));
|
||||
details.add(new TalkgroupDetail(Protocol.FLEETSYNC, IntegerFormat.FORMATTED,
|
||||
new PrefixIdentFormatter(0,0xFFFFF), new PrefixIdentFormatter(0,0xFFFFF),
|
||||
"Format: PPP-IIII = Prefix (0-127), Ident (0-8191)"));
|
||||
new PrefixIdentFormatter(0,0xFFFFF), new PrefixIdentFormatter(0,0xFFFFF),
|
||||
"Format: PPP-IIII = Prefix (0-127), Ident (0-8191)"));
|
||||
details.add(new TalkgroupDetail(Protocol.LTR, IntegerFormat.FORMATTED, new LtrFormatter(0,0x3FFF),
|
||||
new LtrFormatter(0,0x3FFF), "Format: A-HH-TTT = Area (0-1), Home (1-31), Talkgroup (1-255)"));
|
||||
new LtrFormatter(0,0x3FFF), "Format: A-HH-TTT = Area (0-1), Home (1-31), Talkgroup (1-255)"));
|
||||
details.add(new TalkgroupDetail(Protocol.MDC1200, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFF),
|
||||
new IntegerFormatter(0,0xFFFF), "Format: 0 - 65535"));
|
||||
new IntegerFormatter(0,0xFFFF), "Format: 0 - 65535"));
|
||||
details.add(new TalkgroupDetail(Protocol.MDC1200, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFF),
|
||||
new HexFormatter(0,0xFFFF), "Format: 0 - FFFF"));
|
||||
new HexFormatter(0,0xFFFF), "Format: 0 - FFFF"));
|
||||
details.add(new TalkgroupDetail(Protocol.MPT1327, IntegerFormat.FORMATTED,
|
||||
new PrefixIdentFormatter(0,0xFFFFF), new PrefixIdentFormatter(0,0xFFFFF),
|
||||
"Format: PPP-IIII = Prefix (0-127), Ident (1-8191)"));
|
||||
new PrefixIdentFormatter(0,0xFFFFF), new PrefixIdentFormatter(0,0xFFFFF),
|
||||
"Format: PPP-IIII = Prefix (0-127), Ident (1-8191)"));
|
||||
details.add(new TalkgroupDetail(Protocol.PASSPORT, IntegerFormat.DECIMAL, new IntegerFormatter(0,0xFFFF),
|
||||
new IntegerFormatter(0,0xFFFF), "Format: 0 - 65535"));
|
||||
new IntegerFormatter(0,0xFFFF), "Format: 0 - 65535"));
|
||||
details.add(new TalkgroupDetail(Protocol.PASSPORT, IntegerFormat.HEXADECIMAL, new HexFormatter(0,0xFFFF),
|
||||
new HexFormatter(0,0xFFFF), "Format: 0 - FFFF"));
|
||||
new HexFormatter(0,0xFFFF), "Format: 0 - FFFF"));
|
||||
details.add(new TalkgroupDetail(Protocol.UNKNOWN, IntegerFormat.DECIMAL, new IntegerFormatter(0,16777215),
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - 16777215"));
|
||||
details.add(new TalkgroupDetail(Protocol.UNKNOWN, IntegerFormat.FORMATTED, new IntegerFormatter(0,16777215),
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
new IntegerFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
details.add(new TalkgroupDetail(Protocol.UNKNOWN, IntegerFormat.HEXADECIMAL, new HexFormatter(0,16777215),
|
||||
new HexFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
new HexFormatter(0,16777215), "Format: 0 - FFFFFF"));
|
||||
details.add(new TalkgroupDetail(Protocol.DMR, IntegerFormat.DECIMAL, new IntegerFormatter(1,16777215),
|
||||
new IntegerFormatter(1,16777215), "Format: 1 - 16777215"));
|
||||
details.add(new TalkgroupDetail(Protocol.DMR, IntegerFormat.HEXADECIMAL, new HexFormatter(1,0XFFFFFF),
|
||||
new HexFormatter(1, 0xFFFFFF), "Format: 0 - FFFFFF"));
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -38,6 +38,8 @@ import io.github.dsheirer.record.RecorderType;
|
||||
import io.github.dsheirer.record.config.RecordConfiguration;
|
||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
@@ -65,9 +67,6 @@ import org.controlsfx.control.ToggleSwitch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DMR channel configuration editor
|
||||
*/
|
||||
@@ -83,6 +82,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
private RecordConfigurationEditor mRecordConfigurationEditor;
|
||||
private ToggleSwitch mIgnoreDataCallsButton;
|
||||
private ToggleSwitch mIgnoreCRCChecksumsButton;
|
||||
private ToggleSwitch mUseCompressedTalkgroupsToggle;
|
||||
private Spinner<Integer> mTrafficChannelPoolSizeSpinner;
|
||||
private TableView<TimeslotFrequency> mTimeslotFrequencyTable;
|
||||
private IntegerTextField mLogicalSlotNumberField;
|
||||
@@ -164,6 +164,14 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
GridPane.setConstraints(ignoreCRCLabel, 5, row);
|
||||
gridPane.getChildren().add(ignoreCRCLabel);
|
||||
|
||||
GridPane.setConstraints(getUseCompressedTalkgroupsToggle(), 6, row);
|
||||
gridPane.getChildren().add(getUseCompressedTalkgroupsToggle());
|
||||
|
||||
Label useCompressedTalkgroupsLabel = new Label("Use Compressed Talkgroups");
|
||||
GridPane.setHalignment(useCompressedTalkgroupsLabel, HPos.LEFT);
|
||||
GridPane.setConstraints(useCompressedTalkgroupsLabel, 7, row);
|
||||
gridPane.getChildren().add(useCompressedTalkgroupsLabel);
|
||||
|
||||
Label timeslotTableLabel = new Label("Logical Slot Number (LSN) to Frequency Map. Required for: Connect Plus and Tier-III systems that don't use absolute frequencies");
|
||||
GridPane.setHalignment(timeslotTableLabel, HPos.LEFT);
|
||||
GridPane.setConstraints(timeslotTableLabel, 0, ++row, 6, 1);
|
||||
@@ -512,6 +520,24 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
return mIgnoreCRCChecksumsButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use compressed talkgroups toggle switch. Let's the user select to turn on compressed talkgroups for Hytera
|
||||
* Tier-III systems.
|
||||
* @return toggle.
|
||||
*/
|
||||
private ToggleSwitch getUseCompressedTalkgroupsToggle()
|
||||
{
|
||||
if(mUseCompressedTalkgroupsToggle == null)
|
||||
{
|
||||
mUseCompressedTalkgroupsToggle = new ToggleSwitch();
|
||||
mUseCompressedTalkgroupsToggle.setTooltip(new Tooltip("Use Compressed Talkgroups. This is only for Hytera Tier-III Trunked Systems"));
|
||||
mUseCompressedTalkgroupsToggle.setDisable(true);
|
||||
mUseCompressedTalkgroupsToggle.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
}
|
||||
|
||||
return mUseCompressedTalkgroupsToggle;
|
||||
}
|
||||
|
||||
private Spinner<Integer> getTrafficChannelPoolSizeSpinner()
|
||||
{
|
||||
if(mTrafficChannelPoolSizeSpinner == null)
|
||||
@@ -578,6 +604,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
getIgnoreCRCChecksumsButton().setDisable(config == null);
|
||||
getIgnoreDataCallsButton().setDisable(config == null);
|
||||
getUseCompressedTalkgroupsToggle().setDisable(config == null);
|
||||
getTrafficChannelPoolSizeSpinner().setDisable(config == null);
|
||||
getTimeslotTable().getItems().clear();
|
||||
getTimeslotTable().setDisable(config == null);
|
||||
@@ -597,6 +624,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
|
||||
getIgnoreDataCallsButton().setSelected(decodeConfig.getIgnoreDataCalls());
|
||||
getIgnoreCRCChecksumsButton().setSelected(decodeConfig.getIgnoreCRCChecksums());
|
||||
getUseCompressedTalkgroupsToggle().setSelected(decodeConfig.isUseCompressedTalkgroups());
|
||||
getTrafficChannelPoolSizeSpinner().getValueFactory().setValue(decodeConfig.getTrafficChannelPoolSize());
|
||||
|
||||
for(TimeslotFrequency timeslotFrequency: decodeConfig.getTimeslotMap())
|
||||
@@ -608,6 +636,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
getIgnoreCRCChecksumsButton().setSelected(false);
|
||||
getIgnoreDataCallsButton().setSelected(false);
|
||||
getUseCompressedTalkgroupsToggle().setSelected(false);
|
||||
getTrafficChannelPoolSizeSpinner().getValueFactory().setValue(0);
|
||||
getChannelRotationDelaySpinner().getValueFactory().setValue(200);
|
||||
}
|
||||
@@ -630,6 +659,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
config.setIgnoreCRCChecksums(getIgnoreCRCChecksumsButton().isSelected());
|
||||
config.setIgnoreDataCalls(getIgnoreDataCallsButton().isSelected());
|
||||
config.setTrafficChannelPoolSize(getTrafficChannelPoolSizeSpinner().getValue());
|
||||
config.setUseCompressedTalkgroups(getUseCompressedTalkgroupsToggle().isSelected());
|
||||
config.setTimeslotMap(new ArrayList<>(getTimeslotTable().getItems()));
|
||||
getItem().setDecodeConfiguration(config);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public class PreferenceEditorFactory
|
||||
return new DirectoryPreferenceEditor(userPreferences);
|
||||
case JMBE_LIBRARY:
|
||||
return new JmbeLibraryPreferenceEditor(userPreferences);
|
||||
case SOURCE_TUNER_CHANNELIZER:
|
||||
case SOURCE_TUNERS:
|
||||
return new TunerPreferenceEditor(userPreferences);
|
||||
case TALKGROUP_FORMAT:
|
||||
return new TalkgroupFormatPreferenceEditor(userPreferences);
|
||||
|
||||
@@ -31,7 +31,7 @@ public enum PreferenceEditorType
|
||||
AUDIO_RECORD("Record"),
|
||||
AUDIO_OUTPUT("Output/Tones"),
|
||||
AUDIO_DUPLICATE_CALL_DETECTION("Duplicate Calls"),
|
||||
SOURCE_TUNER_CHANNELIZER("Tuner Channelizer"),
|
||||
SOURCE_TUNERS("Tuners"),
|
||||
TALKGROUP_FORMAT("Talkgroup & Radio ID"),
|
||||
VECTOR_CALIBRATION("Vector Calibration"),
|
||||
DEFAULT("Default");
|
||||
|
||||
@@ -22,6 +22,8 @@ package io.github.dsheirer.gui.preference;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.gui.playlist.ViewPlaylistRequest;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
@@ -44,9 +46,6 @@ import javafx.stage.Stage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Preferences editor dialog
|
||||
*/
|
||||
@@ -196,7 +195,7 @@ public class UserPreferencesEditor extends BorderPane
|
||||
storageItem.setExpanded(true);
|
||||
|
||||
TreeItem<String> sourceItem = new TreeItem<>("Source");
|
||||
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.SOURCE_TUNER_CHANNELIZER));
|
||||
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.SOURCE_TUNERS));
|
||||
treeRoot.getChildren().add(sourceItem);
|
||||
sourceItem.setExpanded(true);
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.gui.preference.tuner;
|
||||
|
||||
/**
|
||||
* RSPduo tuner select mode preferences.
|
||||
*/
|
||||
public enum RspDuoSelectionMode
|
||||
{
|
||||
DUAL("Dual Tuner"),
|
||||
SINGLE_1("Single Tuner 1"),
|
||||
SINGLE_2("Single Tuner 2");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
RspDuoSelectionMode(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the enum entry from the value.
|
||||
* @param value to match
|
||||
* @return matched value or (default) DUAL if the value couldn't be matched.
|
||||
*/
|
||||
public static RspDuoSelectionMode fromValue(String value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return RspDuoSelectionMode.valueOf(value);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//Do nothing
|
||||
}
|
||||
|
||||
return DUAL;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 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.gui.preference.tuner;
|
||||
@@ -55,6 +52,8 @@ public class TunerPreferenceEditor extends HBox
|
||||
private Label mHelpTextPolyphaseLabel;
|
||||
private Label mHeterodyneLabel;
|
||||
private Label mHelpTextHeterodyneLabel;
|
||||
private ChoiceBox<RspDuoSelectionMode> mRspDuoTunerModeChoiceBox;
|
||||
private Label mRspDuoModeLabel;
|
||||
|
||||
public TunerPreferenceEditor(UserPreferences userPreferences)
|
||||
{
|
||||
@@ -66,19 +65,24 @@ public class TunerPreferenceEditor extends HBox
|
||||
{
|
||||
if(mEditorPane == null)
|
||||
{
|
||||
int row = 0;
|
||||
mEditorPane = new GridPane();
|
||||
mEditorPane.setVgap(10);
|
||||
mEditorPane.setHgap(10);
|
||||
mEditorPane.setPadding(new Insets(10, 10, 10, 10));
|
||||
GridPane.setHalignment(getChannelizerLabel(), HPos.LEFT);
|
||||
mEditorPane.add(getChannelizerLabel(), 0, 0);
|
||||
mEditorPane.add(getChannelizerTypeChoiceBox(), 1, 0);
|
||||
mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, 1, 2, 1);
|
||||
mEditorPane.add(getPolyphaseLabel(), 0, 2, 2, 1);
|
||||
mEditorPane.add(getHelpTextPolyphaseLabel(), 0, 3, 2, 3);
|
||||
mEditorPane.add(new Label(" "), 0, 6);
|
||||
mEditorPane.add(getHeterodyneLabel(), 0, 7, 2, 1);
|
||||
mEditorPane.add(getHelpTextHeterodyneLabel(), 0, 8, 2, 3);
|
||||
GridPane.setHalignment(getChannelizerLabel(), HPos.RIGHT);
|
||||
mEditorPane.add(getChannelizerLabel(), 0, row);
|
||||
mEditorPane.add(getChannelizerTypeChoiceBox(), 1, row);
|
||||
mEditorPane.add(getPolyphaseLabel(), 0, ++row, 2, 1);
|
||||
mEditorPane.add(getHelpTextPolyphaseLabel(), 0, ++row, 2, 3);
|
||||
row += 3;
|
||||
mEditorPane.add(new Label(" "), 0, row);
|
||||
mEditorPane.add(getHeterodyneLabel(), 0, ++row, 2, 1);
|
||||
mEditorPane.add(getHelpTextHeterodyneLabel(), 0, ++row, 2, 3);
|
||||
row += 3;
|
||||
mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, row, 2, 1);
|
||||
mEditorPane.add(getRspDuoModeLabel(), 0, ++row);
|
||||
mEditorPane.add(getRspDuoTunerModeChoiceBox(), 1, row);
|
||||
}
|
||||
|
||||
return mEditorPane;
|
||||
@@ -161,4 +165,40 @@ public class TunerPreferenceEditor extends HBox
|
||||
|
||||
return mHelpTextHeterodyneLabel;
|
||||
}
|
||||
|
||||
private ChoiceBox<RspDuoSelectionMode> getRspDuoTunerModeChoiceBox()
|
||||
{
|
||||
if(mRspDuoTunerModeChoiceBox == null)
|
||||
{
|
||||
mRspDuoTunerModeChoiceBox = new ChoiceBox<>();
|
||||
mRspDuoTunerModeChoiceBox.getItems().addAll(RspDuoSelectionMode.values());
|
||||
|
||||
RspDuoSelectionMode current = mTunerPreference.getRspDuoTunerMode();
|
||||
mRspDuoTunerModeChoiceBox.getSelectionModel().select(current);
|
||||
|
||||
mRspDuoTunerModeChoiceBox.setOnAction(event -> {
|
||||
RspDuoSelectionMode selected = mRspDuoTunerModeChoiceBox.getSelectionModel().getSelectedItem();
|
||||
mTunerPreference.setRspDuoTunerMode(selected);
|
||||
|
||||
Label label = new Label("Please restart the application for this change to take effect");
|
||||
label.setWrapText(true);
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.getDialogPane().setContent(label);
|
||||
alert.initOwner(((Node)getRspDuoTunerModeChoiceBox()).getScene().getWindow());
|
||||
alert.show();
|
||||
});
|
||||
}
|
||||
|
||||
return mRspDuoTunerModeChoiceBox;
|
||||
}
|
||||
|
||||
private Label getRspDuoModeLabel()
|
||||
{
|
||||
if(mRspDuoModeLabel == null)
|
||||
{
|
||||
mRspDuoModeLabel = new Label("SDRPlay RSPduo Selection Mode");
|
||||
}
|
||||
|
||||
return mRspDuoModeLabel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -53,6 +53,7 @@ public enum Form
|
||||
SITE,
|
||||
STATE,
|
||||
SYSTEM,
|
||||
TALKER_ALIAS,
|
||||
TALKGROUP,
|
||||
TELEPHONE_NUMBER,
|
||||
TONE,
|
||||
|
||||
@@ -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,6 @@
|
||||
package io.github.dsheirer.identifier;
|
||||
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -70,6 +69,15 @@ public abstract class Identifier<T>
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow subclass implementations to set or update the value after construction.
|
||||
* @param value to update or set.
|
||||
*/
|
||||
protected void setValue(T value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class/type of identifier
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.identifier.alias;
|
||||
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Talker alias value provided by the network for the current talker (ie FROM).
|
||||
*/
|
||||
public class DmrTalkerAliasIdentifier extends TalkerAliasIdentifier
|
||||
{
|
||||
/**
|
||||
* Constructs an instance.
|
||||
* @param value of the talker alias
|
||||
*/
|
||||
public DmrTalkerAliasIdentifier(String value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return getValue() != null && !getValue().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return Protocol.DMR;
|
||||
}
|
||||
|
||||
public static DmrTalkerAliasIdentifier create(String value)
|
||||
{
|
||||
return new DmrTalkerAliasIdentifier(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.identifier.alias;
|
||||
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.IdentifierClass;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.string.StringIdentifier;
|
||||
|
||||
/**
|
||||
* Talker alias value provided by the network for the current talker (ie FROM).
|
||||
*/
|
||||
public abstract class TalkerAliasIdentifier extends StringIdentifier
|
||||
{
|
||||
/**
|
||||
* Constructs an instance.
|
||||
* @param value of the string/alias identifier
|
||||
*/
|
||||
public TalkerAliasIdentifier(String value)
|
||||
{
|
||||
super(value, IdentifierClass.USER, Form.TALKER_ALIAS, Role.FROM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return getValue() != null && !getValue().isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -59,6 +59,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.header.HeaderMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.GPSInformation;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.GroupVoiceChannelUser;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasComplete;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.UnitToUnitVoiceChannelUser;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraGroupVoiceChannelUser;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraUnitToUnitVoiceChannelUser;
|
||||
@@ -66,6 +67,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.Capaci
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusWideAreaVoiceChannelUser;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.CapacityPlusRestChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.terminator.Terminator;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.ServiceOptions;
|
||||
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
|
||||
@@ -75,13 +77,12 @@ import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import io.github.dsheirer.source.tuner.channel.rotation.AddChannelRotationActiveStateRequest;
|
||||
import io.github.dsheirer.util.PacketUtil;
|
||||
import org.jdesktop.swingx.mapviewer.GeoPosition;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import org.jdesktop.swingx.mapviewer.GeoPosition;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Decoder state for an DMR channel. Maintains the call/data/idle state of the channel and produces events by
|
||||
@@ -207,6 +208,10 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
processPacket((DMRPacketMessage)message);
|
||||
}
|
||||
else if(message instanceof UDTShortMessageService sms)
|
||||
{
|
||||
processSMS(sms);
|
||||
}
|
||||
else if(message instanceof DMRMessage)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE, getTimeslot()));
|
||||
@@ -269,6 +274,24 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a short data message carrying SMS text
|
||||
* @param sms
|
||||
*/
|
||||
private void processSMS(UDTShortMessageService sms)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
|
||||
|
||||
DecodeEvent smsEvent = DMRDecodeEvent.builder(sms.getTimestamp())
|
||||
.eventType(DecodeEventType.SMS)
|
||||
.eventDescription(DecodeEventType.SMS.toString())
|
||||
.details("MESSAGE: " + sms.getSMS())
|
||||
.identifiers(new IdentifierCollection(sms.getIdentifiers()))
|
||||
.timeslot(sms.getTimeslot())
|
||||
.build();
|
||||
broadcast(smsEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a packet message
|
||||
*/
|
||||
@@ -1127,6 +1150,12 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
broadcast(gpsEvent);
|
||||
}
|
||||
break;
|
||||
case FULL_STANDARD_TALKER_ALIAS_COMPLETE:
|
||||
if(message instanceof TalkerAliasComplete tac && tac.hasTalkerAliasIdentifier())
|
||||
{
|
||||
getIdentifierCollection().update(tac.getTalkerAliasIdentifier());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +1,56 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr;
|
||||
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.DMRBurst;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.IDLEMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.block.DataBlock;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Aloha;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Preamble;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.announcement.Announcement;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.MBCHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.PacketSequenceHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.ProprietaryDataHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.UDTHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FLCAssembler;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasAssembler;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.SLCAssembler;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ShortLCMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCAssembler;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.packet.PacketSequenceAssembler;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.terminator.Terminator;
|
||||
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceEMBMessage;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Processes DMR messages and performs re-assembly of link control fragments
|
||||
@@ -54,12 +58,13 @@ import java.util.TreeMap;
|
||||
public class DMRMessageProcessor implements Listener<IMessage>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DMRMessageProcessor.class);
|
||||
|
||||
private DecodeConfigDMR mConfigDMR;
|
||||
private FLCAssembler mFLCAssemblerTimeslot1 = new FLCAssembler(1);
|
||||
private FLCAssembler mFLCAssemblerTimeslot2 = new FLCAssembler(2);
|
||||
private MBCAssembler mMBCAssembler = new MBCAssembler();
|
||||
private PacketSequenceAssembler mPacketSequenceAssembler;
|
||||
private SLCAssembler mSLCAssembler = new SLCAssembler();
|
||||
private TalkerAliasAssembler mTalkerAliasAssembler = new TalkerAliasAssembler();
|
||||
private Listener<IMessage> mMessageListener;
|
||||
private Map<Integer,TimeslotFrequency> mTimeslotFrequencyMap = new TreeMap<>();
|
||||
|
||||
@@ -68,6 +73,8 @@ public class DMRMessageProcessor implements Listener<IMessage>
|
||||
*/
|
||||
public DMRMessageProcessor(DecodeConfigDMR config)
|
||||
{
|
||||
mConfigDMR = config;
|
||||
|
||||
for(TimeslotFrequency timeslotFrequency: config.getTimeslotMap())
|
||||
{
|
||||
mTimeslotFrequencyMap.put(timeslotFrequency.getNumber(), timeslotFrequency);
|
||||
@@ -157,22 +164,43 @@ public class DMRMessageProcessor implements Listener<IMessage>
|
||||
}
|
||||
|
||||
//Packet Sequence Message Assembly ...
|
||||
if(message instanceof Preamble)
|
||||
if(message instanceof Preamble preamble)
|
||||
{
|
||||
mPacketSequenceAssembler.process((Preamble)message);
|
||||
mPacketSequenceAssembler.process(preamble);
|
||||
}
|
||||
else if(message instanceof PacketSequenceHeader)
|
||||
else if(message instanceof PacketSequenceHeader header)
|
||||
{
|
||||
mPacketSequenceAssembler.process((PacketSequenceHeader)message);
|
||||
mPacketSequenceAssembler.process(header);
|
||||
}
|
||||
else if(message instanceof ProprietaryDataHeader)
|
||||
else if(message instanceof ProprietaryDataHeader header)
|
||||
{
|
||||
mPacketSequenceAssembler.process(((ProprietaryDataHeader)message));
|
||||
mPacketSequenceAssembler.process(header);
|
||||
}
|
||||
else if(message instanceof DataBlock)
|
||||
else if(message instanceof DataBlock dataBlock)
|
||||
{
|
||||
mPacketSequenceAssembler.process((DataBlock)message);
|
||||
mPacketSequenceAssembler.process(dataBlock);
|
||||
}
|
||||
else if(message instanceof UDTHeader header)
|
||||
{
|
||||
mPacketSequenceAssembler.process(header);
|
||||
}
|
||||
else if((message instanceof IDLEMessage || message instanceof Aloha || message instanceof Announcement) &&
|
||||
message.getTimeslot() != 0)
|
||||
{
|
||||
mPacketSequenceAssembler.dispatchPacketSequence(message.getTimeslot());
|
||||
}
|
||||
|
||||
//Reset talker alias assembler on Idle or Terminator
|
||||
if(message.isValid() && (message instanceof IDLEMessage || message instanceof Terminator))
|
||||
{
|
||||
mTalkerAliasAssembler.reset(message.getTimeslot());
|
||||
}
|
||||
}
|
||||
|
||||
//Assemble Talker Alias from FLC message fragments (header & blocks 1-3)
|
||||
if(message instanceof FullLCMessage flc && flc.getOpcode().isTalkerAliasOpcode() && message.isValid())
|
||||
{
|
||||
dispatch(mTalkerAliasAssembler.process(flc));
|
||||
}
|
||||
|
||||
//Now that the message has been (potentially) enriched, dispatch it to the modules
|
||||
@@ -186,6 +214,26 @@ public class DMRMessageProcessor implements Listener<IMessage>
|
||||
{
|
||||
if(mMessageListener != null && message != null)
|
||||
{
|
||||
//Configure compressed talkgroups for the DMR talkgroup identifiers in the message.
|
||||
if(mConfigDMR.isUseCompressedTalkgroups())
|
||||
{
|
||||
try
|
||||
{
|
||||
for(Identifier identifier: message.getIdentifiers())
|
||||
{
|
||||
if(identifier instanceof DMRTalkgroup talkgroup)
|
||||
{
|
||||
talkgroup.setCompressed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error applying compressed talkgroup setting to identifier in message: " + message +
|
||||
" [" + message.getClass() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
mMessageListener.receive(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer, Zhenyu Mao
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr;
|
||||
|
||||
@@ -25,7 +24,6 @@ import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -40,6 +38,7 @@ public class DecodeConfigDMR extends DecodeConfiguration
|
||||
private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT;
|
||||
private boolean mIgnoreDataCalls = true;
|
||||
private boolean mIgnoreCRCChecksums = false;
|
||||
private boolean mUseCompressedTalkgroups = false;
|
||||
private List<TimeslotFrequency> mTimeslotMap = new ArrayList<>();
|
||||
|
||||
public DecodeConfigDMR()
|
||||
@@ -109,6 +108,28 @@ public class DecodeConfigDMR extends DecodeConfiguration
|
||||
mIgnoreCRCChecksums = ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if decoder should use compressed talkgroups
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "use_compressed_talkgroups")
|
||||
public boolean isUseCompressedTalkgroups()
|
||||
{
|
||||
return mUseCompressedTalkgroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flag to use compressed talkgroups.
|
||||
*
|
||||
* Converts the air interface value to a compressed value described in ETSI 102 361-2, V.2.4.1, Paragraph C2.1.2
|
||||
* This has been observed in use on Hytera Tier III systems.
|
||||
* See: https://cwh050.blogspot.com/2021/03/converting-flat-number-to-dmr-id.html?m=1
|
||||
* @param useCompressedTalkgroups true to use compressed talkgroups.
|
||||
*/
|
||||
public void setUseCompressedTalkgroups(boolean useCompressedTalkgroups)
|
||||
{
|
||||
mUseCompressedTalkgroups = useCompressedTalkgroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traffic channel pool size.
|
||||
* @return
|
||||
|
||||
@@ -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.module.decode.dmr.identifier;
|
||||
@@ -26,16 +23,33 @@ import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* DMR Talkgroup identifier
|
||||
*/
|
||||
public class DMRTalkgroup extends TalkgroupIdentifier
|
||||
{
|
||||
public DMRTalkgroup(Integer value)
|
||||
private boolean mCompressed = false;
|
||||
private int mOriginalValue;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param value of the talkgroup
|
||||
*/
|
||||
public DMRTalkgroup(int value)
|
||||
{
|
||||
super(value, Role.TO);
|
||||
mOriginalValue = value;
|
||||
}
|
||||
|
||||
public DMRTalkgroup(Integer value, Role role)
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param value of the talkgroup
|
||||
* @param role of the talkgroup
|
||||
*/
|
||||
public DMRTalkgroup(int value, Role role)
|
||||
{
|
||||
super(value, role);
|
||||
mOriginalValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,6 +58,84 @@ public class DMRTalkgroup extends TalkgroupIdentifier
|
||||
return Protocol.DMR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if(mCompressed)
|
||||
{
|
||||
return super.toString() + "(c)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compressed flag for this value.
|
||||
*
|
||||
* Talkgroup compression is an alternate format for DMR systems that allows the end user to dial a 7-digit number
|
||||
* on their mobile keypad using just the digits 0 - 9, to address a talkgroup. The downside to this is that the
|
||||
* total available talkgroup address space is reduced to just those dialable talkgroups.
|
||||
*
|
||||
* @param compressed true when using compressed talkgroup format.
|
||||
*/
|
||||
public void setCompressed(boolean compressed)
|
||||
{
|
||||
mCompressed = compressed;
|
||||
|
||||
if(mCompressed)
|
||||
{
|
||||
setValue(getCompressed(mOriginalValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
setValue(mOriginalValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the air interface value to a compressed value described in ETSI 102 361-2, V.2.4.1, Paragraph C2.1.2
|
||||
*
|
||||
* This has been observed in use on Hytera Tier III systems.
|
||||
*
|
||||
* See: https://cwh050.blogspot.com/2021/03/converting-flat-number-to-dmr-id.html?m=1
|
||||
*
|
||||
* @param original value to convert
|
||||
* @return converted value.
|
||||
*/
|
||||
public static int getCompressed(int original)
|
||||
{
|
||||
int converted = 0;
|
||||
int units = original / 1_464_100;
|
||||
converted += (units * 1_000_000);
|
||||
original -= (units * 1_464_100);
|
||||
|
||||
units = original / 146_410;
|
||||
converted += (units * 100_000);
|
||||
original -= (units * 146_410);
|
||||
|
||||
units = original / 14_641;
|
||||
converted += (units * 10_000);
|
||||
original -= (units * 14_641);
|
||||
|
||||
units = original / 1_331;
|
||||
converted += (units * 1_000);
|
||||
original -= (units * 1_331);
|
||||
|
||||
units = original / 121;
|
||||
converted += (units * 100);
|
||||
original -= (units * 121);
|
||||
|
||||
units = original / 11;
|
||||
converted += (units * 10);
|
||||
original -= (units * 11);
|
||||
|
||||
converted += original;
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DMR TO talkgroup identifier
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -24,7 +24,12 @@ import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraAdjacentSiteInformation;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraAloha;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraAnnouncement;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraCsbko32;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraCsbko44;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraCsbko47;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraSmsWaitingNotification;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraXPTPreamble;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraXPTSiteState;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityMaxAloha;
|
||||
@@ -49,6 +54,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Aloha;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Clear;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.MoveTSCC;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Preamble;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Protect;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.acknowledge.Acknowledge;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.acknowledge.AcknowledgeStatus;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.acknowledge.RegistrationAccepted;
|
||||
@@ -78,7 +84,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBloc
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.mbc.UnknownMultiCSBK;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.AnnouncementType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.ServiceKind;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -200,6 +205,9 @@ public class CSBKMessageFactory
|
||||
case STANDARD_PRIVATE_VOICE_CHANNEL_GRANT:
|
||||
csbk = new PrivateVoiceChannelGrant(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
case STANDARD_PROTECT:
|
||||
csbk = new Protect(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
case STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM:
|
||||
csbk = new TalkgroupDataChannelGrant(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
@@ -213,6 +221,9 @@ public class CSBKMessageFactory
|
||||
csbk = new Preamble(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
|
||||
case HYTERA_ALOHA:
|
||||
csbk = new HyteraAloha(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
case HYTERA_08_ANNOUNCEMENT:
|
||||
case HYTERA_68_ANNOUNCEMENT:
|
||||
AnnouncementType announcementType = HyteraAnnouncement.getAnnouncementType(message);
|
||||
@@ -232,7 +243,18 @@ public class CSBKMessageFactory
|
||||
case HYTERA_XPT_SITE_STATE:
|
||||
csbk = new HyteraXPTSiteState(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
|
||||
case HYTERA_CSBKO_32:
|
||||
csbk = new HyteraCsbko32(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
case HYTERA_CSBKO_44:
|
||||
csbk = new HyteraCsbko44(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
case HYTERA_CSBKO_47:
|
||||
csbk = new HyteraCsbko47(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
case HYTERA_CSBKO_62:
|
||||
csbk = new HyteraSmsWaitingNotification(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
case MOTOROLA_CAPMAX_ALOHA:
|
||||
csbk = new CapacityMaxAloha(pattern, message, cach, slotType, timestamp, timeslot);
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -20,7 +20,6 @@
|
||||
package io.github.dsheirer.module.decode.dmr.message.data.csbk;
|
||||
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.Vendor;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
@@ -50,8 +49,8 @@ public enum Opcode
|
||||
STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_PAYLOAD(Vendor.STANDARD, 35, "ACKNOWLEDGE RESPONSE INBOUND PAYLOAD"),
|
||||
STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_OUTBOUND_HEADER(Vendor.STANDARD, 36, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
|
||||
STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_INBOUND_HEADER(Vendor.STANDARD, 37, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
|
||||
STANDARD_ANNOUNCEMENT(Vendor.STANDARD, 40, "ANNOUNCEMENT"),
|
||||
STANDARD_NEGATIVE_ACKNOWLEDGE_RESPONSE(Vendor.STANDARD, 38, "NEGATIVE ACKNOWLEDGE RESPONSE"),
|
||||
STANDARD_ANNOUNCEMENT(Vendor.STANDARD, 40, "ANNOUNCEMENT"),
|
||||
STANDARD_MAINTENANCE(Vendor.STANDARD, 42, "MAINTENANCE"),
|
||||
STANDARD_CLEAR(Vendor.STANDARD, 46, "CLEAR"),
|
||||
STANDARD_PROTECT(Vendor.STANDARD, 47, "PROTECT"),
|
||||
@@ -90,12 +89,17 @@ public enum Opcode
|
||||
MOTOROLA_CAPPLUS_PREAMBLE(Vendor.MOTOROLA_CAPACITY_PLUS, 61, "PREAMBLE"),
|
||||
MOTOROLA_CAPPLUS_SYSTEM_STATUS(Vendor.MOTOROLA_CAPACITY_PLUS, 62, "SYSTEM STATUS"),
|
||||
|
||||
HYTERA_XPT_SITE_STATE(Vendor.HYTERA_68, 10, "HYTERA XPT SITE STATE"),
|
||||
HYTERA_CSBKO_32(Vendor.HYTERA_8, 32, "T-III HYTERA 08 CSBKO 32"),
|
||||
HYTERA_08_ANNOUNCEMENT(Vendor.HYTERA_8, 40, "T-III 08 ANNOUNCEMENT"),
|
||||
HYTERA_68_ANNOUNCEMENT(Vendor.HYTERA_68, 40, "T-III 68 ANNOUNCEMENT"),
|
||||
HYTERA_CSBKO_44(Vendor.HYTERA_8, 44, "T-III HYTERA 08 CSBKO 44"),
|
||||
HYTERA_CSBKO_47(Vendor.HYTERA_8, 47, "T-III HYTERA 08 CSBKO 47"),
|
||||
|
||||
HYTERA_XPT_SITE_STATE(Vendor.HYTERA_68, 10, "HYTERA XPT SITE STATE"),
|
||||
HYTERA_ALOHA(Vendor.HYTERA_68, 25, "HYTERA ALOHA"),
|
||||
HYTERA_68_ANNOUNCEMENT(Vendor.HYTERA_68, 40, "T-III 68 ANNOUNCEMENT"),
|
||||
HYTERA_XPT_PREAMBLE(Vendor.HYTERA_68, 61, "HYTERA XPT PREAMBLE"), //Opcode 54 and/or 55 - See patent on Hytera call forwarding
|
||||
HYTERA_CSBKO_62(Vendor.HYTERA_68, 62, "HYTERA CSBKO 62"),
|
||||
|
||||
//Opcode 54 and/or 55 - See patent on Hytera call forwarding
|
||||
HYTERA_XPT_PREAMBLE(Vendor.HYTERA_68, 61, "HYTERA XPT PREAMBLE"),
|
||||
|
||||
UNKNOWN(Vendor.UNKNOWN, -1, "UNKNOWN");
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.csbk.hytera;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.ServiceFunction;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.Version;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hytera DMR Tier III - Aloha Message
|
||||
*/
|
||||
public class HyteraAloha extends CSBKMessage
|
||||
{
|
||||
//TODO: not sure what bits 16 to 31 are used for ... awaiting documentation.
|
||||
//TODO: bits 32 to 79 seem to align with the standard aloha.
|
||||
|
||||
private static final int[] RESERVED = new int[]{16, 17};
|
||||
private static final int SITE_TIMESLOT_SYNCHRONIZATION = 18;
|
||||
private static final int[] VERSION = new int[]{19, 20, 21};
|
||||
private static final int TIMING_OFFSET = 22;
|
||||
private static final int ACTIVE_NETWORK_CONNECTION_FLAG = 23;
|
||||
private static final int[] MASK = new int[]{24, 25, 26, 27, 28};
|
||||
private static final int[] SERVICE_FUNCTION = new int[]{29, 30};
|
||||
private static final int[] N_RAND_WAIT = new int[]{31, 32, 33, 34};
|
||||
private static final int REGISTRATION_REQUIRED_FLAG = 35;
|
||||
private static final int[] BACKOFF = new int[]{36, 37, 38, 39};
|
||||
private static final int SYSTEM_IDENTITY_CODE_OFFSET = 40;
|
||||
private static final int[] RADIO = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
|
||||
73, 74, 75, 76, 77, 78, 79};
|
||||
|
||||
private SystemIdentityCode mSystemIdentityCode;
|
||||
private RadioIdentifier mRadioIdentifier;
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param syncPattern for the CSBK
|
||||
* @param message bits
|
||||
* @param cach for the DMR burst
|
||||
* @param slotType for this message
|
||||
* @param timestamp
|
||||
* @param timeslot
|
||||
*/
|
||||
public HyteraAloha(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
|
||||
{
|
||||
super(syncPattern, message, cach, slotType, timestamp, timeslot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC-ERROR] ");
|
||||
}
|
||||
|
||||
sb.append("CC:").append(getSlotType().getColorCode());
|
||||
sb.append(" HYTERA ALOHA");
|
||||
|
||||
if(hasRadioIdentifier())
|
||||
{
|
||||
sb.append(" TO:").append(getRadioIdentifier());
|
||||
}
|
||||
|
||||
sb.append(" ").append(getSystemIdentityCode().getModel());
|
||||
sb.append(" NETWORK:").append(getSystemIdentityCode().getNetwork());
|
||||
sb.append(" SITE:").append(getSystemIdentityCode().getSite());
|
||||
if(hasActiveNetworkConnection())
|
||||
{
|
||||
sb.append(" NET-CONNECTED");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(" NET-DISCONNECTED");
|
||||
}
|
||||
|
||||
sb.append(" SERVICES:").append(getServiceFunction());
|
||||
|
||||
sb.append(" ETSI VER:").append(getVersion());
|
||||
sb.append(" MASK:").append(getMask());
|
||||
|
||||
if(getSystemIdentityCode().getPAR().isMultipleControlChannels())
|
||||
{
|
||||
sb.append(" ").append(getSystemIdentityCode().getPAR());
|
||||
}
|
||||
|
||||
sb.append(" ").append(getMessage().toHexString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Services provided by the control channel.
|
||||
*/
|
||||
public ServiceFunction getServiceFunction()
|
||||
{
|
||||
return ServiceFunction.fromValue(getMessage().getInt(SERVICE_FUNCTION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mobile subscriber ID masking value. See: 102 361-4 p6.1.3
|
||||
*/
|
||||
public int getMask()
|
||||
{
|
||||
return getMessage().getInt(MASK);
|
||||
}
|
||||
|
||||
public boolean hasActiveNetworkConnection()
|
||||
{
|
||||
return getMessage().get(ACTIVE_NETWORK_CONNECTION_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* DMR Tier III ETSI 102 361-4 ICD Version Number supported by this system
|
||||
*/
|
||||
public Version getVersion()
|
||||
{
|
||||
return Version.fromValue(getMessage().getInt(VERSION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledged radio identifier
|
||||
*/
|
||||
public RadioIdentifier getRadioIdentifier()
|
||||
{
|
||||
if(mRadioIdentifier == null)
|
||||
{
|
||||
mRadioIdentifier = DMRRadio.createTo(getMessage().getInt(RADIO));
|
||||
}
|
||||
|
||||
return mRadioIdentifier;
|
||||
}
|
||||
|
||||
public boolean hasRadioIdentifier()
|
||||
{
|
||||
return getMessage().getInt(RADIO) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* System Identity Code structure
|
||||
*/
|
||||
public SystemIdentityCode getSystemIdentityCode()
|
||||
{
|
||||
if(mSystemIdentityCode == null)
|
||||
{
|
||||
mSystemIdentityCode = new SystemIdentityCode(getMessage(), SYSTEM_IDENTITY_CODE_OFFSET, true);
|
||||
}
|
||||
|
||||
return mSystemIdentityCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(mIdentifiers == null)
|
||||
{
|
||||
mIdentifiers = new ArrayList<>();
|
||||
if(hasRadioIdentifier())
|
||||
{
|
||||
mIdentifiers.add(getRadioIdentifier());
|
||||
}
|
||||
mIdentifiers.add(getSystemIdentityCode().getNetwork());
|
||||
mIdentifiers.add(getSystemIdentityCode().getSite());
|
||||
}
|
||||
|
||||
return mIdentifiers;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -28,7 +28,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.AnnouncementType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -78,7 +77,7 @@ public class HyteraAnnouncement extends CSBKMessage
|
||||
}
|
||||
|
||||
sb.append("CC:").append(getSlotType().getColorCode());
|
||||
sb.append(" HYTERA ").append(getAnnouncementType());
|
||||
sb.append(" HYTERA ANNOUNCEMENT-TYPE:").append(getAnnouncementType());
|
||||
sb.append(" ").append(getSystemIdentityCode().getModel());
|
||||
sb.append(" NETWORK:").append(getSystemIdentityCode().getNetwork());
|
||||
sb.append(" SITE:").append(getSystemIdentityCode().getSite());
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.csbk.hytera;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hytera DMR Tier III - CSBKO 32 - Possibly Acknowledge Response
|
||||
*
|
||||
* Analysis: ... this was transmitted on the traffic channel while the mobile was at the end of a call, in the terminator
|
||||
* sequence. Since this comes from the SMS gateway 16776906, I wonder if this is a traffic channel message waiting notification?
|
||||
*/
|
||||
public class HyteraCsbko32 extends CSBKMessage
|
||||
{
|
||||
private static final int[] UNKNOWN_1 = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
|
||||
private static final int[] UNKNOWN_2 = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
|
||||
private static final int[] DESTINATION_RADIO = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] SOURCE_RADIO = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
|
||||
73, 74, 75, 76, 77, 78, 79};
|
||||
|
||||
private List<Identifier> mIdentifiers;
|
||||
private RadioIdentifier mDestinationRadio;
|
||||
private RadioIdentifier mSourceRadio;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param syncPattern for the CSBK
|
||||
* @param message bits
|
||||
* @param cach for the DMR burst
|
||||
* @param slotType for this message
|
||||
* @param timestamp
|
||||
* @param timeslot
|
||||
*/
|
||||
public HyteraCsbko32(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
|
||||
{
|
||||
super(syncPattern, message, cach, slotType, timestamp, timeslot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC-ERROR] ");
|
||||
}
|
||||
|
||||
sb.append("CC:").append(getSlotType().getColorCode());
|
||||
sb.append(" HYTERA CSBKO=32 PROBABLE ACKNOWLEDGE RESPONSE");
|
||||
sb.append(" FM:").append(getSourceRadio());
|
||||
sb.append(" TO:").append(getDestinationRadio());
|
||||
sb.append(" UNK1:").append(getUnknown1());
|
||||
sb.append(" UNK2:").append(getUnknown2());
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Addressed (ie TO) radio identifier
|
||||
* @return radio identifier
|
||||
*/
|
||||
public RadioIdentifier getDestinationRadio()
|
||||
{
|
||||
if(mDestinationRadio == null)
|
||||
{
|
||||
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION_RADIO));
|
||||
}
|
||||
|
||||
return mDestinationRadio;
|
||||
}
|
||||
|
||||
public String getUnknown1()
|
||||
{
|
||||
return getMessage().getHex(UNKNOWN_1, 2);
|
||||
}
|
||||
|
||||
public String getUnknown2()
|
||||
{
|
||||
return getMessage().getHex(UNKNOWN_2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Source radio. Should be 16,777,906 the Hytera SMS gateway.
|
||||
*/
|
||||
public RadioIdentifier getSourceRadio()
|
||||
{
|
||||
if(mSourceRadio == null)
|
||||
{
|
||||
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
|
||||
}
|
||||
|
||||
return mSourceRadio;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(mIdentifiers == null)
|
||||
{
|
||||
mIdentifiers = new ArrayList<>();
|
||||
mIdentifiers.add(getDestinationRadio());
|
||||
mIdentifiers.add(getSourceRadio());
|
||||
}
|
||||
|
||||
return mIdentifiers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.csbk.hytera;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hytera DMR Tier III - CSBKO 44 -
|
||||
*
|
||||
* Analysis: ... this was transmitted on the Tier III traffic channel during call termination. This CSBK was
|
||||
* interleaved with every other Terminator message, starting after the first 2x terminator messages. This continued
|
||||
* through the final teardown which was 3x Clear messages.
|
||||
*/
|
||||
public class HyteraCsbko44 extends CSBKMessage
|
||||
{
|
||||
private static final int[] UNKNOWN_1 = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
|
||||
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param syncPattern for the CSBK
|
||||
* @param message bits
|
||||
* @param cach for the DMR burst
|
||||
* @param slotType for this message
|
||||
* @param timestamp
|
||||
* @param timeslot
|
||||
*/
|
||||
public HyteraCsbko44(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
|
||||
{
|
||||
super(syncPattern, message, cach, slotType, timestamp, timeslot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC-ERROR] ");
|
||||
}
|
||||
|
||||
sb.append("CC:").append(getSlotType().getColorCode());
|
||||
sb.append(" HYTERA UNKNOWN CSBKO=44");
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.csbk.hytera;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hytera DMR Tier III - CSBKO 47 -
|
||||
*
|
||||
* Analysis: ... this was transmitted on the traffic channel while the mobile was at the end of a call, in the terminator
|
||||
* sequence. Since this comes from the SMS gateway 16776906, I wonder if this is a traffic channel message waiting notification?
|
||||
*/
|
||||
public class HyteraCsbko47 extends CSBKMessage
|
||||
{
|
||||
private static final int[] UNKNOWN_1 = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
|
||||
private static final int[] UNKNOWN_2 = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
|
||||
private static final int[] DESTINATION_RADIO = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] SOURCE_RADIO = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
|
||||
73, 74, 75, 76, 77, 78, 79};
|
||||
|
||||
private List<Identifier> mIdentifiers;
|
||||
private RadioIdentifier mDestinationRadio;
|
||||
private RadioIdentifier mSourceRadio;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param syncPattern for the CSBK
|
||||
* @param message bits
|
||||
* @param cach for the DMR burst
|
||||
* @param slotType for this message
|
||||
* @param timestamp
|
||||
* @param timeslot
|
||||
*/
|
||||
public HyteraCsbko47(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
|
||||
{
|
||||
super(syncPattern, message, cach, slotType, timestamp, timeslot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC-ERROR] ");
|
||||
}
|
||||
|
||||
sb.append("CC:").append(getSlotType().getColorCode());
|
||||
sb.append(" HYTERA CSBKO=47 ######### UNKNOWN");
|
||||
sb.append(" FM:").append(getSourceRadio());
|
||||
sb.append(" TO:").append(getDestinationRadio());
|
||||
sb.append(" UNK1:").append(getUnknown1());
|
||||
sb.append(" UNK2:").append(getUnknown2());
|
||||
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Addressed (ie TO) radio identifier
|
||||
* @return radio identifier
|
||||
*/
|
||||
public RadioIdentifier getDestinationRadio()
|
||||
{
|
||||
if(mDestinationRadio == null)
|
||||
{
|
||||
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION_RADIO));
|
||||
}
|
||||
|
||||
return mDestinationRadio;
|
||||
}
|
||||
|
||||
public String getUnknown1()
|
||||
{
|
||||
return getMessage().getHex(UNKNOWN_1, 2);
|
||||
}
|
||||
|
||||
public String getUnknown2()
|
||||
{
|
||||
return getMessage().getHex(UNKNOWN_2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Source radio. Should be 16,777,906 the Hytera SMS gateway.
|
||||
*/
|
||||
public RadioIdentifier getSourceRadio()
|
||||
{
|
||||
if(mSourceRadio == null)
|
||||
{
|
||||
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
|
||||
}
|
||||
|
||||
return mSourceRadio;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(mIdentifiers == null)
|
||||
{
|
||||
mIdentifiers = new ArrayList<>();
|
||||
mIdentifiers.add(getDestinationRadio());
|
||||
mIdentifiers.add(getSourceRadio());
|
||||
}
|
||||
|
||||
return mIdentifiers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.csbk.hytera;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hytera DMR Tier III - CSBKO 62 -
|
||||
*
|
||||
* Analysis: this message gets sent on timeslot 2 (not the Tier III control timeslot 1) from the SMS gateway (16776906)
|
||||
* and gets sent to each of the radios involved in the SMS transfer (sender/receiver). I suspect that the network tells
|
||||
* each of the radios to join the control channel and prepare for the SMS message.
|
||||
*/
|
||||
public class HyteraSmsWaitingNotification extends CSBKMessage
|
||||
{
|
||||
private static final int[] UNKNOWN_1 = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
|
||||
private static final int[] UNKNOWN_2 = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
|
||||
private static final int[] DESTINATION_RADIO = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] SOURCE_RADIO = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
|
||||
73, 74, 75, 76, 77, 78, 79};
|
||||
|
||||
private List<Identifier> mIdentifiers;
|
||||
private RadioIdentifier mDestinationRadio;
|
||||
private RadioIdentifier mSourceRadio;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param syncPattern for the CSBK
|
||||
* @param message bits
|
||||
* @param cach for the DMR burst
|
||||
* @param slotType for this message
|
||||
* @param timestamp
|
||||
* @param timeslot
|
||||
*/
|
||||
public HyteraSmsWaitingNotification(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
|
||||
{
|
||||
super(syncPattern, message, cach, slotType, timestamp, timeslot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC-ERROR] ");
|
||||
}
|
||||
|
||||
sb.append("CC:").append(getSlotType().getColorCode());
|
||||
sb.append(" HYTERA PREPARE FOR SMS MESSAGE FOR:").append(getDestinationRadio());
|
||||
sb.append(" FM:").append(getSourceRadio());
|
||||
sb.append(" UNK1:").append(getUnknown1());
|
||||
sb.append(" UNK2:").append(getUnknown2());
|
||||
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Addressed (ie TO) radio identifier
|
||||
* @return radio identifier
|
||||
*/
|
||||
public RadioIdentifier getDestinationRadio()
|
||||
{
|
||||
if(mDestinationRadio == null)
|
||||
{
|
||||
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION_RADIO));
|
||||
}
|
||||
|
||||
return mDestinationRadio;
|
||||
}
|
||||
|
||||
public String getUnknown1()
|
||||
{
|
||||
return getMessage().getHex(UNKNOWN_1, 2);
|
||||
}
|
||||
|
||||
public String getUnknown2()
|
||||
{
|
||||
return getMessage().getHex(UNKNOWN_2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Source radio. Should be 16,777,906 the Hytera SMS gateway.
|
||||
*/
|
||||
public RadioIdentifier getSourceRadio()
|
||||
{
|
||||
if(mSourceRadio == null)
|
||||
{
|
||||
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
|
||||
}
|
||||
|
||||
return mSourceRadio;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(mIdentifiers == null)
|
||||
{
|
||||
mIdentifiers = new ArrayList<>();
|
||||
mIdentifiers.add(getDestinationRadio());
|
||||
mIdentifiers.add(getSourceRadio());
|
||||
}
|
||||
|
||||
return mIdentifiers;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -37,7 +37,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.AbsoluteChannelParameters;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.DataType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -118,6 +117,7 @@ public class Clear extends CSBKMessage implements ITimeslotFrequencyReceiver
|
||||
sb.append(" CLEAR - RETURN TO ").append(getMoveToChannel());
|
||||
sb.append(" FM:").append(getSourceRadio());
|
||||
sb.append(" TO:").append(getDestinationId());
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -32,7 +32,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.ServiceAccessPoint;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.UnifiedDataTransportFormat;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.Vendor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -84,13 +83,13 @@ public class UDTHeader extends DataHeader
|
||||
sb.append(" [CRC ERROR]");
|
||||
}
|
||||
sb.append(" UDT HEADER");
|
||||
if(isSupplementaryData())
|
||||
if(isShortData())
|
||||
{
|
||||
sb.append(" SUPPLEMENTARY DATA");
|
||||
sb.append(" SHORT DATA");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(" SHORT DATA");
|
||||
sb.append(" SUPPLEMENTARY DATA");
|
||||
}
|
||||
sb.append(" FM:").append(getSourceLLID());
|
||||
sb.append(" TO:").append(getDestinationLLID());
|
||||
@@ -106,7 +105,7 @@ public class UDTHeader extends DataHeader
|
||||
sb.append(" OPCODE:").append(getOpcode());
|
||||
}
|
||||
sb.append(" PAD NIBBLES:").append(getPadNibbleCount());
|
||||
sb.append(" ").append(getServiceAccessPoint());
|
||||
sb.append(" SAP:").append(getServiceAccessPoint());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -121,7 +120,15 @@ public class UDTHeader extends DataHeader
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the header contains supplementary data (true) or short data (false)
|
||||
* Indicates if the header is for a short data sequence
|
||||
*/
|
||||
public boolean isShortData()
|
||||
{
|
||||
return !isSupplementaryData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the header is for supplementary data
|
||||
*/
|
||||
public boolean isSupplementaryData()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -21,10 +21,15 @@ package io.github.dsheirer.module.decode.dmr.message.data.lc;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCDMR;
|
||||
import io.github.dsheirer.edac.Checksum_5_DMR;
|
||||
import io.github.dsheirer.edac.ReedSolomon_12_9_4_DMR;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.GPSInformation;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.GroupVoiceChannelUser;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasBlock1;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasBlock2;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasBlock3;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TerminatorData;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.UnitToUnitVoiceChannelUser;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.UnknownFullLCMessage;
|
||||
@@ -71,9 +76,22 @@ public class LCMessageFactory
|
||||
throw new IllegalArgumentException("Message cannot be null");
|
||||
}
|
||||
|
||||
//RS(12,9,4) can correct up to floor(4/2) = 1 bit error.
|
||||
boolean corrected = REED_SOLOMON_12_9_4_DMR.correctFullLinkControl(message,
|
||||
isTerminator ? TERMINATOR_LINK_CONTROL_CRC_MASK : VOICE_LINK_CONTROL_CRC_MASK);
|
||||
boolean valid = true;
|
||||
|
||||
if(message.size() == 77)
|
||||
{
|
||||
valid = Checksum_5_DMR.isValid(message);
|
||||
}
|
||||
else if(message.size() == 96)
|
||||
{
|
||||
//RS(12,9,4) can correct up to floor(4/2) = 1 bit error.
|
||||
valid = REED_SOLOMON_12_9_4_DMR.correctFullLinkControl(message,
|
||||
isTerminator ? TERMINATOR_LINK_CONTROL_CRC_MASK : VOICE_LINK_CONTROL_CRC_MASK);
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.warn("Unrecognized Link Control Message Size: " + message.size());
|
||||
}
|
||||
|
||||
LCOpcode opcode = FullLCMessage.getOpcode(message);
|
||||
|
||||
@@ -110,15 +128,24 @@ public class LCMessageFactory
|
||||
flc = new HyteraTerminator(message, timestamp, timeslot);
|
||||
break;
|
||||
case FULL_STANDARD_TALKER_ALIAS_HEADER:
|
||||
flc = new TalkerAliasHeader(message, timestamp, timeslot);
|
||||
break;
|
||||
case FULL_STANDARD_TALKER_ALIAS_BLOCK_1:
|
||||
flc = new TalkerAliasBlock1(message, timestamp, timeslot);
|
||||
break;
|
||||
case FULL_STANDARD_TALKER_ALIAS_BLOCK_2:
|
||||
flc = new TalkerAliasBlock2(message, timestamp, timeslot);
|
||||
break;
|
||||
case FULL_STANDARD_TALKER_ALIAS_BLOCK_3:
|
||||
flc = new TalkerAliasBlock3(message, timestamp, timeslot);
|
||||
break;
|
||||
default:
|
||||
flc = new UnknownFullLCMessage(message, timestamp, timeslot);
|
||||
break;
|
||||
}
|
||||
|
||||
flc.setValid(corrected);
|
||||
flc.setValid(valid);
|
||||
|
||||
return flc;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -20,8 +20,8 @@
|
||||
package io.github.dsheirer.module.decode.dmr.message.data.lc;
|
||||
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.Vendor;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@@ -39,6 +39,7 @@ public enum LCOpcode
|
||||
FULL_STANDARD_TALKER_ALIAS_BLOCK_1(Vendor.STANDARD,true, 5, "TALKER ALIAS BLOCK 1"),
|
||||
FULL_STANDARD_TALKER_ALIAS_BLOCK_2(Vendor.STANDARD,true, 6, "TALKER ALIAS BLOCK 2"),
|
||||
FULL_STANDARD_TALKER_ALIAS_BLOCK_3(Vendor.STANDARD,true, 7, "TALKER ALIAS BLOCK 3"),
|
||||
FULL_STANDARD_TALKER_ALIAS_COMPLETE(Vendor.STANDARD, true, -1, "TALKER ALIAS COMPLETE"), //Not part of ICD
|
||||
FULL_STANDARD_GPS_INFO(Vendor.STANDARD,true, 8, "GPS INFO"),
|
||||
FULL_STANDARD_TERMINATOR_DATA(Vendor.STANDARD, true, 48, "TERMINATOR DATA"),
|
||||
FULL_STANDARD_UNKNOWN(Vendor.STANDARD,true, -1, "FULL UNKNOWN"),
|
||||
@@ -67,6 +68,9 @@ public enum LCOpcode
|
||||
|
||||
SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "SHORT UNKNOWN");
|
||||
|
||||
private static final EnumSet<LCOpcode> TALKER_ALIAS_OPCODES = EnumSet.of(FULL_STANDARD_TALKER_ALIAS_HEADER,
|
||||
FULL_STANDARD_TALKER_ALIAS_BLOCK_1, FULL_STANDARD_TALKER_ALIAS_BLOCK_2, FULL_STANDARD_TALKER_ALIAS_BLOCK_3);
|
||||
|
||||
private Vendor mVendor;
|
||||
private boolean mFull;
|
||||
private int mValue;
|
||||
@@ -111,6 +115,14 @@ public enum LCOpcode
|
||||
return !mFull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this entry is one of the four talker alias opcodes.
|
||||
*/
|
||||
public boolean isTalkerAliasOpcode()
|
||||
{
|
||||
return TALKER_ALIAS_OPCODES.contains(this);
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -93,7 +93,7 @@ public class FLCAssembler
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a fully-received messsage
|
||||
* Dispatches a fully-received message
|
||||
*/
|
||||
private FullLCMessage dispatch()
|
||||
{
|
||||
@@ -133,6 +133,7 @@ public class FLCAssembler
|
||||
for(int row = 0; row < 8; row++)
|
||||
{
|
||||
int rowErrorCount = Hamming16.checkAndCorrect(descrambled, row * 16);
|
||||
|
||||
errorCount += rowErrorCount;
|
||||
|
||||
if(errorCount > 1)
|
||||
@@ -179,6 +180,9 @@ public class FLCAssembler
|
||||
{
|
||||
extractedMessage.set(pointer++, descrambled.get(row * 16 + column));
|
||||
}
|
||||
|
||||
//Transfer the checksum parity bit from this row
|
||||
extractedMessage.set(70 + row, descrambled.get(row * 16 + 10));
|
||||
}
|
||||
|
||||
extractedMessage.setCorrectedBitCount(errorCount);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -99,4 +99,13 @@ public abstract class FullLCMessage extends LCMessage
|
||||
{
|
||||
return getMessage().get(ENCRYPTION_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the reserved bit is set.
|
||||
* @return true if set
|
||||
*/
|
||||
public boolean isReservedBitSet()
|
||||
{
|
||||
return getMessage().get(RESERVED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -25,7 +25,6 @@ import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -69,6 +68,7 @@ public class GroupVoiceChannelUser extends AbstractVoiceChannelUser
|
||||
sb.append(getRadio());
|
||||
sb.append(" TO:").append(getTalkgroup());
|
||||
sb.append(" ").append(getServiceOptions());
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.lc.full;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
|
||||
/**
|
||||
* Aggregates a sequence of talker alias full link control messages to reconstruct the talker alias.
|
||||
*/
|
||||
public class TalkerAliasAssembler
|
||||
{
|
||||
//Timeslot 1 fragments
|
||||
private TalkerAliasHeader mHeaderTs1;
|
||||
private TalkerAliasBlock1 mBlock1Ts1;
|
||||
private TalkerAliasBlock2 mBlock2Ts1;
|
||||
private TalkerAliasBlock3 mBlock3Ts1;
|
||||
|
||||
//Timeslot 2 fragments
|
||||
private TalkerAliasHeader mHeaderTs2;
|
||||
private TalkerAliasBlock1 mBlock1Ts2;
|
||||
private TalkerAliasBlock2 mBlock2Ts2;
|
||||
private TalkerAliasBlock3 mBlock3Ts2;
|
||||
|
||||
//Flags indicating if we have any non-null fragments for the specified timeslot to support resetting.
|
||||
private boolean mEmptyTs1 = true;
|
||||
private boolean mEmptyTs2 = true;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*/
|
||||
public TalkerAliasAssembler()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the contents of either timeslot 1 or 2 when a terminator or idle message is received.
|
||||
* @param timeslot 1 or 2
|
||||
*/
|
||||
public void reset(int timeslot)
|
||||
{
|
||||
if(timeslot == 1 && !mEmptyTs1)
|
||||
{
|
||||
mHeaderTs1 = null;
|
||||
mBlock1Ts1 = null;
|
||||
mBlock2Ts1 = null;
|
||||
mBlock3Ts1 = null;
|
||||
mEmptyTs1 = true;
|
||||
}
|
||||
else if(timeslot == 2 && !mEmptyTs2)
|
||||
{
|
||||
mHeaderTs2 = null;
|
||||
mBlock1Ts2 = null;
|
||||
mBlock2Ts2 = null;
|
||||
mBlock3Ts2 = null;
|
||||
mEmptyTs2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the FLC talker alias header or block and returns a fully assembled alias when all fragments are received.
|
||||
* @param message containing FLC talker alias header or blocks 1-3
|
||||
* @return fully assembled talker alias, if available, or null.
|
||||
*/
|
||||
public TalkerAliasComplete process(FullLCMessage message)
|
||||
{
|
||||
switch(message.getOpcode())
|
||||
{
|
||||
case FULL_STANDARD_TALKER_ALIAS_HEADER:
|
||||
if(message instanceof TalkerAliasHeader tah)
|
||||
{
|
||||
if(message.getTimeslot() == 1)
|
||||
{
|
||||
mHeaderTs1 = tah;
|
||||
mEmptyTs1 = false;
|
||||
return assemble(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mHeaderTs2 = tah;
|
||||
mEmptyTs2 = false;
|
||||
return assemble(2);
|
||||
}
|
||||
}
|
||||
case FULL_STANDARD_TALKER_ALIAS_BLOCK_1:
|
||||
if(message instanceof TalkerAliasBlock1 tab1)
|
||||
{
|
||||
if(message.getTimeslot() == 1)
|
||||
{
|
||||
mBlock1Ts1 = tab1;
|
||||
mEmptyTs1 = false;
|
||||
return assemble(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mBlock1Ts2 = tab1;
|
||||
mEmptyTs2 = false;
|
||||
return assemble(2);
|
||||
}
|
||||
}
|
||||
case FULL_STANDARD_TALKER_ALIAS_BLOCK_2:
|
||||
if(message instanceof TalkerAliasBlock2 tab2)
|
||||
{
|
||||
if(message.getTimeslot() == 1)
|
||||
{
|
||||
mBlock2Ts1 = tab2;
|
||||
mEmptyTs1 = false;
|
||||
return assemble(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mBlock2Ts2 = tab2;
|
||||
mEmptyTs2 = false;
|
||||
return assemble(2);
|
||||
}
|
||||
}
|
||||
case FULL_STANDARD_TALKER_ALIAS_BLOCK_3:
|
||||
if(message instanceof TalkerAliasBlock3 tab3)
|
||||
{
|
||||
if(message.getTimeslot() == 1)
|
||||
{
|
||||
mBlock3Ts1 = tab3;
|
||||
mEmptyTs1 = false;
|
||||
return assemble(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mBlock3Ts2 = tab3;
|
||||
mEmptyTs2 = false;
|
||||
return assemble(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the complete alias.
|
||||
* @param timeslot to assemble.
|
||||
* @return assembled alias or null.
|
||||
*/
|
||||
private TalkerAliasComplete assemble(int timeslot)
|
||||
{
|
||||
if(timeslot == 1)
|
||||
{
|
||||
return assemble(mHeaderTs1, mBlock1Ts1, mBlock2Ts1, mBlock3Ts1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return assemble(mHeaderTs2, mBlock1Ts2, mBlock2Ts2, mBlock3Ts2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a complete alias from the header and block fragments.
|
||||
* @param header of the alias
|
||||
* @param block1 of the alias, optional
|
||||
* @param block2 of the alias, optional
|
||||
* @param block3 of the alias, optional
|
||||
* @return assembled alias or null.
|
||||
*/
|
||||
private TalkerAliasComplete assemble(TalkerAliasHeader header,
|
||||
TalkerAliasBlock1 block1,
|
||||
TalkerAliasBlock2 block2,
|
||||
TalkerAliasBlock3 block3)
|
||||
{
|
||||
if(header != null)
|
||||
{
|
||||
int bitLength = header.getTotalBitLength();
|
||||
|
||||
if(1 <= bitLength && bitLength <= 49)
|
||||
{
|
||||
return new TalkerAliasComplete(header.getPayloadFragment(), header.getFormat(), header.getCharacterLength(),
|
||||
header.getTimestamp(), header.getTimeslot());
|
||||
}
|
||||
else if(50 <= bitLength && bitLength <= 105 && block1 != null)
|
||||
{
|
||||
CorrectedBinaryMessage message = new CorrectedBinaryMessage(105);
|
||||
message.load(0, header.getPayloadFragment());
|
||||
message.load(49, block1.getPayloadFragment());
|
||||
return new TalkerAliasComplete(message, header.getFormat(), header.getCharacterLength(),
|
||||
header.getTimestamp(), header.getTimeslot());
|
||||
}
|
||||
else if(106 <= bitLength && bitLength <= 161 && block1 != null && block2 != null)
|
||||
{
|
||||
CorrectedBinaryMessage message = new CorrectedBinaryMessage(161);
|
||||
message.load(0, header.getPayloadFragment());
|
||||
message.load(49, block1.getPayloadFragment());
|
||||
message.load(105, block2.getPayloadFragment());
|
||||
return new TalkerAliasComplete(message, header.getFormat(), header.getCharacterLength(),
|
||||
header.getTimestamp(), header.getTimeslot());
|
||||
}
|
||||
else if(162 <= bitLength && bitLength <= 217 && block1 != null && block2 != null && block3 != null)
|
||||
{
|
||||
CorrectedBinaryMessage message = new CorrectedBinaryMessage(217);
|
||||
message.load(0, header.getPayloadFragment());
|
||||
message.load(49, block1.getPayloadFragment());
|
||||
message.load(105, block2.getPayloadFragment());
|
||||
message.load(161, block3.getPayloadFragment());
|
||||
return new TalkerAliasComplete(message, header.getFormat(), header.getCharacterLength(),
|
||||
header.getTimestamp(), header.getTimeslot());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.lc.full;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Talker alias continuation block 1.
|
||||
*/
|
||||
public class TalkerAliasBlock1 extends FullLCMessage
|
||||
{
|
||||
private static final int PAYLOAD_START = 16;
|
||||
private static final int PAYLOAD_END = 72;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param message for link control payload
|
||||
* @param timestamp for the message
|
||||
* @param timeslot where the message was transmitted
|
||||
*/
|
||||
public TalkerAliasBlock1(CorrectedBinaryMessage message, long timestamp, int timeslot)
|
||||
{
|
||||
super(message, timestamp, timeslot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload fragment carried by the header.
|
||||
* @return payload fragment.
|
||||
*/
|
||||
public CorrectedBinaryMessage getPayloadFragment()
|
||||
{
|
||||
return getMessage().getSubMessage(PAYLOAD_START, PAYLOAD_END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("FLC TALKER ALIAS BLOCK 1");
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.lc.full;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Talker alias continuation block 2.
|
||||
*/
|
||||
public class TalkerAliasBlock2 extends FullLCMessage
|
||||
{
|
||||
private static final int PAYLOAD_START = 16;
|
||||
private static final int PAYLOAD_END = 72;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param message for link control payload
|
||||
* @param timestamp for the message
|
||||
* @param timeslot where the message was transmitted
|
||||
*/
|
||||
public TalkerAliasBlock2(CorrectedBinaryMessage message, long timestamp, int timeslot)
|
||||
{
|
||||
super(message, timestamp, timeslot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload fragment carried by the header.
|
||||
* @return payload fragment.
|
||||
*/
|
||||
public CorrectedBinaryMessage getPayloadFragment()
|
||||
{
|
||||
return getMessage().getSubMessage(PAYLOAD_START, PAYLOAD_END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("FLC TALKER ALIAS BLOCK 2");
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.lc.full;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Talker alias continuation block 3.
|
||||
*/
|
||||
public class TalkerAliasBlock3 extends FullLCMessage
|
||||
{
|
||||
private static final int PAYLOAD_START = 16;
|
||||
private static final int PAYLOAD_END = 72;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param message for link control payload
|
||||
* @param timestamp for the message
|
||||
* @param timeslot where the message was transmitted
|
||||
*/
|
||||
public TalkerAliasBlock3(CorrectedBinaryMessage message, long timestamp, int timeslot)
|
||||
{
|
||||
super(message, timestamp, timeslot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload fragment carried by the header.
|
||||
* @return payload fragment.
|
||||
*/
|
||||
public CorrectedBinaryMessage getPayloadFragment()
|
||||
{
|
||||
return getMessage().getSubMessage(PAYLOAD_START, PAYLOAD_END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("FLC TALKER ALIAS BLOCK 3");
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.lc.full;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.alias.DmrTalkerAliasIdentifier;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.TalkerAliasDataFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Complete talker alias assembled from talker alias header and 0 or more talker alias blocks.
|
||||
*/
|
||||
public class TalkerAliasComplete extends FullLCMessage
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TalkerAliasComplete.class);
|
||||
|
||||
private TalkerAliasDataFormat mDataFormat;
|
||||
private int mCharacterCount;
|
||||
private String mAlias;
|
||||
private DmrTalkerAliasIdentifier mTalkerAliasIdentifier;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param message for link control payload
|
||||
* @param timestamp for the header
|
||||
* @param timeslot for the header and blocks
|
||||
*/
|
||||
public TalkerAliasComplete(CorrectedBinaryMessage message, TalkerAliasDataFormat format, int characterCount,
|
||||
long timestamp, int timeslot)
|
||||
{
|
||||
super(message, timestamp, timeslot);
|
||||
mDataFormat = format;
|
||||
mCharacterCount = characterCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCOpcode getOpcode()
|
||||
{
|
||||
return LCOpcode.FULL_STANDARD_TALKER_ALIAS_COMPLETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpcodeValue()
|
||||
{
|
||||
return getOpcode().getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias value
|
||||
* @return alias value if it can be decoded or empty string.
|
||||
*/
|
||||
public String getAliasValue()
|
||||
{
|
||||
if(mAlias == null)
|
||||
{
|
||||
switch(mDataFormat)
|
||||
{
|
||||
//Note: there are 31 possible 7-bit characters, 27 possible 8-bit characters and 13.5 possible 16-bit
|
||||
//characters. We check each format to ensure character count is at least one and no more than max.
|
||||
case BIT_7:
|
||||
if(1 <= mCharacterCount && mCharacterCount <= 31)
|
||||
{
|
||||
mAlias = getMessage().parseISO7(0, mCharacterCount);
|
||||
}
|
||||
break;
|
||||
case BIT_8:
|
||||
if(1 <= mCharacterCount && mCharacterCount <= 27)
|
||||
{
|
||||
mAlias = getMessage().parseISO8(1, mCharacterCount);
|
||||
}
|
||||
break;
|
||||
case UTF_8:
|
||||
if(1 <= mCharacterCount && mCharacterCount <= 27)
|
||||
{
|
||||
try
|
||||
{
|
||||
mAlias = getMessage().parseUTF8(1, mCharacterCount);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOGGER.info("Error decoding talker alias as UTF-8: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UNICODE_UTF_16_BE:
|
||||
if(1 <= mCharacterCount && mCharacterCount <= 13)
|
||||
{
|
||||
mAlias = getMessage().parseUnicode(1, mCharacterCount);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//If we couldn't decode, set it to empty so that we don't reprocess every time.
|
||||
if(mAlias == null)
|
||||
{
|
||||
mAlias = "";
|
||||
}
|
||||
}
|
||||
|
||||
return mAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Talker Alias identifier.
|
||||
* @return identifier or null if the alias string is null or empty, meaning it could not be decoded.
|
||||
*/
|
||||
public DmrTalkerAliasIdentifier getTalkerAliasIdentifier()
|
||||
{
|
||||
if(mTalkerAliasIdentifier == null && getAliasValue() != null && !getAliasValue().isEmpty())
|
||||
{
|
||||
mTalkerAliasIdentifier = DmrTalkerAliasIdentifier.create(getAliasValue());
|
||||
}
|
||||
|
||||
return mTalkerAliasIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the talker alias identifier is non-null.
|
||||
* @return true if non-null.
|
||||
*/
|
||||
public boolean hasTalkerAliasIdentifier()
|
||||
{
|
||||
return getTalkerAliasIdentifier() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("FLC TALKER ALIAS:");
|
||||
|
||||
if(hasTalkerAliasIdentifier())
|
||||
{
|
||||
sb.append(getTalkerAliasIdentifier());
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("(empty/error)");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(hasTalkerAliasIdentifier())
|
||||
{
|
||||
List<Identifier> identifiers = new ArrayList<>();
|
||||
identifiers.add(getTalkerAliasIdentifier());
|
||||
return identifiers;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.lc.full;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.TalkerAliasDataFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Talker alias header with format and length description and a fragment of the alias data.
|
||||
*/
|
||||
public class TalkerAliasHeader extends FullLCMessage
|
||||
{
|
||||
private static final int[] FORMAT = new int[]{16, 17};
|
||||
private static final int[] LENGTH = new int[]{18, 19, 20, 21, 22};
|
||||
private static final int PAYLOAD_START = 23;
|
||||
private static final int PAYLOAD_END = 72;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param message for link control payload
|
||||
* @param timestamp for the message
|
||||
* @param timeslot where the message was transmitted
|
||||
*/
|
||||
public TalkerAliasHeader(CorrectedBinaryMessage message, long timestamp, int timeslot)
|
||||
{
|
||||
super(message, timestamp, timeslot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatting used for the alias characters.
|
||||
* @return format
|
||||
*/
|
||||
public TalkerAliasDataFormat getFormat()
|
||||
{
|
||||
return TalkerAliasDataFormat.fromValue(getMessage().getInt(FORMAT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Length of the alias in characters.
|
||||
* @return length in characters.
|
||||
*/
|
||||
public int getCharacterLength()
|
||||
{
|
||||
return getMessage().getInt(LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Total bit length of the alias characters.
|
||||
*/
|
||||
public int getTotalBitLength()
|
||||
{
|
||||
int total = getFormat().getBitsPerCharacter() * getCharacterLength();
|
||||
|
||||
//Max payload bit length is 217 (49 + 56 + 56 + 56)
|
||||
if(total > 217)
|
||||
{
|
||||
return 217;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload fragment carried by the header.
|
||||
* @return payload fragment.
|
||||
*/
|
||||
public CorrectedBinaryMessage getPayloadFragment()
|
||||
{
|
||||
return getMessage().getSubMessage(PAYLOAD_START, PAYLOAD_END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("FLC TALKER ALIAS HEADER");
|
||||
sb.append(" FORMAT:").append(getFormat());
|
||||
sb.append(" CHARACTERS:").append(getCharacterLength());
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -28,20 +28,22 @@ import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.AbstractVoiceChannelUser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Motorola Capacity Plus - Group Voice Channel User
|
||||
*/
|
||||
public class CapacityPlusGroupVoiceChannelUser extends AbstractVoiceChannelUser implements ITimeslotFrequencyReceiver
|
||||
public class CapacityPlusGroupVoiceChannelUser extends CapacityPlusVoiceChannelUser implements ITimeslotFrequencyReceiver
|
||||
{
|
||||
private static final int[] GROUP_ADDRESS = new int[]{40, 41, 42, 43, 44, 45, 46, 47};
|
||||
private static final int[] CAPACITY_PLUS_GROUP_ADDRESS = new int[]{40, 41, 42, 43, 44, 45, 46, 47};
|
||||
private static final int[] CONVENTIONAL_GROUP_ADDRESS = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
|
||||
private static final int[] VOICE_CHANNEL_REPEATER = new int[]{51, 52, 53, 54};
|
||||
private static final int[] VOICE_CHANNEL_TIMESLOT = new int[]{55};
|
||||
private static final int[] SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71};
|
||||
private static final int[] CAPACITY_PLUS_SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71};
|
||||
private static final int[] CONVENTIONAL_SOURCE_ADDRESS = new int[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71};
|
||||
private static final int[] UNKNOWN = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
|
||||
|
||||
private RadioIdentifier mRadio;
|
||||
@@ -69,20 +71,33 @@ public class CapacityPlusGroupVoiceChannelUser extends AbstractVoiceChannelUser
|
||||
sb.append("[CRC-ERROR] ");
|
||||
}
|
||||
|
||||
sb.append("FLC MOTOROLA CAP+ GROUP VOICE CHANNEL USER FM:");
|
||||
sb.append(getRadio());
|
||||
sb.append(" TO:").append(getTalkgroup());
|
||||
sb.append(" ON CHANNEL:");
|
||||
if(hasVoiceChannel())
|
||||
if(isEncrypted())
|
||||
{
|
||||
sb.append(getVoiceChannel());
|
||||
sb.append(" ENCRYPTED");
|
||||
}
|
||||
|
||||
if(isReservedBitSet())
|
||||
{
|
||||
sb.append(" RESERVED-BIT");
|
||||
}
|
||||
|
||||
if(getServiceOptions().isCapacityPlus())
|
||||
{
|
||||
sb.append("FLC MOTOROLA CAP+ GROUP VOICE CHANNEL USER");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("--");
|
||||
sb.append("FLC MOTOROLA CONV/IP SITE GROUP VOICE CHANNEL USER");
|
||||
}
|
||||
sb.append(" FM:").append(getRadio());
|
||||
sb.append(" TO:").append(getTalkgroup());
|
||||
|
||||
if(hasVoiceChannel())
|
||||
{
|
||||
sb.append(" ON CHANNEL:");
|
||||
sb.append(getVoiceChannel());
|
||||
}
|
||||
|
||||
sb.append(" UNK:").append(getUnknown());
|
||||
sb.append(" ").append(getServiceOptions());
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
@@ -119,6 +134,7 @@ public class CapacityPlusGroupVoiceChannelUser extends AbstractVoiceChannelUser
|
||||
|
||||
/**
|
||||
* Rest timeslot
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getVoiceChannelTimeslot()
|
||||
@@ -131,7 +147,7 @@ public class CapacityPlusGroupVoiceChannelUser extends AbstractVoiceChannelUser
|
||||
*/
|
||||
public boolean hasVoiceChannel()
|
||||
{
|
||||
return getVoiceChannelRepeater() != 0;
|
||||
return getServiceOptions().isCapacityPlus() && getVoiceChannelRepeater() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +157,14 @@ public class CapacityPlusGroupVoiceChannelUser extends AbstractVoiceChannelUser
|
||||
{
|
||||
if(mRadio == null)
|
||||
{
|
||||
mRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_ADDRESS));
|
||||
if(getServiceOptions().isCapacityPlus())
|
||||
{
|
||||
mRadio = DMRRadio.createFrom(getMessage().getInt(CAPACITY_PLUS_SOURCE_ADDRESS));
|
||||
}
|
||||
else
|
||||
{
|
||||
mRadio = DMRRadio.createFrom(getMessage().getInt(CONVENTIONAL_SOURCE_ADDRESS));
|
||||
}
|
||||
}
|
||||
|
||||
return mRadio;
|
||||
@@ -154,7 +177,14 @@ public class CapacityPlusGroupVoiceChannelUser extends AbstractVoiceChannelUser
|
||||
{
|
||||
if(mTalkgroup == null)
|
||||
{
|
||||
mTalkgroup = DMRTalkgroup.create(getMessage().getInt(GROUP_ADDRESS));
|
||||
if(getServiceOptions().isCapacityPlus())
|
||||
{
|
||||
mTalkgroup = DMRTalkgroup.create(getMessage().getInt(CAPACITY_PLUS_GROUP_ADDRESS));
|
||||
}
|
||||
else
|
||||
{
|
||||
mTalkgroup = DMRTalkgroup.create(getMessage().getInt(CONVENTIONAL_GROUP_ADDRESS));
|
||||
}
|
||||
}
|
||||
|
||||
return mTalkgroup;
|
||||
@@ -189,12 +219,13 @@ public class CapacityPlusGroupVoiceChannelUser extends AbstractVoiceChannelUser
|
||||
|
||||
/**
|
||||
* Applies the LSN to frequency map to the rest channel.
|
||||
*
|
||||
* @param timeslotFrequencies that match the logical timeslots
|
||||
*/
|
||||
@Override
|
||||
public void apply(List<TimeslotFrequency> timeslotFrequencies)
|
||||
{
|
||||
for(TimeslotFrequency timeslotFrequency: timeslotFrequencies)
|
||||
for(TimeslotFrequency timeslotFrequency : timeslotFrequencies)
|
||||
{
|
||||
if(getVoiceChannel().getLogicalSlotNumber() == timeslotFrequency.getNumber())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.lc.full.motorola;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.CapacityPlusServiceOptions;
|
||||
|
||||
/**
|
||||
* Any Capacity Plus Voice Channel User link control message that contains vendor-specific service options.
|
||||
*/
|
||||
public abstract class CapacityPlusVoiceChannelUser extends FullLCMessage
|
||||
{
|
||||
private static final int[] SERVICE_OPTIONS = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
|
||||
|
||||
private CapacityPlusServiceOptions mServiceOptions;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param message for the link control payload
|
||||
*/
|
||||
public CapacityPlusVoiceChannelUser(CorrectedBinaryMessage message, long timestamp, int timeslot)
|
||||
{
|
||||
super(message, timestamp, timeslot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service options for the call
|
||||
*/
|
||||
public CapacityPlusServiceOptions getServiceOptions()
|
||||
{
|
||||
if(mServiceOptions == null)
|
||||
{
|
||||
mServiceOptions = new CapacityPlusServiceOptions(getMessage().getInt(SERVICE_OPTIONS));
|
||||
}
|
||||
|
||||
return mServiceOptions;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -28,22 +28,20 @@ import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.AbstractVoiceChannelUser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Motorola Capacity Plus - Wide Area (Multi-Site) Voice Channel User
|
||||
*/
|
||||
public class CapacityPlusWideAreaVoiceChannelUser extends AbstractVoiceChannelUser implements ITimeslotFrequencyReceiver
|
||||
public class CapacityPlusWideAreaVoiceChannelUser extends CapacityPlusVoiceChannelUser implements ITimeslotFrequencyReceiver
|
||||
{
|
||||
private static final int[] UNKNOWN_1 = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
|
||||
private static final int[] GROUP_ADDRESS = new int[]{40, 41, 42, 43, 44, 45, 46, 47};
|
||||
private static final int[] REST_REPEATER = new int[]{51, 52, 53, 54};
|
||||
private static final int[] REST_TIMESLOT = new int[]{55};
|
||||
private static final int[] SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71};
|
||||
private static final int[] UNKNOWN_2 = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
|
||||
private static final int[] UNKNOWN_3 = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
|
||||
|
||||
private RadioIdentifier mRadio;
|
||||
private TalkgroupIdentifier mTalkgroup;
|
||||
@@ -70,6 +68,16 @@ public class CapacityPlusWideAreaVoiceChannelUser extends AbstractVoiceChannelUs
|
||||
sb.append("[CRC-ERROR] ");
|
||||
}
|
||||
|
||||
if(isEncrypted())
|
||||
{
|
||||
sb.append(" *ENCRYPTED*");
|
||||
}
|
||||
|
||||
if(isReservedBitSet())
|
||||
{
|
||||
sb.append(" *RESERVED-BIT*");
|
||||
}
|
||||
|
||||
sb.append("FLC MOTOROLA CAP+ WIDE-AREA VOICE CHANNEL USER FM:");
|
||||
sb.append(getRadio());
|
||||
sb.append(" TO:").append(getTalkgroup());
|
||||
@@ -102,7 +110,7 @@ public class CapacityPlusWideAreaVoiceChannelUser extends AbstractVoiceChannelUs
|
||||
*/
|
||||
public int getUnknown2()
|
||||
{
|
||||
return getMessage().getInt(UNKNOWN_2);
|
||||
return getMessage().getInt(UNKNOWN_3);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -23,7 +23,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.block.DataBlock;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Preamble;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.PacketSequenceHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.ProprietaryDataHeader;
|
||||
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.UDTHeader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,6 +34,7 @@ public class PacketSequence
|
||||
{
|
||||
private int mTimeslot;
|
||||
private PacketSequenceHeader mPacketSequenceHeader;
|
||||
private UDTHeader mUDTHeader;
|
||||
private ProprietaryDataHeader mProprietaryDataHeader;
|
||||
private List<Preamble> mPreambles = new ArrayList<>();
|
||||
private List<DataBlock> mDataBlocks = new ArrayList<>();
|
||||
@@ -51,7 +52,11 @@ public class PacketSequence
|
||||
sb.append(" TS:").append(getTimeslot());
|
||||
if(mPacketSequenceHeader != null)
|
||||
{
|
||||
sb.append(mPacketSequenceHeader.toString());
|
||||
sb.append(mPacketSequenceHeader);
|
||||
}
|
||||
else if(mUDTHeader != null)
|
||||
{
|
||||
sb.append(mUDTHeader);
|
||||
}
|
||||
sb.append(" HEADER:").append(hasPacketSequenceHeader());
|
||||
sb.append(" PROPRIETARY HEADER:").append(hasProprietaryDataHeader());
|
||||
@@ -97,6 +102,15 @@ public class PacketSequence
|
||||
mPacketSequenceHeader = header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified Data Transport (UDT) header
|
||||
* @param header for a UDT sequence
|
||||
*/
|
||||
public void setUDTHeader(UDTHeader header)
|
||||
{
|
||||
mUDTHeader = header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proprietary data header
|
||||
*/
|
||||
@@ -138,6 +152,23 @@ public class PacketSequence
|
||||
return mPacketSequenceHeader != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* UDT header
|
||||
* @return header or null.
|
||||
*/
|
||||
public UDTHeader getUDTHeader()
|
||||
{
|
||||
return mUDTHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this packet sequence has a UDT header
|
||||
*/
|
||||
public boolean hasUDTHeader()
|
||||
{
|
||||
return getUDTHeader() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Secondary/Proprietary packet sequence header
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2021 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,6 +24,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.block.DataBlock;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Preamble;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.PacketSequenceHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.ProprietaryDataHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.UDTHeader;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
/**
|
||||
@@ -70,7 +71,7 @@ public class PacketSequenceAssembler
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to receive packet packet sequence messages
|
||||
* Sets the listener to receive packet sequence messages
|
||||
* @param listener to receive messages
|
||||
*/
|
||||
public void setMessageListener(Listener<IMessage> listener)
|
||||
@@ -82,11 +83,11 @@ public class PacketSequenceAssembler
|
||||
* Dispatches the packet sequence for the specified timeslot and sets the sequence to null.
|
||||
* @param timeslot to dispatch
|
||||
*/
|
||||
private void dispatchPacketSequence(int timeslot)
|
||||
public void dispatchPacketSequence(int timeslot)
|
||||
{
|
||||
PacketSequence packetSequence = (timeslot == 1 ? mTimeslot1Sequence : mTimeslot2Sequence);
|
||||
|
||||
if(mMessageListener != null && packetSequence != null && packetSequence.isComplete())
|
||||
if(packetSequence != null && mMessageListener != null)
|
||||
{
|
||||
IMessage message = PacketSequenceMessageFactory.create(packetSequence);
|
||||
|
||||
@@ -154,6 +155,27 @@ public class PacketSequenceAssembler
|
||||
packetSequence.setPacketSequenceHeader(header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a UDT header.
|
||||
* @param header
|
||||
*/
|
||||
public void process(UDTHeader header)
|
||||
{
|
||||
int timeslot = header.getTimeslot();
|
||||
|
||||
PacketSequence packetSequence = getPacketSequence(timeslot);
|
||||
|
||||
//If we already have headers or data blocks for the current sequence, then this is a new sequence
|
||||
if(packetSequence.hasPacketSequenceHeader() || packetSequence.hasProprietaryDataHeader() ||
|
||||
packetSequence.hasDataBlocks())
|
||||
{
|
||||
dispatchPacketSequence(timeslot);
|
||||
packetSequence = getPacketSequence(timeslot);
|
||||
}
|
||||
|
||||
packetSequence.setUDTHeader(header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a proprietary packet sequence header.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -26,6 +26,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.block.DataBlock;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.HeaderMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.PacketSequenceHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.ProprietaryDataHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.UDTHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.hytera.HyteraProprietaryDataHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MNISProprietaryDataHeader;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.ApplicationType;
|
||||
@@ -35,11 +36,10 @@ import io.github.dsheirer.module.decode.ip.ipv4.IPV4Header;
|
||||
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
|
||||
import io.github.dsheirer.module.decode.ip.lrrp.LRRPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.xcmp.XCMPPacket;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Converts a DMR packet sequence into a message
|
||||
@@ -55,30 +55,73 @@ public class PacketSequenceMessageFactory
|
||||
*/
|
||||
public static IMessage create(PacketSequence packetSequence)
|
||||
{
|
||||
if(packetSequence != null && packetSequence.isComplete())
|
||||
if(packetSequence != null)
|
||||
{
|
||||
PacketSequenceHeader primaryHeader = packetSequence.getPacketSequenceHeader();
|
||||
CorrectedBinaryMessage packet = getPacket(packetSequence, primaryHeader.isConfirmedData());
|
||||
|
||||
if(packet != null)
|
||||
if(packetSequence.hasPacketSequenceHeader())
|
||||
{
|
||||
switch(primaryHeader.getServiceAccessPoint())
|
||||
PacketSequenceHeader primaryHeader = packetSequence.getPacketSequenceHeader();
|
||||
CorrectedBinaryMessage packet = getPacket(packetSequence, primaryHeader.isConfirmedData());
|
||||
|
||||
if(packet != null)
|
||||
{
|
||||
case IP_PACKET_DATA:
|
||||
return createIPPacketData(packetSequence, packet);
|
||||
case PROPRIETARY_DATA:
|
||||
return createProprietary(packetSequence, packet);
|
||||
case SHORT_DATA:
|
||||
return createDefinedShortData(packetSequence, packet);
|
||||
default:
|
||||
mLog.info("Unknown Packet SAP: " + primaryHeader.getServiceAccessPoint() + " - returning unknown packet");
|
||||
return new DMRPacketMessage(packetSequence, new UnknownPacket(packet, 0), packet,
|
||||
packetSequence.getTimeslot(), packetSequence.getPacketSequenceHeader().getTimestamp());
|
||||
switch(primaryHeader.getServiceAccessPoint())
|
||||
{
|
||||
case IP_PACKET_DATA:
|
||||
return createIPPacketData(packetSequence, packet);
|
||||
case PROPRIETARY_DATA:
|
||||
return createProprietary(packetSequence, packet);
|
||||
case SHORT_DATA:
|
||||
return createDefinedShortData(packetSequence, packet);
|
||||
default:
|
||||
mLog.info("Unknown Packet SAP: " + primaryHeader.getServiceAccessPoint() + " - returning unknown packet");
|
||||
return new DMRPacketMessage(packetSequence, new UnknownPacket(packet, 0), packet,
|
||||
packetSequence.getTimeslot(), packetSequence.getPacketSequenceHeader().getTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(packetSequence.hasUDTHeader() && packetSequence.hasDataBlocks())
|
||||
{
|
||||
UDTHeader header = packetSequence.getUDTHeader();
|
||||
if(header.isShortData())
|
||||
{
|
||||
CorrectedBinaryMessage payload = getUnconfirmedPayload(packetSequence.getDataBlocks());
|
||||
return new UDTShortMessageService(header, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: NOTE: Don't create a DMRPacketMessage unless we have a)Packet and b)Packet Sequence Header
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the unconfirmed payload bytes from each of the data blocks into a contiguous binary message.
|
||||
* @param dataBlocks to assemble
|
||||
* @return assembled unconfirmed payload.
|
||||
*/
|
||||
public static CorrectedBinaryMessage getUnconfirmedPayload(List<DataBlock> dataBlocks)
|
||||
{
|
||||
if(!dataBlocks.isEmpty())
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
List<CorrectedBinaryMessage> fragments = new ArrayList<>();
|
||||
for(DataBlock dataBlock: dataBlocks)
|
||||
{
|
||||
CorrectedBinaryMessage fragment = dataBlock.getUnConfirmedPayload();
|
||||
length += fragment.size();
|
||||
fragments.add(fragment);
|
||||
}
|
||||
|
||||
CorrectedBinaryMessage combined = new CorrectedBinaryMessage(length);
|
||||
int pointer = 0;
|
||||
for(CorrectedBinaryMessage fragment: fragments)
|
||||
{
|
||||
combined.load(pointer, fragment);
|
||||
pointer += fragment.size();
|
||||
}
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.data.packet;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.message.DMRMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.header.UDTHeader;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Short Message Service (SMS) using Unified Data Transport
|
||||
*/
|
||||
public class UDTShortMessageService extends DMRMessage
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(UDTShortMessageService.class);
|
||||
private UDTHeader mHeader;
|
||||
private String mSMS;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param header for the sequence
|
||||
* @param payload from the data blocks
|
||||
*/
|
||||
public UDTShortMessageService(UDTHeader header, CorrectedBinaryMessage payload)
|
||||
{
|
||||
super(payload, header.getTimestamp(), header.getTimeslot());
|
||||
mHeader = header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return mHeader.getIdentifiers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CC:").append(mHeader.getSlotType().getColorCode());
|
||||
sb.append(" SMS MESSAGE:").append(getSMS());
|
||||
sb.append(" FROM:").append(mHeader.getSourceLLID());
|
||||
sb.append(" TO:").append(mHeader.getDestinationLLID());
|
||||
sb.append(" HEX:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getSMS()
|
||||
{
|
||||
if(mSMS == null)
|
||||
{
|
||||
switch(mHeader.getFormat())
|
||||
{
|
||||
case UNICODE_16:
|
||||
mSMS = parseUnicodePayload();
|
||||
break;
|
||||
case ASCII_7:
|
||||
mSMS = parseISO7Payload();
|
||||
break;
|
||||
case ASCII_8:
|
||||
mSMS = parseISO8Payload();
|
||||
break;
|
||||
case BCD_4_BITS:
|
||||
mSMS = parseBCD4Payload();
|
||||
break;
|
||||
case BINARY:
|
||||
case MOBILE_SUBSCRIBER_OR_TALKGROUP_ADDRESS:
|
||||
case NMEA_GPS_LOCATION_CODED:
|
||||
case IP_ADDRESS:
|
||||
case VENDOR_PROPRIETARY_8:
|
||||
case VENDOR_PROPRIETARY_9:
|
||||
case MIXED_FORMAT:
|
||||
case UNKNOWN:
|
||||
LOGGER.warn("Unrecognized UDT Short Data Format: " + mHeader.getFormat() +
|
||||
" - Please send this to the sdrtrunk developer - Hex:" + getMessage().toHexString() +
|
||||
" As String:" + new String(getMessage().getBytes()));
|
||||
mSMS = "Error:" + new String(getMessage().getBytes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(mSMS == null)
|
||||
{
|
||||
mSMS = "error - unrecognized format: " + mHeader.getFormat();
|
||||
}
|
||||
|
||||
return mSMS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a unicode 16-bit payload from the message.
|
||||
* @return parsed message
|
||||
*/
|
||||
private String parseUnicodePayload()
|
||||
{
|
||||
int length = getMessage().size();
|
||||
length -= (mHeader.getPadNibbleCount() * 4);
|
||||
length -= 16; //Exclude the dangling 32-bit CRC
|
||||
length -= 16; //Exclude the dangling 0x0006 ACKNOWLEDGE character
|
||||
|
||||
if(length > 16)
|
||||
{
|
||||
return getMessage().parseUnicode(0, (length / 16));
|
||||
}
|
||||
else
|
||||
{
|
||||
return "(insufficient data)";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an ISO-7 payload from the message.
|
||||
* @return parsed message
|
||||
*/
|
||||
private String parseISO7Payload()
|
||||
{
|
||||
int length = getMessage().size();
|
||||
length -= (mHeader.getPadNibbleCount() * 4);
|
||||
|
||||
if(length > 7)
|
||||
{
|
||||
return getMessage().parseISO7(0, (length / 7));
|
||||
}
|
||||
else
|
||||
{
|
||||
return "(insufficient data)";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an ISO-8 payload from the message.
|
||||
* @return parsed message
|
||||
*/
|
||||
private String parseISO8Payload()
|
||||
{
|
||||
int length = getMessage().size();
|
||||
length -= (mHeader.getPadNibbleCount() * 4);
|
||||
|
||||
if(length > 8)
|
||||
{
|
||||
return getMessage().parseISO8(0, (length / 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
return "(insufficient data)";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a BCD 4-bit payload from the message.
|
||||
* @return parsed message
|
||||
*/
|
||||
private String parseBCD4Payload()
|
||||
{
|
||||
int length = getMessage().size();
|
||||
length -= (mHeader.getPadNibbleCount() * 4);
|
||||
|
||||
if(length > 4)
|
||||
{
|
||||
return getMessage().parseBCD4(0, (length / 4));
|
||||
}
|
||||
else
|
||||
{
|
||||
return "(insufficient data)";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -36,7 +36,7 @@ public enum AnnouncementType
|
||||
ADJACENT_SITE_INFORMATION(6, "NEIGHBOR SITE INFORMATION"),
|
||||
GENERAL_SITE_INFORMATION(7, "SITE INFORMATION"),
|
||||
RESERVED_8(8, "RESERVED 8"),
|
||||
RESERVED_9(9, "RESERVED 8"),
|
||||
RESERVED_9(9, "RESERVED 9"),
|
||||
RESERVED_10(10, "RESERVED 10"),
|
||||
RESERVED_11(11, "RESERVED 11"),
|
||||
RESERVED_12(12, "RESERVED 12"),
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.type;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Motorola Capacity Plus flavor of service options. Motorola uses the 2x reserved bits from the default implementation.
|
||||
*/
|
||||
public class CapacityPlusServiceOptions extends ServiceOptions
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param value of the service options bitmap
|
||||
*/
|
||||
public CapacityPlusServiceOptions(int value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserved bit 1 is used to signal if the system is IP Site Connect or Conventional (true) or Capacity Plus (false).
|
||||
* @return
|
||||
*/
|
||||
public boolean isCapacityPlus()
|
||||
{
|
||||
return !isReserved1();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the transmission is interruptible.
|
||||
* @return true if interruptible.
|
||||
*/
|
||||
public boolean isInterruptible()
|
||||
{
|
||||
return isReserved2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
List<String> flags = new ArrayList<>();
|
||||
|
||||
if(isEmergency())
|
||||
{
|
||||
flags.add("EMERGENCY");
|
||||
}
|
||||
|
||||
if(isEncrypted())
|
||||
{
|
||||
flags.add("ENCRYPTED");
|
||||
}
|
||||
|
||||
if(isBroadcastService())
|
||||
{
|
||||
flags.add("BROADCAST");
|
||||
}
|
||||
|
||||
if(isOpenVoiceCallMode())
|
||||
{
|
||||
flags.add("OVCM");
|
||||
}
|
||||
|
||||
if(isInterruptible())
|
||||
{
|
||||
flags.add("TXI CALL");
|
||||
}
|
||||
|
||||
if(getPriority() > 0)
|
||||
{
|
||||
flags.add("PRIORITY-" + getPriority());
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("SERVICE OPTIONS [");
|
||||
if(!flags.isEmpty())
|
||||
{
|
||||
sb.append(Joiner.on(",").join(flags));
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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
|
||||
@@ -20,7 +20,6 @@
|
||||
package io.github.dsheirer.module.decode.dmr.message.type;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -31,6 +30,8 @@ public class ServiceOptions
|
||||
{
|
||||
private static final int EMERGENCY_MASK = 0x80;
|
||||
private static final int ENCRYPTION_MASK = 0x40;
|
||||
private static final int RESERVED_1 = 0x20;
|
||||
private static final int RESERVED_2 = 0x10;
|
||||
private static final int BROADCAST_SERVICE_MASK = 0x8;
|
||||
private static final int OPEN_VOICE_CALL_MODE_MASK = 0x4;
|
||||
private static final int PRIORITY_MASK = 0x3;
|
||||
@@ -77,6 +78,24 @@ public class ServiceOptions
|
||||
return isSet(OPEN_VOICE_CALL_MODE_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if Reserved 1 bit is set
|
||||
* @return true if set
|
||||
*/
|
||||
public boolean isReserved1()
|
||||
{
|
||||
return isSet(RESERVED_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if Reserved 2 bit is set
|
||||
* @return true if set
|
||||
*/
|
||||
public boolean isReserved2()
|
||||
{
|
||||
return isSet(RESERVED_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Priority of the communication
|
||||
* @return priority 0-3, 0 = lowest/routine, 3 = highest
|
||||
@@ -119,6 +138,16 @@ public class ServiceOptions
|
||||
flags.add("OVCM");
|
||||
}
|
||||
|
||||
if(isReserved1())
|
||||
{
|
||||
flags.add("RSVD1");
|
||||
}
|
||||
|
||||
if(isReserved2())
|
||||
{
|
||||
flags.add("RSVD2");
|
||||
}
|
||||
|
||||
if(getPriority() > 0)
|
||||
{
|
||||
flags.add("PRIORITY-" + getPriority());
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.module.decode.dmr.message.type;
|
||||
|
||||
/**
|
||||
* Enumeration of talker alias data formatting options.
|
||||
*
|
||||
* ETSI 102 361-2 v2.4.1 paragraph 7.2.18
|
||||
*/
|
||||
public enum TalkerAliasDataFormat
|
||||
{
|
||||
BIT_7(0, 7, "ASCII-7"),
|
||||
BIT_8(1, 8, "ASCII-8"),
|
||||
UTF_8(2, 8, "UTF-8"),
|
||||
UNICODE_UTF_16_BE(3, 16, "UNICODE-16");
|
||||
|
||||
private int mValue;
|
||||
private int mBitsPerCharacter;
|
||||
private String mLabel;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param value ordinal
|
||||
*/
|
||||
TalkerAliasDataFormat(int value, int bitsPerCharacter, String label)
|
||||
{
|
||||
mValue = value;
|
||||
mBitsPerCharacter = bitsPerCharacter;
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ordinal value of the format
|
||||
* @return ordinal value.
|
||||
*/
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of bits per character for the format.
|
||||
* @return bits per character
|
||||
*/
|
||||
public int getBitsPerCharacter()
|
||||
{
|
||||
return mBitsPerCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the format from the ordinal value.
|
||||
* @param value to lookup in range 0-3
|
||||
* @return format for the value.
|
||||
*/
|
||||
public static TalkerAliasDataFormat fromValue(int value)
|
||||
{
|
||||
if(value < 0 || value > 3)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid value - out of range (0-3): " + value);
|
||||
}
|
||||
|
||||
return TalkerAliasDataFormat.values()[value];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
@@ -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,6 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -55,7 +54,7 @@ public class VoiceAMessage extends VoiceMessage
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(" ").append(getSyncPattern());
|
||||
sb.append("CC:- ").append(getSyncPattern());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
@@ -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.module.decode.event;
|
||||
@@ -50,6 +49,7 @@ public enum DecodeEventType
|
||||
EMERGENCY("EMERGENCY"),
|
||||
FUNCTION("Function"),
|
||||
GPS("GPS"),
|
||||
ICMP_PACKET("ICMP Packet"),
|
||||
ID_ANI("ANI"),
|
||||
ID_UNIQUE("Unique ID"),
|
||||
IP_PACKET("IP Packet"),
|
||||
@@ -63,9 +63,11 @@ public enum DecodeEventType
|
||||
RESPONSE("Response"),
|
||||
RESPONSE_PACKET("Response Packet"),
|
||||
SDM("Short Data Message"),
|
||||
SMS("SMS"),
|
||||
STATION_ID("Station ID"),
|
||||
STATUS("Status"),
|
||||
UDP_PACKET("UDP/IP Packet"),
|
||||
UNKNOWN_PACKET("Unknown Packet"),
|
||||
UNKNOWN("Unknown");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
@@ -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,8 +43,10 @@ import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.UnknownPacket;
|
||||
import io.github.dsheirer.module.decode.ip.ars.ARSPacket;
|
||||
import io.github.dsheirer.module.decode.ip.cellocator.MCGPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.icmp.ICMPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
|
||||
import io.github.dsheirer.module.decode.ip.lrrp.LRRPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
|
||||
@@ -87,7 +89,6 @@ import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCUn
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCUnitToUnitVoiceServiceChannelGrant;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCUnitToUnitVoiceServiceChannelGrantUpdate;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.isp.UMBTCTelephoneInterconnectRequestExplicitDialing;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
@@ -139,12 +140,11 @@ import io.github.dsheirer.module.decode.p25.reference.ServiceOptions;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.util.PacketUtil;
|
||||
import java.util.List;
|
||||
import org.jdesktop.swingx.mapviewer.GeoPosition;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decoder state for an APCO25 channel. Maintains the call/data/idle state of the channel and produces events by
|
||||
* monitoring the decoded message stream.
|
||||
@@ -877,12 +877,12 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
|
||||
if(message instanceof SNDCPPacketMessage)
|
||||
{
|
||||
SNDCPPacketMessage sndcp = (SNDCPPacketMessage)message;
|
||||
SNDCPPacketMessage sndcp = (SNDCPPacketMessage) message;
|
||||
getIdentifierCollection().update(sndcp.getIdentifiers());
|
||||
}
|
||||
else if(message instanceof PacketMessage)
|
||||
{
|
||||
PacketMessage packetMessage = (PacketMessage)message;
|
||||
PacketMessage packetMessage = (PacketMessage) message;
|
||||
getIdentifierCollection().remove(IdentifierClass.USER);
|
||||
getIdentifierCollection().update(packetMessage.getIdentifiers());
|
||||
|
||||
@@ -890,41 +890,41 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
|
||||
if(packet instanceof IPV4Packet)
|
||||
{
|
||||
IPV4Packet ipv4 = (IPV4Packet)packet;
|
||||
IPV4Packet ipv4 = (IPV4Packet) packet;
|
||||
|
||||
IPacket ipPayload = ipv4.getPayload();
|
||||
|
||||
if(ipPayload instanceof UDPPacket)
|
||||
{
|
||||
UDPPacket udpPacket = (UDPPacket)ipPayload;
|
||||
UDPPacket udpPacket = (UDPPacket) ipPayload;
|
||||
|
||||
IPacket udpPayload = udpPacket.getPayload();
|
||||
|
||||
if(udpPayload instanceof ARSPacket)
|
||||
{
|
||||
ARSPacket arsPacket = (ARSPacket)udpPayload;
|
||||
ARSPacket arsPacket = (ARSPacket) udpPayload;
|
||||
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier: packet.getIdentifiers())
|
||||
for(Identifier identifier : packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE.toString())
|
||||
.identifiers(ic)
|
||||
.details(arsPacket.toString() + " " + ipv4.toString())
|
||||
.build();
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE.toString())
|
||||
.identifiers(ic)
|
||||
.details(arsPacket.toString() + " " + ipv4.toString())
|
||||
.build();
|
||||
|
||||
broadcast(packetEvent);
|
||||
}
|
||||
else if(udpPayload instanceof MCGPPacket)
|
||||
{
|
||||
MCGPPacket mcgpPacket = (MCGPPacket)udpPayload;
|
||||
MCGPPacket mcgpPacket = (MCGPPacket) udpPayload;
|
||||
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier: packet.getIdentifiers())
|
||||
for(Identifier identifier : packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
@@ -954,7 +954,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
|
||||
GeoPosition geoPosition = PacketUtil.extractGeoPosition(lrrpPacket);
|
||||
|
||||
if (geoPosition != null)
|
||||
if(geoPosition != null)
|
||||
{
|
||||
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
@@ -970,21 +970,72 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
else
|
||||
{
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier: packet.getIdentifiers())
|
||||
for(Identifier identifier : packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.UDP_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(ipv4.toString())
|
||||
.build();
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.UDP_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(ipv4.toString())
|
||||
.build();
|
||||
|
||||
broadcast(packetEvent);
|
||||
}
|
||||
}
|
||||
else if(ipPayload instanceof ICMPPacket)
|
||||
{
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier : packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.ICMP_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(ipv4.toString())
|
||||
.build();
|
||||
|
||||
broadcast(packetEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier : packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.IP_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(ipv4.toString())
|
||||
.build();
|
||||
|
||||
broadcast(packetEvent);
|
||||
}
|
||||
}
|
||||
else if(packet instanceof UnknownPacket)
|
||||
{
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier : packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.UNKNOWN_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(packet.toString())
|
||||
.build();
|
||||
|
||||
broadcast(packetEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -992,18 +1043,14 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
/**
|
||||
* Sub-Network Dependent Convergence Protocol (SNDCP)
|
||||
*
|
||||
* @param message
|
||||
* @param message to process
|
||||
*/
|
||||
private void processSNDCP(P25Message message)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
|
||||
|
||||
if(message.isValid() && message instanceof SNDCPPacketMessage)
|
||||
if(message.isValid() && message instanceof SNDCPPacketMessage sndcpPacket)
|
||||
{
|
||||
SNDCPPacketMessage sndcpPacket = (SNDCPPacketMessage)message;
|
||||
|
||||
SNDCPMessage sndcpMessage = sndcpPacket.getSNDCPMessage();
|
||||
|
||||
MutableIdentifierCollection ic = getMutableIdentifierCollection(sndcpPacket.getIdentifiers());
|
||||
|
||||
switch(sndcpPacket.getSNDCPPacketHeader().getPDUType())
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
package io.github.dsheirer.preference.source;
|
||||
|
||||
import io.github.dsheirer.gui.preference.tuner.RspDuoSelectionMode;
|
||||
import io.github.dsheirer.preference.Preference;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.util.prefs.Preferences;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* Tuner preferences
|
||||
*/
|
||||
@@ -35,8 +35,10 @@ public class TunerPreference extends Preference
|
||||
private final static Logger mLog = LoggerFactory.getLogger(TunerPreference.class);
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(TunerPreference.class);
|
||||
private static final String PREFERENCE_KEY_CHANNELIZER_TYPE = "channelizer.type";
|
||||
private static final String PREFERENCE_KEY_RSP_DUO_TUNER_MODE = "rsp.duo.tuner.mode";
|
||||
|
||||
private ChannelizerType mChannelizerType;
|
||||
private RspDuoSelectionMode mRspDuoSelectionMode;
|
||||
|
||||
/**
|
||||
* Constructs a tuner preference with the update listener
|
||||
@@ -94,4 +96,30 @@ public class TunerPreference extends Preference
|
||||
mPreferences.put(PREFERENCE_KEY_CHANNELIZER_TYPE, mChannelizerType.name());
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* RSPduo tuner select mode.
|
||||
* @return mode or a default value of DUAL
|
||||
*/
|
||||
public RspDuoSelectionMode getRspDuoTunerMode()
|
||||
{
|
||||
if(mRspDuoSelectionMode == null)
|
||||
{
|
||||
String mode = mPreferences.get(PREFERENCE_KEY_RSP_DUO_TUNER_MODE, RspDuoSelectionMode.DUAL.name());
|
||||
mRspDuoSelectionMode = RspDuoSelectionMode.fromValue(mode);
|
||||
}
|
||||
|
||||
return mRspDuoSelectionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the RSPduo tuner select mode
|
||||
* @param mode to use
|
||||
*/
|
||||
public void setRspDuoTunerMode(RspDuoSelectionMode mode)
|
||||
{
|
||||
mRspDuoSelectionMode = mode;
|
||||
mPreferences.put(PREFERENCE_KEY_RSP_DUO_TUNER_MODE, mRspDuoSelectionMode.name());
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +18,6 @@
|
||||
*/
|
||||
package io.github.dsheirer.record.binary;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -29,6 +26,8 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class BinaryReader implements Iterator<ByteBuffer>, AutoCloseable
|
||||
{
|
||||
@@ -94,7 +93,7 @@ public class BinaryReader implements Iterator<ByteBuffer>, AutoCloseable
|
||||
byte[] readBytes = new byte[mBufferSize];
|
||||
int bytesRead = mInputStream.read(readBytes);
|
||||
|
||||
if(bytesRead < readBytes.length)
|
||||
if(bytesRead < readBytes.length && bytesRead > 0)
|
||||
{
|
||||
readBytes = Arrays.copyOf(readBytes, bytesRead);
|
||||
}
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
*/
|
||||
package io.github.dsheirer.sample;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Broadcasts an item to multiple listeners
|
||||
@@ -100,6 +101,10 @@ public class Broadcaster<T> implements Listener<T>
|
||||
mListeners.add(listener);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Unexpected error while adding listener", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
@@ -120,6 +125,10 @@ public class Broadcaster<T> implements Listener<T>
|
||||
{
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Unexpected error while removing listener", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
@@ -151,6 +160,10 @@ public class Broadcaster<T> implements Listener<T>
|
||||
listener.receive(t);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Unexpected error while broadcasting to listeners", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.source.tuner;
|
||||
@@ -29,4 +26,9 @@ public interface ITunerErrorListener
|
||||
* @param errorMessage to set
|
||||
*/
|
||||
void setErrorMessage(String errorMessage);
|
||||
|
||||
/**
|
||||
* Indicates that the tuner has been removed from the system.
|
||||
*/
|
||||
void tunerRemoved();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -34,4 +34,10 @@ public class LoggingTunerErrorListener implements ITunerErrorListener
|
||||
{
|
||||
mLog.error("Tuner Error: " + errorMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tunerRemoved()
|
||||
{
|
||||
mLog.warn("Tuner removal detected");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -149,6 +149,20 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process tuner removal error from tuner controller and propagate to an external listener.
|
||||
*/
|
||||
@Override
|
||||
public void tunerRemoved()
|
||||
{
|
||||
stop();
|
||||
|
||||
if(mTunerErrorListener != null)
|
||||
{
|
||||
mTunerErrorListener.tunerRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel source manager
|
||||
* @param manager to use
|
||||
|
||||
@@ -30,6 +30,7 @@ public enum TunerClass
|
||||
FUNCUBE_DONGLE_PRO_PLUS("Funcube Dongle Pro+" ),
|
||||
HACKRF("HackRF" ),
|
||||
RTL2832("RTL-2832"),
|
||||
RSP("RSP"),
|
||||
TEST_TUNER("Test"),
|
||||
RECORDING_TUNER("Recording"),
|
||||
UNKNOWN("Unknown" );
|
||||
|
||||
@@ -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
|
||||
@@ -46,7 +46,11 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
INativeBufferProvider, Listener<INativeBuffer>, ITunerErrorListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(TunerController.class);
|
||||
|
||||
//Protects access to the native buffer broadcaster for adding, removing or checking for listener count.
|
||||
protected ReentrantLock mBufferListenerLock = new ReentrantLock();
|
||||
protected Broadcaster<INativeBuffer> mNativeBufferBroadcaster = new Broadcaster();
|
||||
|
||||
protected FrequencyController mFrequencyController;
|
||||
private int mMiddleUnusableHalfBandwidth;
|
||||
private int mMeasuredFrequencyError;
|
||||
@@ -129,6 +133,19 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the tuner has been removed from the system and requests the listener to process the cleanup.
|
||||
*/
|
||||
@Override
|
||||
public void tunerRemoved()
|
||||
{
|
||||
//Request parent tuner process the shutdown and cleanup
|
||||
if(mTunerErrorListener != null)
|
||||
{
|
||||
mTunerErrorListener.tunerRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of samples contained in each complex buffer provided by this tuner.
|
||||
*
|
||||
@@ -572,7 +589,16 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
@Override
|
||||
public void addBufferListener(Listener<INativeBuffer> listener)
|
||||
{
|
||||
mNativeBufferBroadcaster.addListener(listener);
|
||||
mBufferListenerLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
mNativeBufferBroadcaster.addListener(listener);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mBufferListenerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -581,7 +607,16 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
@Override
|
||||
public void removeBufferListener(Listener<INativeBuffer> listener)
|
||||
{
|
||||
mNativeBufferBroadcaster.removeListener(listener);
|
||||
mBufferListenerLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
mNativeBufferBroadcaster.removeListener(listener);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mBufferListenerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -590,7 +625,20 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
@Override
|
||||
public boolean hasBufferListeners()
|
||||
{
|
||||
return mNativeBufferBroadcaster.hasListeners();
|
||||
boolean hasListeners;
|
||||
|
||||
mBufferListenerLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
hasListeners = mNativeBufferBroadcaster.hasListeners();
|
||||
}
|
||||
finally
|
||||
{
|
||||
mBufferListenerLock.unlock();
|
||||
}
|
||||
|
||||
return hasListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -598,6 +646,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
*/
|
||||
protected void broadcast(INativeBuffer complexSamples)
|
||||
{
|
||||
//Note: unprotected access to the broadcaster ... the broadcaster uses thread-save internal list
|
||||
mNativeBufferBroadcaster.broadcast(complexSamples);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -54,6 +54,24 @@ public class TunerEvent
|
||||
return mTuner != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Tuner Event [").append(getEvent().name()).append("] for tuner [");
|
||||
if(hasTuner())
|
||||
{
|
||||
sb.append(getTuner());
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("No Tuner");
|
||||
}
|
||||
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Event type
|
||||
*/
|
||||
@@ -75,6 +93,7 @@ public class TunerEvent
|
||||
NOTIFICATION_SHUTTING_DOWN,
|
||||
|
||||
REQUEST_CLEAR_MAIN_SPECTRAL_DISPLAY,
|
||||
REQUEST_ENABLE_RSP_SLAVE_DEVICE,
|
||||
REQUEST_MAIN_SPECTRAL_DISPLAY,
|
||||
REQUEST_NEW_SPECTRAL_DISPLAY;
|
||||
}
|
||||
|
||||
@@ -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,6 +19,7 @@
|
||||
|
||||
package io.github.dsheirer.source.tuner;
|
||||
|
||||
import io.github.dsheirer.gui.preference.tuner.RspDuoSelectionMode;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.source.ChannelizerType;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
@@ -53,7 +54,60 @@ import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerEditor;
|
||||
import io.github.dsheirer.source.tuner.rtl.r820t.R820TEmbeddedTuner;
|
||||
import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerEditor;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.DiscoveredRspTuner;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.RspTuner;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.DeviceSelectionMode;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.SDRPlayException;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.SDRplay;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.device.Device;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.device.DeviceInfo;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.device.Rsp1Device;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.device.Rsp1aDevice;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.device.Rsp2Device;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.device.RspDuoDevice;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.device.RspDxDevice;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1.ControlRsp1;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1.DiscoveredRsp1Tuner;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1.IControlRsp1;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1.Rsp1TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1.Rsp1TunerController;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1.Rsp1TunerEditor;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1a.ControlRsp1a;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1a.DiscoveredRsp1aTuner;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1a.IControlRsp1a;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1a.Rsp1aTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1a.Rsp1aTunerController;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp1a.Rsp1aTunerEditor;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp2.ControlRsp2;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp2.DiscoveredRsp2Tuner;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp2.IControlRsp2;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp2.Rsp2TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp2.Rsp2TunerController;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rsp2.Rsp2TunerEditor;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.ControlRspDuoTuner1Master;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.ControlRspDuoTuner1Single;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.ControlRspDuoTuner2Single;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.ControlRspDuoTuner2Slave;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.DiscoveredRspDuoTuner1;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.DiscoveredRspDuoTuner2;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.IControlRspDuoTuner1;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.IControlRspDuoTuner2;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.MasterSlaveBridge;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.RspDuoTuner1Configuration;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.RspDuoTuner1Controller;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.RspDuoTuner1Editor;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.RspDuoTuner2Configuration;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.RspDuoTuner2Controller;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.RspDuoTuner2Editor;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDx.ControlRspDx;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDx.DiscoveredRspDxTuner;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDx.IControlRspDx;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDx.RspDxTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDx.RspDxTunerController;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.rspDx.RspDxTunerEditor;
|
||||
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -66,6 +120,182 @@ public class TunerFactory
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(TunerFactory.class);
|
||||
|
||||
/**
|
||||
* Creates one or two (e.g. RSPduo) Discovered RSP tuner instances for the RSP device.
|
||||
* @param deviceInfo describing the RSP device
|
||||
* @param channelizerType to use with the device
|
||||
* @param selectionMode to specify how to configure an RSPduo dual-tuner device.
|
||||
* @return zero or more discovered RSP tuners.
|
||||
*/
|
||||
public static List<DiscoveredRspTuner> getRspTuners(DeviceInfo deviceInfo, ChannelizerType channelizerType, RspDuoSelectionMode selectionMode)
|
||||
{
|
||||
List<DiscoveredRspTuner> tuners = new ArrayList<>();
|
||||
|
||||
switch(deviceInfo.getDeviceType())
|
||||
{
|
||||
case RSP1:
|
||||
tuners.add(new DiscoveredRsp1Tuner(deviceInfo, channelizerType));
|
||||
break;
|
||||
case RSP1A:
|
||||
tuners.add(new DiscoveredRsp1aTuner(deviceInfo, channelizerType));
|
||||
break;
|
||||
case RSP2:
|
||||
tuners.add(new DiscoveredRsp2Tuner(deviceInfo, channelizerType));
|
||||
break;
|
||||
case RSPdx:
|
||||
tuners.add(new DiscoveredRspDxTuner(deviceInfo, channelizerType));
|
||||
break;
|
||||
case RSPduo:
|
||||
switch(selectionMode)
|
||||
{
|
||||
case SINGLE_1:
|
||||
deviceInfo.setDeviceSelectionMode(DeviceSelectionMode.SINGLE_TUNER_1);
|
||||
tuners.add(new DiscoveredRspDuoTuner1(deviceInfo, channelizerType));
|
||||
break;
|
||||
case SINGLE_2:
|
||||
deviceInfo.setDeviceSelectionMode(DeviceSelectionMode.SINGLE_TUNER_2);
|
||||
tuners.add(new DiscoveredRspDuoTuner2(deviceInfo, channelizerType));
|
||||
break;
|
||||
case DUAL:
|
||||
MasterSlaveBridge bridge = new MasterSlaveBridge();
|
||||
deviceInfo.setDeviceSelectionMode(DeviceSelectionMode.MASTER_TUNER_1);
|
||||
tuners.add(new DiscoveredRspDuoTuner1(deviceInfo, channelizerType, bridge));
|
||||
|
||||
DeviceInfo deviceInfoTuner2 = deviceInfo.clone();
|
||||
deviceInfoTuner2.setDeviceSelectionMode(DeviceSelectionMode.SLAVE_TUNER_2);
|
||||
tuners.add(new DiscoveredRspDuoTuner2(deviceInfoTuner2, channelizerType, bridge));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return tuners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the matching RSP device described by the device info object and creates an RSP tuner instance.
|
||||
* @param deviceInfo to match
|
||||
* @param channelizerType for the tuner
|
||||
* @param tunerErrorListener to monitor the tuner's status
|
||||
* @return constructed RSP tuner instance
|
||||
* @throws SDRPlayException if the device is not available or is not supported
|
||||
*/
|
||||
public static RspTuner getRspTuner(DeviceInfo deviceInfo, ChannelizerType channelizerType,
|
||||
ITunerErrorListener tunerErrorListener) throws SDRPlayException
|
||||
{
|
||||
//API instance is retained across the lifecycle of the constructed device, so we only close it if we don't get
|
||||
//a device from it.
|
||||
SDRplay api = new SDRplay();
|
||||
|
||||
if(api.isAvailable())
|
||||
{
|
||||
Device device = api.getDevice(deviceInfo);
|
||||
|
||||
switch(device.getDeviceType())
|
||||
{
|
||||
case RSP1:
|
||||
if(device instanceof Rsp1Device rsp1Device)
|
||||
{
|
||||
IControlRsp1 controlRsp1 = new ControlRsp1(rsp1Device);
|
||||
Rsp1TunerController rsp1TunerController = new Rsp1TunerController(controlRsp1, tunerErrorListener);
|
||||
return new RspTuner(rsp1TunerController, tunerErrorListener, channelizerType);
|
||||
}
|
||||
break;
|
||||
case RSP1A:
|
||||
if(device instanceof Rsp1aDevice rsp1aDevice)
|
||||
{
|
||||
IControlRsp1a controlRsp1a = new ControlRsp1a(rsp1aDevice);
|
||||
Rsp1aTunerController rsp1aTunerController = new Rsp1aTunerController(controlRsp1a, tunerErrorListener);
|
||||
return new RspTuner(rsp1aTunerController, tunerErrorListener, channelizerType);
|
||||
}
|
||||
break;
|
||||
case RSP2:
|
||||
if(device instanceof Rsp2Device rsp2Device)
|
||||
{
|
||||
IControlRsp2 controlRsp2 = new ControlRsp2(rsp2Device);
|
||||
Rsp2TunerController rsp2TunerController = new Rsp2TunerController(controlRsp2, tunerErrorListener);
|
||||
return new RspTuner(rsp2TunerController, tunerErrorListener, channelizerType);
|
||||
}
|
||||
break;
|
||||
case RSPdx:
|
||||
if(device instanceof RspDxDevice rspDxDevice)
|
||||
{
|
||||
IControlRspDx controlRspDx = new ControlRspDx(rspDxDevice);
|
||||
RspDxTunerController rspDxTunerController = new RspDxTunerController(controlRspDx, tunerErrorListener);
|
||||
return new RspTuner(rspDxTunerController, tunerErrorListener, channelizerType);
|
||||
}
|
||||
break;
|
||||
case RSPduo:
|
||||
if(device instanceof RspDuoDevice rspDuoDevice)
|
||||
{
|
||||
switch(deviceInfo.getDeviceSelectionMode())
|
||||
{
|
||||
case SINGLE_TUNER_1:
|
||||
IControlRspDuoTuner1 controlRspDuoTuner1 = new ControlRspDuoTuner1Single(rspDuoDevice);
|
||||
RspDuoTuner1Controller rspDuoTuner1Controller = new RspDuoTuner1Controller(controlRspDuoTuner1, tunerErrorListener);
|
||||
return new RspTuner(rspDuoTuner1Controller, tunerErrorListener, channelizerType);
|
||||
case SINGLE_TUNER_2:
|
||||
IControlRspDuoTuner2 controlRspDuoTuner2 = new ControlRspDuoTuner2Single(rspDuoDevice);
|
||||
RspDuoTuner2Controller rspDuoTuner2Controller = new RspDuoTuner2Controller(controlRspDuoTuner2, tunerErrorListener);
|
||||
return new RspTuner(rspDuoTuner2Controller, tunerErrorListener, channelizerType);
|
||||
default:
|
||||
throw new SDRPlayException("This method only supports RSPduo single tuner configurations");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case default:
|
||||
mLog.warn("Unrecognized SDRplay RSP Device Type: " + device.getDeviceType() + " SER#: " + device.getSerialNumber());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new SDRPlayException("Unable to obtain RSP tuner");
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the matching RSP device described by the device info object and creates an RSP tuner instance.
|
||||
* @param deviceInfo to match
|
||||
* @param channelizerType for the tuner
|
||||
* @param tunerErrorListener to monitor the tuner's status
|
||||
* @param bridge to link master/slave instances together
|
||||
* @return constructed RSP tuner instance
|
||||
* @throws SDRPlayException if the device is not available or is not supported
|
||||
*/
|
||||
public static RspTuner getRspDuoTuner(DeviceInfo deviceInfo, ChannelizerType channelizerType,
|
||||
ITunerErrorListener tunerErrorListener, MasterSlaveBridge bridge) throws SDRPlayException
|
||||
{
|
||||
SDRplay api = new SDRplay();
|
||||
|
||||
if(api.isAvailable())
|
||||
{
|
||||
Device device = api.getDevice(deviceInfo);
|
||||
|
||||
switch(device.getDeviceType())
|
||||
{
|
||||
case RSPduo:
|
||||
if(device instanceof RspDuoDevice rspDuoDevice)
|
||||
{
|
||||
switch(deviceInfo.getDeviceSelectionMode())
|
||||
{
|
||||
case MASTER_TUNER_1:
|
||||
IControlRspDuoTuner1 controlRspDuoTuner1 = new ControlRspDuoTuner1Master(rspDuoDevice, bridge);
|
||||
RspDuoTuner1Controller rspDuoTuner1Controller = new RspDuoTuner1Controller(controlRspDuoTuner1, tunerErrorListener);
|
||||
return new RspTuner(rspDuoTuner1Controller, tunerErrorListener, channelizerType);
|
||||
case SLAVE_TUNER_2:
|
||||
IControlRspDuoTuner2 controlRspDuoTuner2 = new ControlRspDuoTuner2Slave(rspDuoDevice, bridge);
|
||||
RspDuoTuner2Controller rspDuoTuner2Controller = new RspDuoTuner2Controller(controlRspDuoTuner2, tunerErrorListener);
|
||||
return new RspTuner(rspDuoTuner2Controller, tunerErrorListener, channelizerType);
|
||||
default:
|
||||
throw new SDRPlayException("This method only supports RSPduo single tuner configurations");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new SDRPlayException("Unable to obtain RSPduo tuner");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a USB tuner
|
||||
* @param tunerClass to instantiate
|
||||
@@ -148,6 +378,18 @@ public class TunerFactory
|
||||
return new R820TTunerConfiguration(uniqueID);
|
||||
case RECORDING:
|
||||
return RecordingTunerConfiguration.create();
|
||||
case RSP_1:
|
||||
return new Rsp1TunerConfiguration(uniqueID);
|
||||
case RSP_1A:
|
||||
return new Rsp1aTunerConfiguration(uniqueID);
|
||||
case RSP_2:
|
||||
return new Rsp2TunerConfiguration(uniqueID);
|
||||
case RSP_DUO_1:
|
||||
return new RspDuoTuner1Configuration(uniqueID);
|
||||
case RSP_DUO_2:
|
||||
return new RspDuoTuner2Configuration(uniqueID);
|
||||
case RSP_DX:
|
||||
return new RspDxTunerConfiguration(uniqueID);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized tuner type [" + type.name() + "]");
|
||||
}
|
||||
@@ -169,6 +411,42 @@ public class TunerFactory
|
||||
return new FCD2TunerEditor(userPreferences, tunerManager, discoveredTuner);
|
||||
case HACKRF:
|
||||
return new HackRFTunerEditor(userPreferences, tunerManager, discoveredTuner);
|
||||
case RSP:
|
||||
if(discoveredTuner instanceof DiscoveredRspTuner discoveredRspTuner)
|
||||
{
|
||||
switch(discoveredRspTuner.getDeviceType())
|
||||
{
|
||||
case RSP1:
|
||||
return new Rsp1TunerEditor(userPreferences, tunerManager, discoveredRspTuner);
|
||||
case RSP1A:
|
||||
return new Rsp1aTunerEditor(userPreferences, tunerManager, discoveredRspTuner);
|
||||
case RSP2:
|
||||
return new Rsp2TunerEditor(userPreferences, tunerManager, discoveredRspTuner);
|
||||
case RSPdx:
|
||||
return new RspDxTunerEditor(userPreferences, tunerManager, discoveredRspTuner);
|
||||
case RSPduo:
|
||||
if(discoveredRspTuner instanceof DiscoveredRspDuoTuner1 duoTuner1)
|
||||
{
|
||||
return new RspDuoTuner1Editor(userPreferences, tunerManager, duoTuner1);
|
||||
}
|
||||
else if(discoveredRspTuner instanceof DiscoveredRspDuoTuner2 duoTuner2)
|
||||
{
|
||||
return new RspDuoTuner2Editor(userPreferences, tunerManager, duoTuner2);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Unrecognized RSPduo device type:" +
|
||||
discoveredRspTuner.getClass());
|
||||
}
|
||||
case UNKNOWN:
|
||||
throw new IllegalArgumentException("Unrecognized RSP device type: " +
|
||||
discoveredRspTuner.getDeviceType());
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unrecognized discovered RSP tuner class: " +
|
||||
discoveredTuner.getClass());
|
||||
case RECORDING_TUNER:
|
||||
return new RecordingTunerEditor(userPreferences, tunerManager, discoveredTuner);
|
||||
case RTL2832:
|
||||
if(discoveredTuner.hasTuner())
|
||||
{
|
||||
@@ -181,8 +459,6 @@ public class TunerFactory
|
||||
}
|
||||
}
|
||||
return new RTL2832UnknownTunerEditor(userPreferences, tunerManager, discoveredTuner);
|
||||
case RECORDING_TUNER:
|
||||
return new RecordingTunerEditor(userPreferences, tunerManager, discoveredTuner);
|
||||
case TEST_TUNER:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
|
||||
@@ -37,6 +37,13 @@ public enum TunerType
|
||||
HACKRF_RAD1O("RAD1O"),
|
||||
RAFAELMICRO_R820T("R820T"),
|
||||
RAFAELMICRO_R828D("R828D"),
|
||||
RSP_1("RSP1"),
|
||||
RSP_1A("RSP1A"),
|
||||
RSP_2("RSP2"),
|
||||
RSP_DUO_1("RSPduo Tuner 1"),
|
||||
RSP_DUO_2("RSPduo Tuner 2"),
|
||||
RSP_DX("RSPdx"),
|
||||
|
||||
TEST("Test"),
|
||||
RECORDING("Recording"),
|
||||
UNKNOWN("Unknown");
|
||||
|
||||
@@ -31,6 +31,7 @@ import io.github.dsheirer.source.tuner.hackrf.HackRFTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.RspTunerConfiguration;
|
||||
|
||||
/**
|
||||
* Abstract class to hold a configuration for a specific type of tuner
|
||||
@@ -44,6 +45,7 @@ import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerConfiguration;
|
||||
@JsonSubTypes.Type(value = HackRFTunerConfiguration.class, name = "hackRFTunerConfiguration"),
|
||||
@JsonSubTypes.Type(value = RecordingTunerConfiguration.class, name = "recordingTunerConfiguration"),
|
||||
@JsonSubTypes.Type(value = R820TTunerConfiguration.class, name = "r820TTunerConfiguration"),
|
||||
@JsonSubTypes.Type(value = RspTunerConfiguration.class, name = "rspTunerConfiguration"),
|
||||
})
|
||||
@JacksonXmlRootElement(localName = "tuner_configuration")
|
||||
public abstract class TunerConfiguration
|
||||
|
||||
@@ -147,6 +147,12 @@ public class TunerConfigurationManager implements IDiscoveredTunerStatusListener
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors discovered tuner enabled status and applies configurations or updates disable state of tuners.
|
||||
* @param discoveredTuner that has a status change.
|
||||
* @param previous tuner status
|
||||
* @param current tuner status
|
||||
*/
|
||||
@Override
|
||||
public void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current)
|
||||
{
|
||||
@@ -157,6 +163,23 @@ public class TunerConfigurationManager implements IDiscoveredTunerStatusListener
|
||||
else if(current == TunerStatus.ENABLED)
|
||||
{
|
||||
removeDisabledTuner(discoveredTuner);
|
||||
|
||||
if(discoveredTuner.hasTuner())
|
||||
{
|
||||
TunerType tunerType = discoveredTuner.getTuner().getTunerType();
|
||||
|
||||
if(tunerType != TunerType.RECORDING)
|
||||
{
|
||||
TunerConfiguration tunerConfiguration = getTunerConfiguration(tunerType, discoveredTuner.getId());
|
||||
|
||||
if(tunerConfiguration != null)
|
||||
{
|
||||
discoveredTuner.setTunerConfiguration(tunerConfiguration);
|
||||
saveConfigurations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,10 @@ public abstract class DiscoveredTuner implements ITunerErrorListener
|
||||
*/
|
||||
public void addTunerStatusListener(IDiscoveredTunerStatusListener listener)
|
||||
{
|
||||
mListeners.add(listener);
|
||||
if(!mListeners.contains(listener))
|
||||
{
|
||||
mListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,6 +260,12 @@ public abstract class DiscoveredTuner implements ITunerErrorListener
|
||||
setTunerStatus(TunerStatus.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tunerRemoved()
|
||||
{
|
||||
setTunerStatus(TunerStatus.REMOVED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this tuner has an error message.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user