Initial implementation of: reader thread for async handling of rig data, UDP multicast publisher routine and rig state poll thread routine. The reader thread can correctly handle asynchronous data, such as transceive or spectrum data. Work in progress: multi-platform code for I/O routines still missing and the poll routine is not yet in use. Tested briefly on an IC-7300 so far.

pull/892/head
Mikael Nousiainen 2021-11-28 20:52:29 +02:00
rodzic 39cec4aa27
commit d857f18163
35 zmienionych plików z 5648 dodań i 1770 usunięć

Wyświetl plik

@ -1,10 +1,9 @@
Planned for version 4.3 -- comments/suggestions about this are more than welcome
Multicast UDP broadcast on port 4531 (one below rigctld 4532)
Planned for version 5.0 -- comments/suggestions about this are more than welcome
Multicast UDP broadcast on port 4532
Bidirectional rig control and status
Choice of token pairs or JSON
All packets will be tagged with ID=[unique name] -- so multiple rigs can broadcast/rx on the same port
Broadcast packet contents to be based on get_rig_info output
This will be the text format of name=value pairs
Can be multiple VFO lines
@ -25,163 +24,94 @@ CRC=0xf49f4708 (this is just an example CRC and not accurate for this example)
Example JSON
{
"__comment1__": "customizable rig identification -- will allow multiple rigs to be on the multicast",
"ID": "Rig#1",
"VFOs": [
"app": "Hamlib",
"__comment_version__": "protocol version YYYYMMDD x.x.x, 1.0.0 will be used when this is implemented",
"version": "20210521 0.0.0",
"__comment_seq__": "Seq is 1-up sequence number 32-bit -- wraps around to 1 from 2^32-1",
"seq": 1,
"__comment_crc__": "32-bit CRC of entire JSON record replacing the CRC value with 0x00000000",
"crc": "0x00000000",
"rig": {
"__comment1__": "customizable rig identification -- will allow multiple rigs to be on the multicast",
"id": "Rig#1",
"name": "Dummy",
"ptt" : false,
"split": true,
"splitVfo": "VFOB",
"satMode": false,
"status": "OK";
"errorMsg": "OK",
},
"vfos": [
{
"Name": "VFOA",
"Freq": 14074000,
"Mode": "USB",
"Width": 5000,
"RX": true,
"TX": false
"name": "VFOA",
"freq": 14074000,
"mode": "USB",
"width": 5000,
"rx": true,
"tx": false
},
{
"Name": "VFOB",
"Freq": 14076000,
"Mode": "USB",
"Width": 5000,
"RX": false,
"TX": false
"name": "VFOB",
"freq": 14076000,
"mode": "USB",
"width": 5000,
"rx": false,
"tx": true
}],
"__comment_spectra__": "Rigs that have spectrum output may include this array data",
"Spectra": [
"spectra": [
{
"Name": "Main",
"Length": 475,
"__comment_spectrum_data__": "2-char hex bytes so data len=2*Length",
"Data": "00AAFF75BD2AAA...",
"Type": "FIXED|CENTER",
"MinLevel": 0,
"MaxLevel": 140,
"MinStrength": -100,
"MaxStrength": 0,
"__comment_id__": "A numeric ID for the spectrum data stream. These IDs are exposed in rig caps.",
"id": 0,
"__comment_name__": "Name identifying the spectrum data stream and matching the ID. The name corresponds to VFOs.",
"name": "Main",
"__comment_spectrum_length__": "Length of spectrum FFT data in bytes",
"length": 475,
"__comment_spectrum_data__": "Spectrum FFT data in 2-char hexadecimal byte format, so that length of the string is 2 * length",
"data": "00AAFF75BD2AAA...",
"__comment_spectrum_center__": "If Type=CENTER, the following fields will be present:",
"CenterFreq": 14267000,
"Span": 25000,
"type": "FIXED|CENTER",
"minLevel": 0,
"maxLevel": 140,
"minStrength": -100,
"maxStrength": 0,
"__comment_spectrum_fixed__": "If SpectrumType=FIXED, the following fields will be present:",
"LowFreq": 14000000,
"HighFreq": 14250000
"__comment_spectrum_frequencies__": "The following fields will be calculated automatically by Hamlib based on the spectrum information exposed by the rig",
"centerFreq": 14267000,
"span": 25000,
"lowFreq": 14000000,
"highFreq": 14250000
},
{
"Name": "Sub",
"Length": 475,
"__comment_spectrum_data__": "2-char hex bytes so data len=2*Length",
"Data": "00AAFF75BD2AAA...",
"Type": "FIXED|CENTER",
"MinLevel": 0,
"MaxLevel": 140,
"MinStrength": -100,
"MaxStrength": 0,
"__comment_id__": "A numeric ID for the spectrum data stream. These IDs are exposed in rig caps.",
"id": 1,
"__comment_name__": "Name identifying the spectrum data stream and matching the ID. The name corresponds to VFOs.",
"name": "Sub",
"__comment_spectrum_length__": "Length of spectrum FFT data in bytes",
"length": 475,
"__comment_spectrum_data__": "Spectrum FFT data in 2-char hexadecimal byte format, so that length of the string is 2 * length",
"data": "00AAFF75BD2AAA...",
"__comment_spectrum_center__": "If Type=CENTER, the following fields will be present:",
"CenterFreq": 14267000,
"Span": 25000,
"type": "FIXED|CENTER",
"minLevel": 0,
"maxLevel": 140,
"minStrength": -100,
"maxStrength": 0,
"__comment_spectrum_fixed__": "If SpectrumType=FIXED, the following fields will be present:",
"LowFreq": 14000000,
"HighFreq": 14250000
"__comment_spectrum_frequencies__": "The following fields will be calculated automatically by Hamlib based on the spectrum information exposed by the rig",
"centerFreq": 14267000,
"span": 25000,
"lowFreq": 14000000,
"highFreq": 14250000
}],
LastCommand {
"ID": "MyApp 123",
"Command": "set_freq VFOA 14074000".
"Status": "OK"
"lastCommand": {
"id": "MyApp 123",
"command": "set_freq VFOA 14074000",
"status": "OK"
},
"PTT" : false,
"Split": true,
"SatMode": false,
"Rig": "Dummy",
"App": "Hamlib",
"__comment_version__": "protocol version YYYYMMDD x.x.x, 1.0.0 will be used when this is implemented",
"Version": "20210521 0.0.0",
"Status": "OK";
"__comment_seq__": "Seq is 1-up sequence number 32-bit -- wraps around to 1 from 2^32-1",
"Seq": 1,
"__comment_crc__": "32-bit CRC of entire JSON record replacing the CRC value with 0x00000000",
"ErrorMsg": "OK",
"CRC": "0x00000000"
}
Will be able to set freq, mode, width, ptt, satmode, and split to start since those are common to many apps.
More functions will be added as time goes on.
C# Json Deserialize Example -- beginning of a HamlibMultiCast client
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
namespace HamlibMultiCast
{
public class HamlibMulticastClient
{
public class VFO
{
public string Name;
public ulong Freq;
public string Mode;
public int Width;
public bool RX;
public bool TX;
}
public class SpectrumClass
{
public int Length;
public string Data;
public string Type;
public int MinLevel;
public int MaxLevel;
public int MinStrength;
public int MaxStrength;
public double CenterFreq;
public int Span;
public double LowFreq;
public double HighFreq;
}
LastCommand {
public string ID; // an application name plus sequence number recommended
public string Command;
public string Status;
}
public string ID;
public List<VFO> VFOs { get; set; }
public bool PTT;
public bool Split;
public bool SatMode;
public string Rig;
public string App;
public string Version;
public string Status;
public UInt32 Seq;
public string CRC;
public SpectrumClass Spectrum { get; set; }
public string ErrorMsg;
}
class Program
{
static int Main(string[] args)
{
Console.WriteLine("HamlibMultiCast Test Json Deserialize");
ITraceWriter traceWriter = new MemoryTraceWriter();
try
{
string json = File.ReadAllText("test.json");
var client = JsonConvert.DeserializeObject<HamlibMulticastClient>(json, new JsonSerializerSettings { TraceWriter = traceWriter, Converters = { new JavaScriptDateTimeConverter() } });
Console.WriteLine(traceWriter);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return 1;
}
return 0;
}
}
}

Wyświetl plik

@ -967,7 +967,7 @@ type
{*
* non overridable fields, internal use
*}
hold_decode : integer;
transaction_active : integer;
current_vfo : vfo_t;
transceive : integer;
vfo_list : integer;

Wyświetl plik

@ -147,7 +147,8 @@ enum rig_errcode_e {
RIG_BUSBUSY, /*!< 14 Collision on the bus */
RIG_EARG, /*!< 15 NULL RIG handle or any invalid pointer parameter in get arg */
RIG_EVFO, /*!< 16 Invalid VFO */
RIG_EDOM /*!< 17 Argument out of domain of func */
RIG_EDOM, /*!< 17 Argument out of domain of func */
RIG_EDEPRECATED /*!< 18 Function deprecated */
};
/**
@ -1045,12 +1046,15 @@ typedef uint64_t setting_t;
* \brief Transceive mode
* The rig notifies the host of any event, like freq changed, mode changed, etc.
* \def RIG_TRN_OFF
* \deprecated
* Turn it off
* \brief Transceive mode
* \def RIG_TRN_RIG
* \deprecated
* RIG_TRN_RIG means the rig acts asynchronously
* \brief Transceive mode
* \def RIG_TRN_POLL
* \deprecated
* RIG_TRN_POLL means we have to poll the rig
*
*/
@ -1681,7 +1685,7 @@ struct rig_spectrum_line
freq_t low_edge_freq; /*!< Low edge frequency of the spectrum scope in Hz in RIG_SPECTRUM_FIXED mode. */
freq_t high_edge_freq; /*!< High edge frequency of the spectrum scope in Hz in RIG_SPECTRUM_FIXED mode. */
int spectrum_data_length; /*!< Number of bytes of 8-bit spectrum data in the data buffer. The amount of data may vary if the rig has multiple spectrum scopes, depending on the scope. */
size_t spectrum_data_length; /*!< Number of bytes of 8-bit spectrum data in the data buffer. The amount of data may vary if the rig has multiple spectrum scopes, depending on the scope. */
unsigned char *spectrum_data; /*!< 8-bit spectrum data covering bandwidth of either the span_freq in center mode or from low edge to high edge in fixed mode. A higher value represents higher signal strength. */
};
@ -1763,7 +1767,7 @@ struct rig_caps {
vfo_op_t vfo_ops; /*!< VFO op bit field list */
scan_t scan_ops; /*!< Scan bit field list */
int targetable_vfo; /*!< Bit field list of direct VFO access commands */
int transceive; /*!< Supported transceive mode */
int transceive; /*!< \deprecated Use async_data_supported instead */
int bank_qty; /*!< Number of banks */
int chan_desc_sz; /*!< Max length of memory channel name */
@ -1985,7 +1989,7 @@ struct rig_caps {
const char *clone_combo_get; /*!< String describing key combination to enter save cloning mode */
const char *macro_name; /*!< Rig model macro name */
int async_data_supported;
int async_data_supported; /*!< Indicates that rig is capable of outputting asynchronous data updates, such as transceive state updates or spectrum data. 1 if true, 0 otherwise. */
int (*read_frame_direct)(RIG *rig,
size_t buffer_length,
const unsigned char *buffer);
@ -2088,6 +2092,9 @@ enum rig_function_e {
RIG_FUNCTION_SET_MEM_ALL_CB,
RIG_FUNCTION_GET_MEM_ALL_CB,
RIG_FUNCTION_SET_VFO_OPT,
RIG_FUNCTION_READ_FRAME_DIRECT,
RIG_FUNCTION_IS_ASYNC_FRAME,
RIG_FUNCTION_PROCESS_ASYNC_FRAME,
};
/**
@ -2198,9 +2205,11 @@ typedef struct hamlib_port {
int client_port; /*!< client socket port for tcp connection */
RIG *rig; /*!< our parent RIG device */
int async; /*!< enable asynchronous data handling if true */
int fd_sync_write; /*!< file descriptor for writing synchronous data */
int fd_sync_read; /*!< file descriptor for reading synchronous data */
int async; /*!< enable asynchronous data handling if true */
int fd_sync_write; /*!< file descriptor for writing synchronous data */
int fd_sync_read; /*!< file descriptor for reading synchronous data */
int fd_sync_error_write; /*!< file descriptor for writing synchronous data error codes */
int fd_sync_error_read; /*!< file descriptor for reading synchronous data error codes */
} hamlib_port_t;
//! @endcond
@ -2363,15 +2372,15 @@ struct rig_state {
* non overridable fields, internal use
*/
int hold_decode; /*!< set to 1 to hold the event decoder (async) otherwise 0 */
int transaction_active; /*!< set to 1 to inform the async reader thread that a synchronous command transaction is waiting for a response, otherwise 0 */
vfo_t current_vfo; /*!< VFO currently set */
int vfo_list; /*!< Complete list of VFO for this rig */
int comm_state; /*!< Comm port state, opened/closed. */
rig_ptr_t priv; /*!< Pointer to private rig state data. */
rig_ptr_t obj; /*!< Internal use by hamlib++ for event handling. */
int transceive; /*!< Whether the transceive mode is on */
int poll_interval; /*!< Event notification polling period in milliseconds */
int async_data; /*!< Whether async data mode is on */
int poll_interval; /*!< Rig state polling period in milliseconds */
freq_t current_freq; /*!< Frequency currently set */
rmode_t current_mode; /*!< Mode currently set */
//rmode_t current_modeB; /*!< Mode currently set VFOB */
@ -2401,10 +2410,19 @@ struct rig_state {
int power_now; /*!< Current RF power level in rig units */
int power_min; /*!< Minimum RF power level in rig units */
int power_max; /*!< Maximum RF power level in rig units */
unsigned char disable_yaesu_bandselect; /*!< Disables Yaeus band select logic */
unsigned char disable_yaesu_bandselect; /*!< Disables Yaesu band select logic */
int twiddle_rit; /*!< Suppresses VFOB reading (cached value used) so RIT control can be used */
int twiddle_state; /*!< keeps track of twiddle status */
vfo_t rx_vfo; /*!< Rx VFO currently set */
volatile unsigned int snapshot_packet_sequence_number;
volatile int multicast_publisher_run;
void *multicast_publisher_priv_data;
volatile int async_data_handler_thread_run;
void *async_data_handler_priv_data;
volatile int poll_routine_thread_run;
void *poll_routine_priv_data;
};
//! @cond Doxygen_Suppress

Wyświetl plik

@ -3,5 +3,5 @@ EXTRA_DIST = getopt.c getopt.h getopt_long.c usleep.c \
noinst_LTLIBRARIES = libmisc.la
libmisc_la_SOURCES =
libmisc_la_SOURCES = cJSON.c cJSON.h
libmisc_la_LIBADD = $(LTLIBOBJS) $(NET_LIBS)

3110
lib/cJSON.c 100644

Plik diff jest za duży Load Diff

293
lib/cJSON.h 100644
Wyświetl plik

@ -0,0 +1,293 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 15
#include <stddef.h>
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
typedef struct cJSON_Hooks
{
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
void *(CJSON_CDECL *malloc_fn)(size_t sz);
void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* Create a string where valuestring references a string so
* it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
* they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
* The item->next and ->prev pointers are always zero on return from Duplicate. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
* The input pointer json cannot point to a read-only address area, such as a string constant,
* but should point to a readable and writable address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
#ifdef __cplusplus
}
#endif
#endif

Wyświetl plik

@ -189,7 +189,7 @@ int elad_transaction(RIG *rig, const char *cmdstr, char *data, size_t datasize)
rs = &rig->state;
rs->hold_decode = 1;
rs->transaction_active = 1;
/* Emulators don't need any post_write_delay */
if (priv->is_emulation) { rs->rigport.post_write_delay = 0; }
@ -237,7 +237,7 @@ transaction_write:
if (!datasize)
{
rig->state.hold_decode = 0;
rig->state.transaction_active = 0;
/* no reply expected so we need to write a command that always
gives a reply so we can read any error replies from the actual
@ -414,7 +414,7 @@ transaction_read:
transaction_quit:
rs->hold_decode = 0;
rs->transaction_active = 0;
return retval;
}

Wyświetl plik

@ -153,7 +153,7 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
/*
* should check return code and that write wrote cmd_len chars!
*/
Hold_Decode(rig);
set_transaction_active(rig);
rig_flush(&rs->rigport);
@ -163,7 +163,7 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
if (retval != RIG_OK)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(retval);
}
@ -185,14 +185,14 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
if (retval == -RIG_ETIMEOUT || retval == 0)
{
/* Nothing received, CI-V interface is not echoing */
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_BUSERROR);
}
if (retval < 0)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
/* Other error, return it */
RETURNFUNC(retval);
@ -200,7 +200,7 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
if (retval < 1)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_EPROTO);
}
@ -209,7 +209,7 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
{
case COL:
/* Collision */
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_BUSBUSY);
@ -220,7 +220,7 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
default:
/* Timeout after reading at least one character */
/* Problem on ci-v bus? */
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_BUSERROR);
}
@ -230,7 +230,7 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
/* Not the same length??? */
/* Problem on ci-v bus? */
/* Someone else got a packet in? */
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_EPROTO);
}
@ -240,7 +240,7 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
/* Frames are different? */
/* Problem on ci-v bus? */
/* Someone else got a packet in? */
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_EPROTO);
}
@ -251,7 +251,7 @@ int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd,
*/
if (data_len == NULL)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(RIG_OK);
}
@ -284,7 +284,7 @@ read_another_frame:
if (frm_len < 0)
{
rig_unlock();
Unhold_Decode(rig);
set_transaction_inactive(rig);
/* RIG_TIMEOUT: timeout getting response, return timeout */
/* other error: return it */
RETURNFUNC(frm_len);
@ -293,7 +293,7 @@ read_another_frame:
if (frm_len < 1)
{
rig_unlock();
Unhold_Decode(rig);
set_transaction_inactive(rig);
RETURNFUNC(-RIG_EPROTO);
}
@ -301,7 +301,7 @@ read_another_frame:
if (retval < 0)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(retval);
}
@ -318,7 +318,7 @@ read_another_frame:
switch (buf[frm_len - 1])
{
case COL:
Unhold_Decode(rig);
set_transaction_inactive(rig);
/* Collision */
rig_unlock();
RETURNFUNC(-RIG_BUSBUSY);
@ -328,12 +328,12 @@ read_another_frame:
break;
case NAK:
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_ERJCTED);
default:
Unhold_Decode(rig);
set_transaction_inactive(rig);
/* Timeout after reading at least one character */
/* Problem on ci-v bus? */
rig_unlock();
@ -342,7 +342,7 @@ read_another_frame:
if (frm_len < ACKFRMLEN)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_EPROTO);
}
@ -351,7 +351,7 @@ read_another_frame:
// e.g. fe fe e0 50 fa fd
if (frm_len == 6 && NAK == buf[frm_len - 2])
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_ERJCTED);
}
@ -362,7 +362,7 @@ read_another_frame:
// has to be one of these two now or frame is corrupt
if (FI != buf[frm_len - 1] && ACK != buf[frm_len - 1])
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_BUSBUSY);
}
@ -371,7 +371,7 @@ read_another_frame:
if (frm_data_len <= 0)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_EPROTO);
}
@ -389,7 +389,7 @@ read_another_frame:
if (elapsed_ms > rs->rigport.timeout)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
rig_unlock();
RETURNFUNC(-RIG_ETIMEOUT);
}
@ -397,7 +397,7 @@ read_another_frame:
goto read_another_frame;
}
Unhold_Decode(rig);
set_transaction_inactive(rig);
*data_len = frm_data_len;
memcpy(data, buf + 4, *data_len);

Wyświetl plik

@ -44,6 +44,7 @@
#include "icom_defs.h"
#include "frame.h"
#include "misc.h"
#include "event.h"
// we automatically determine availability of the 1A 03 command
enum { ENUM_1A_03_UNK, ENUM_1A_03_YES, ENUM_1A_03_NO };
@ -8455,7 +8456,7 @@ int icom_mW2power(RIG *rig, float *power, unsigned int mwpower, freq_t freq,
RETURNFUNC(RIG_OK);
}
static int icom_parse_spectrum_frame(RIG *rig, int length,
static int icom_parse_spectrum_frame(RIG *rig, size_t length,
const unsigned char *frame_data)
{
struct rig_caps *caps = rig->caps;
@ -8466,7 +8467,7 @@ static int icom_parse_spectrum_frame(RIG *rig, int length,
int division = (int) from_bcd(frame_data + 1, 1 * 2);
int max_division = (int) from_bcd(frame_data + 2, 1 * 2);
int spectrum_data_length_in_frame;
size_t spectrum_data_length_in_frame;
const unsigned char *spectrum_data_start_in_frame;
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
@ -8541,7 +8542,7 @@ static int icom_parse_spectrum_frame(RIG *rig, int length,
cache->spectrum_metadata_valid = 1;
rig_debug(RIG_DEBUG_TRACE,
"%s: Spectrum line start: id=%d division=%d max_division=%d mode=%d center=%.0f span=%.0f low_edge=%.0f high_edge=%.0f oor=%d data_length=%d\n",
"%s: Spectrum line start: id=%d division=%d max_division=%d mode=%d center=%.0f span=%.0f low_edge=%.0f high_edge=%.0f oor=%d data_length=%ld\n",
__func__, spectrum_id, division, max_division, spectrum_scope_mode,
cache->spectrum_center_freq, cache->spectrum_span_freq,
cache->spectrum_low_edge_freq, cache->spectrum_high_edge_freq, out_of_range,
@ -8563,7 +8564,7 @@ static int icom_parse_spectrum_frame(RIG *rig, int length,
priv_caps->spectrum_scope_caps.spectrum_line_length)
{
rig_debug(RIG_DEBUG_ERR,
"%s: too much spectrum scope data received: %d bytes > %d bytes expected\n",
"%s: too much spectrum scope data received: %ld bytes > %d bytes expected\n",
__func__, offset + spectrum_data_length_in_frame,
priv_caps->spectrum_scope_caps.spectrum_line_length);
RETURNFUNC(-RIG_EPROTO);
@ -8578,6 +8579,7 @@ static int icom_parse_spectrum_frame(RIG *rig, int length,
{
struct rig_spectrum_line spectrum_line =
{
.id = spectrum_id,
.data_level_min = priv_caps->spectrum_scope_caps.data_level_min,
.data_level_max = priv_caps->spectrum_scope_caps.data_level_max,
.signal_strength_min = priv_caps->spectrum_scope_caps.signal_strength_min,
@ -8591,10 +8593,7 @@ static int icom_parse_spectrum_frame(RIG *rig, int length,
.spectrum_data = cache->spectrum_data,
};
if (rig->callbacks.spectrum_event)
{
rig->callbacks.spectrum_event(rig, &spectrum_line, rig->callbacks.spectrum_arg);
}
rig_fire_spectrum_event(rig, &spectrum_line);
cache->spectrum_metadata_valid = 0;
}
@ -8634,38 +8633,19 @@ int icom_process_async_frame(RIG *rig, size_t frame_length,
*/
switch (frame[4])
{
case C_SND_FREQ:
case C_SND_FREQ: {
/*
* TODO: the freq length might be less than 4 or 5 bytes
* on older rigs!
*/
if (rig->callbacks.freq_event)
{
freq_t freq;
freq = from_bcd(frame + 5, (priv->civ_731_mode ? 4 : 5) * 2);
RETURNFUNC(rig->callbacks.freq_event(rig, RIG_VFO_CURR, freq,
rig->callbacks.freq_arg));
}
else
{
RETURNFUNC(-RIG_ENAVAIL);
}
freq_t freq = (freq_t) from_bcd(frame + 5, (priv->civ_731_mode ? 4 : 5) * 2);
rig_fire_freq_event(rig, RIG_VFO_CURR, freq);
break;
}
case C_SND_MODE:
if (rig->callbacks.mode_event)
{
icom2rig_mode(rig, frame[5], frame[6], &mode, &width);
RETURNFUNC(rig->callbacks.mode_event(rig, RIG_VFO_CURR,
mode, width, rig->callbacks.mode_arg));
}
else
{
RETURNFUNC(-RIG_ENAVAIL);
}
icom2rig_mode(rig, frame[5], frame[6], &mode, &width);
rig_fire_mode_event(rig, RIG_VFO_CURR, mode, width);
break;
case C_CTL_SCP:
@ -8673,7 +8653,6 @@ int icom_process_async_frame(RIG *rig, size_t frame_length,
{
icom_parse_spectrum_frame(rig, frame_length - (6 + 1), frame + 6);
}
break;
default:

Wyświetl plik

@ -181,7 +181,7 @@ struct icom_spectrum_scope_cache
freq_t spectrum_span_freq; /*!< The frequency span of the current spectrum scope line being received */
freq_t spectrum_low_edge_freq; /*!< The low edge frequency of the current spectrum scope line being received */
freq_t spectrum_high_edge_freq; /*!< The high edge frequency of the current spectrum scope line being received */
int spectrum_data_length; /*!< Number of bytes of 8-bit spectrum data in the data buffer. The amount of data may vary if the rig has multiple spectrum scopes, depending on the scope. */
size_t spectrum_data_length; /*!< Number of bytes of 8-bit spectrum data in the data buffer. The amount of data may vary if the rig has multiple spectrum scopes, depending on the scope. */
unsigned char *spectrum_data; /*!< Dynamically allocated buffer for raw spectrum data */
};

Wyświetl plik

@ -77,25 +77,25 @@ static int jrc_transaction(RIG *rig, const char *cmd, int cmd_len, char *data,
rig_flush(&rs->rigport);
Hold_Decode(rig);
set_transaction_active(rig);
retval = write_block(&rs->rigport, cmd, cmd_len);
if (retval != RIG_OK)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
return retval;
}
if (!data || !data_len)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
return 0;
}
retval = read_string(&rs->rigport, data, BUFSZ, EOM, strlen(EOM), 0);
Unhold_Decode(rig);
set_transaction_inactive(rig);
if (retval < 0)
{

Wyświetl plik

@ -254,7 +254,7 @@ int kenwood_transaction(RIG *rig, const char *cmdstr, char *data,
rs = &rig->state;
rs->hold_decode = 1;
rs->transaction_active = 1;
/* Emulators don't need any post_write_delay */
if (priv->is_emulation) { rs->rigport.post_write_delay = 0; }
@ -350,7 +350,7 @@ transaction_write:
if (!datasize)
{
rig->state.hold_decode = 0;
rig->state.transaction_active = 0;
// there are some commands that have problems with immediate follow-up
// so we'll just ignore them
@ -578,7 +578,7 @@ transaction_quit:
strncpy(priv->last_if_response, buffer, caps->if_len);
}
rs->hold_decode = 0;
rs->transaction_active = 0;
RETURNFUNC(retval);
}

Wyświetl plik

@ -361,11 +361,11 @@ pcr_send(RIG *rig, const char *cmd)
/* XXX not required in auto update mode? (should not harm) */
priv->cmd_buf[len + 0] = 0x0a;
rs->hold_decode = 1;
rs->transaction_active = 1;
err = write_block(&rs->rigport, priv->cmd_buf, len + 1);
rs->hold_decode = 0;
rs->transaction_active = 0;
return err;
}
@ -520,8 +520,6 @@ pcr_init(RIG *rig)
priv->sub_rcvr = priv->main_rcvr;
priv->current_vfo = RIG_VFO_MAIN;
rig->state.transceive = RIG_TRN_OFF;
return RIG_OK;
}

Wyświetl plik

@ -79,10 +79,10 @@ tt550_transaction(RIG *rig, const char *cmd, int cmd_len, char *data,
rs = &rig->state;
/*
* Hold_Decode keeps the asynchronous decode routine from being called
* set_transaction_active keeps the asynchronous decode routine from being called
* when we get data back from a normal command.
*/
Hold_Decode(rig);
set_transaction_active(rig);
rig_flush(&rs->rigport);
@ -90,7 +90,7 @@ tt550_transaction(RIG *rig, const char *cmd, int cmd_len, char *data,
if (retval != RIG_OK)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
return retval;
}
@ -99,7 +99,7 @@ tt550_transaction(RIG *rig, const char *cmd, int cmd_len, char *data,
*/
if (!data || !data_len)
{
Unhold_Decode(rig);
set_transaction_inactive(rig);
return 0;
}
@ -117,7 +117,7 @@ tt550_transaction(RIG *rig, const char *cmd, int cmd_len, char *data,
*data_len = retval;
Unhold_Decode(rig);
set_transaction_inactive(rig);
return RIG_OK;
}

Wyświetl plik

@ -128,7 +128,7 @@ uniden_transaction(RIG *rig, const char *cmdstr, int cmd_len,
size_t reply_len = BUFSZ;
rs = &rig->state;
rs->hold_decode = 1;
rs->transaction_active = 1;
transaction_write:
@ -267,7 +267,7 @@ transaction_write:
retval = RIG_OK;
transaction_quit:
rs->hold_decode = 0;
rs->transaction_active = 0;
return retval;
}

Wyświetl plik

@ -110,7 +110,7 @@ uniden_digital_transaction(RIG *rig, const char *cmdstr, int cmd_len,
size_t reply_len = BUFSZ;
rs = &rig->state;
rs->hold_decode = 1;
rs->transaction_active = 1;
transaction_write:
@ -262,7 +262,7 @@ transaction_write:
retval = RIG_OK;
transaction_quit:
rs->hold_decode = 0;
rs->transaction_active = 0;
return retval;
}

Wyświetl plik

@ -11,7 +11,7 @@ RIGSRC = hamlibdatetime.h rig.c serial.c serial.h misc.c misc.h register.c regis
network.c network.h cm108.c cm108.h gpio.c gpio.h idx_builtin.h token.h \
par_nt.h microham.c microham.h amplifier.c amp_reg.c amp_conf.c \
amp_conf.h amp_settings.c extamp.c sleep.c sleep.h sprintflst.c \
sprintflst.h
sprintflst.h cache.c cache.h snapshot_data.c snapshot_data.h
lib_LTLIBRARIES = libhamlib.la
libhamlib_la_SOURCES = $(RIGSRC)

444
src/cache.c 100644
Wyświetl plik

@ -0,0 +1,444 @@
/*
* Hamlib Interface - rig state cache routines
* Copyright (c) 2000-2012 by Stephane Fillod
* Copyright (c) 2000-2003 by Frank Singleton
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "cache.h"
#include "misc.h"
#define CHECK_RIG_ARG(r) (!(r) || !(r)->caps || !(r)->state.comm_state)
/**
* \addtogroup rig
* @{
*/
int rig_set_cache_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
{
ENTERFUNC;
rig_cache_show(rig, __func__, __LINE__);
if (vfo == RIG_VFO_CURR)
{
// if CURR then update this before we figure out the real VFO
vfo = rig->state.current_vfo;
}
// pick a sane default
if (vfo == RIG_VFO_NONE || vfo == RIG_VFO_CURR) { vfo = RIG_VFO_A; }
if (vfo == RIG_VFO_SUB && rig->state.cache.satmode) { vfo = RIG_VFO_SUB_A; };
switch (vfo)
{
case RIG_VFO_ALL: // we'll use NONE to reset all VFO caches
elapsed_ms(&rig->state.cache.time_modeMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainC, HAMLIB_ELAPSED_INVALIDATE);
break;
case RIG_VFO_A:
case RIG_VFO_MAIN:
case RIG_VFO_MAIN_A:
rig->state.cache.modeMainA = mode;
if (width > 0) { rig->state.cache.widthMainA = width; }
elapsed_ms(&rig->state.cache.time_modeMainA, HAMLIB_ELAPSED_SET);
elapsed_ms(&rig->state.cache.time_widthMainA, HAMLIB_ELAPSED_SET);
break;
case RIG_VFO_B:
case RIG_VFO_SUB:
case RIG_VFO_MAIN_B:
rig->state.cache.modeMainB = mode;
if (width > 0) { rig->state.cache.widthMainB = width; }
elapsed_ms(&rig->state.cache.time_modeMainB, HAMLIB_ELAPSED_SET);
elapsed_ms(&rig->state.cache.time_widthMainB, HAMLIB_ELAPSED_SET);
break;
case RIG_VFO_C:
case RIG_VFO_MAIN_C:
rig->state.cache.modeMainC = mode;
if (width > 0) { rig->state.cache.widthMainC = width; }
elapsed_ms(&rig->state.cache.time_modeMainC, HAMLIB_ELAPSED_SET);
elapsed_ms(&rig->state.cache.time_widthMainC, HAMLIB_ELAPSED_SET);
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unknown vfo=%s\n", __func__, rig_strvfo(vfo));
RETURNFUNC(-RIG_EINTERNAL);
}
rig_cache_show(rig, __func__, __LINE__);
RETURNFUNC(RIG_OK);
}
int rig_set_cache_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
int flag = HAMLIB_ELAPSED_SET;
if (rig_need_debug(RIG_DEBUG_CACHE))
{
ENTERFUNC;
rig_cache_show(rig, __func__, __LINE__);
}
rig_debug(RIG_DEBUG_CACHE, "%s: vfo=%s, current_vfo=%s\n", __func__,
rig_strvfo(vfo), rig_strvfo(rig->state.current_vfo));
if (vfo == RIG_VFO_CURR)
{
// if CURR then update this before we figure out the real VFO
vfo = rig->state.current_vfo;
}
// if freq == 0 then we are asking to invalidate the cache
if (freq == 0) { flag = HAMLIB_ELAPSED_INVALIDATE; }
// pick a sane default
if (vfo == RIG_VFO_NONE || vfo == RIG_VFO_CURR) { vfo = RIG_VFO_A; }
if (vfo == RIG_VFO_SUB && rig->state.cache.satmode) { vfo = RIG_VFO_SUB_A; };
if (rig_need_debug(RIG_DEBUG_CACHE))
{
rig_debug(RIG_DEBUG_CACHE, "%s: set vfo=%s to freq=%.0f\n", __func__,
rig_strvfo(vfo), freq);
}
switch (vfo)
{
case RIG_VFO_ALL: // we'll use NONE to reset all VFO caches
elapsed_ms(&rig->state.cache.time_freqMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqMainC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqSubA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqSubB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqSubC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqMem, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_vfo, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_ptt, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_split, HAMLIB_ELAPSED_INVALIDATE);
break;
case RIG_VFO_A:
case RIG_VFO_MAIN:
case RIG_VFO_MAIN_A:
rig->state.cache.freqMainA = freq;
elapsed_ms(&rig->state.cache.time_freqMainA, flag);
break;
case RIG_VFO_B:
case RIG_VFO_MAIN_B:
case RIG_VFO_SUB:
rig->state.cache.freqMainB = freq;
elapsed_ms(&rig->state.cache.time_freqMainB, flag);
break;
case RIG_VFO_C:
case RIG_VFO_MAIN_C:
rig->state.cache.freqMainC = freq;
elapsed_ms(&rig->state.cache.time_freqMainC, flag);
break;
case RIG_VFO_SUB_A:
rig->state.cache.freqSubA = freq;
elapsed_ms(&rig->state.cache.time_freqSubA, flag);
break;
case RIG_VFO_SUB_B:
rig->state.cache.freqSubB = freq;
elapsed_ms(&rig->state.cache.time_freqSubB, flag);
break;
case RIG_VFO_SUB_C:
rig->state.cache.freqSubC = freq;
elapsed_ms(&rig->state.cache.time_freqSubC, flag);
break;
case RIG_VFO_MEM:
rig->state.cache.freqMem = freq;
elapsed_ms(&rig->state.cache.time_freqMem, flag);
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unknown vfo?, vfo=%s\n", __func__,
rig_strvfo(vfo));
RETURNFUNC(-RIG_EINVAL);
}
if (rig_need_debug(RIG_DEBUG_CACHE))
{
rig_cache_show(rig, __func__, __LINE__);
RETURNFUNC(RIG_OK);
}
return RIG_OK;
}
/**
* \brief get cached values for a VFO
* \param rig The rig handle
* \param vfo The VFO to get information from
* \param freq The frequency is stored here
* \param cache_ms_freq The age of the last frequency update in ms
* \param mode The mode is stored here
* \param cache_ms_mode The age of the last mode update in ms
* \param width The width is stored here
* \param cache_ms_width The age of the last width update in ms
*
* Use this to query the cache and then determine to actually fetch data from
* the rig.
*
* \note All pointers must be given. No pointer can be left at NULL
*
* \return RIG_OK if the operation has been successful, otherwise
* a negative value if an error occurred (in which case, cause is
* set appropriately).
*
*/
int rig_get_cache(RIG *rig, vfo_t vfo, freq_t *freq, int *cache_ms_freq,
rmode_t *mode, int *cache_ms_mode, pbwidth_t *width, int *cache_ms_width)
{
if (CHECK_RIG_ARG(rig) || !freq || !cache_ms_freq ||
!mode || !cache_ms_mode || !width || !cache_ms_width)
{
RETURNFUNC(-RIG_EINVAL);
}
if (rig_need_debug(RIG_DEBUG_CACHE))
{
ENTERFUNC;
}
rig_debug(RIG_DEBUG_CACHE, "%s: vfo=%s, current_vfo=%s\n", __func__,
rig_strvfo(vfo), rig_strvfo(rig->state.current_vfo));
if (vfo == RIG_VFO_CURR)
{
vfo = rig->state.current_vfo;
}
else if (vfo == RIG_VFO_OTHER)
{
switch (vfo)
{
case RIG_VFO_OTHER:
vfo = RIG_VFO_OTHER;
break;
case RIG_VFO_A:
vfo = RIG_VFO_B;
break;
case RIG_VFO_MAIN_A:
vfo = RIG_VFO_MAIN_B;
break;
case RIG_VFO_MAIN:
vfo = RIG_VFO_SUB;
break;
case RIG_VFO_B:
vfo = RIG_VFO_A;
break;
case RIG_VFO_MAIN_B:
vfo = RIG_VFO_MAIN_A;
break;
case RIG_VFO_SUB_A:
vfo = RIG_VFO_SUB_B;
break;
case RIG_VFO_SUB_B:
vfo = RIG_VFO_SUB_A;
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unknown vfo=%s\n", __func__, rig_strvfo(vfo));
}
}
// pick a sane default
if (vfo == RIG_VFO_CURR || vfo == RIG_VFO_NONE) { vfo = RIG_VFO_A; }
// If we're in satmode we map SUB to SUB_A
if (vfo == RIG_VFO_SUB && rig->state.cache.satmode) { vfo = RIG_VFO_SUB_A; };
switch (vfo)
{
case RIG_VFO_CURR:
*freq = rig->state.cache.freqCurr;
*mode = rig->state.cache.modeCurr;
*width = rig->state.cache.widthCurr;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqCurr,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeCurr,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthCurr,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_OTHER:
*freq = rig->state.cache.freqOther;
*mode = rig->state.cache.modeOther;
*width = rig->state.cache.widthOther;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqOther,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeOther,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthOther,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_A:
case RIG_VFO_MAIN:
case RIG_VFO_MAIN_A:
*freq = rig->state.cache.freqMainA;
*mode = rig->state.cache.modeMainA;
*width = rig->state.cache.widthMainA;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqMainA,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeMainA,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthMainA,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_B:
case RIG_VFO_SUB:
case RIG_VFO_MAIN_B:
*freq = rig->state.cache.freqMainB;
*mode = rig->state.cache.modeMainB;
*width = rig->state.cache.widthMainB;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqMainB,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeMainB,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthMainB,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_SUB_A:
*freq = rig->state.cache.freqSubA;
*mode = rig->state.cache.modeSubA;
*width = rig->state.cache.widthSubA;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqSubA,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeSubA,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthSubA,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_SUB_B:
*freq = rig->state.cache.freqSubB;
*mode = rig->state.cache.modeSubB;
*width = rig->state.cache.widthSubB;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqSubB,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeSubB,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthSubB,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_C:
//case RIG_VFO_MAINC: // not used by any rig yet
*freq = rig->state.cache.freqMainC;
*mode = rig->state.cache.modeMainC;
*width = rig->state.cache.widthMainC;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqMainC,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeMainC,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthMainC,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_SUB_C:
*freq = rig->state.cache.freqSubC;
*mode = rig->state.cache.modeSubC;
*width = rig->state.cache.widthSubC;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqSubC,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeSubC,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthSubC,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_MEM:
*freq = rig->state.cache.freqMem;
*mode = rig->state.cache.modeMem;
*width = rig->state.cache.widthMem;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqMem, HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeMem, HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthMem,
HAMLIB_ELAPSED_GET);
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unknown vfo?, vfo=%s\n", __func__,
rig_strvfo(vfo));
RETURNFUNC(-RIG_EINVAL);
}
rig_debug(RIG_DEBUG_CACHE, "%s: vfo=%s, freq=%.0f, mode=%s, width=%d\n", __func__, rig_strvfo(vfo),
(double)*freq, rig_strrmode(*mode), (int)*width);
if (rig_need_debug(RIG_DEBUG_CACHE))
{
RETURNFUNC(RIG_OK);
}
return RIG_OK;
}
void rig_cache_show(RIG *rig, const char *func, int line)
{
rig_debug(RIG_DEBUG_CACHE,
"%s(%d): freqMainA=%.0f, modeMainA=%s, widthMainA=%d\n", func, line,
rig->state.cache.freqMainA, rig_strrmode(rig->state.cache.modeMainA),
(int)rig->state.cache.widthMainA);
rig_debug(RIG_DEBUG_CACHE,
"%s(%d): freqMainB=%.0f, modeMainB=%s, widthMainB=%d\n", func, line,
rig->state.cache.freqMainB, rig_strrmode(rig->state.cache.modeMainB),
(int)rig->state.cache.widthMainB);
if (rig->state.vfo_list & RIG_VFO_SUB_A)
{
rig_debug(RIG_DEBUG_CACHE,
"%s(%d): freqSubA=%.0f, modeSubA=%s, widthSubA=%d\n", func, line,
rig->state.cache.freqSubA, rig_strrmode(rig->state.cache.modeSubA),
(int)rig->state.cache.widthSubA);
rig_debug(RIG_DEBUG_CACHE,
"%s(%d): freqSubB=%.0f, modeSubB=%s, widthSubB=%d\n", func, line,
rig->state.cache.freqSubB, rig_strrmode(rig->state.cache.modeSubB),
(int)rig->state.cache.widthSubB);
}
}
/*! @} */

32
src/cache.h 100644
Wyświetl plik

@ -0,0 +1,32 @@
/*
* Hamlib Interface - rig state cache routines
* Copyright (c) 2000-2012 by Stephane Fillod
* Copyright (c) 2000-2003 by Frank Singleton
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _CACHE_H
#define _CACHE_H
#include <hamlib/rig.h>
int rig_set_cache_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width);
int rig_set_cache_freq(RIG *rig, vfo_t vfo, freq_t freq);
void rig_cache_show(RIG *rig, const char *func, int line);
#endif

Wyświetl plik

@ -89,9 +89,9 @@ static const struct confparams frontend_cfg_params[] =
"0", RIG_CONF_NUMERIC, { .n = { 0.0, 1000.0, .001 } }
},
{
TOK_POLL_INTERVAL, "poll_interval", "Polling interval",
"Polling interval in millisecond for transceive emulation",
"500", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } }
TOK_POLL_INTERVAL, "poll_interval", "Rig state poll interval in milliseconds",
"Polling interval in milliseconds for transceive emulation, value of 0 disables polling",
"0", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } }
},
{
TOK_PTT_TYPE, "ptt_type", "PTT type",
@ -582,6 +582,8 @@ static int frontend_set_conf(RIG *rig, token_t token, const char *val)
case TOK_POLL_INTERVAL:
rs->poll_interval = atof(val);
// Make sure cache times out before next poll cycle
rig_set_cache_timeout_ms(rig, HAMLIB_CACHE_ALL, atol(val));
break;
case TOK_LO_FREQ:

Plik diff jest za duży Load Diff

Wyświetl plik

@ -24,9 +24,16 @@
#include <hamlib/rig.h>
int rig_poll_routine_start(RIG *rig);
int rig_poll_routine_stop(RIG *rig);
int add_trn_rig(RIG *rig);
int remove_trn_rig(RIG *rig);
int rig_fire_freq_event(RIG *rig, vfo_t vfo, freq_t freq);
int rig_fire_mode_event(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width);
int rig_fire_vfo_event(RIG *rig, vfo_t vfo);
int rig_fire_ptt_event(RIG *rig, vfo_t vfo, ptt_t ptt);
int rig_fire_dcd_event(RIG *rig, vfo_t vfo, dcd_t dcd);
int rig_fire_pltune_event(RIG *rig, vfo_t vfo, freq_t *freq, rmode_t *mode, pbwidth_t *width);
int rig_fire_spectrum_event(RIG *rig, struct rig_spectrum_line *line);
#endif /* _EVENT_H */

Wyświetl plik

@ -65,6 +65,16 @@ static void close_sync_data_pipe(hamlib_port_t *p)
close(p->fd_sync_write);
p->fd_sync_write = -1;
}
if (p->fd_sync_error_read != -1) {
close(p->fd_sync_error_read);
p->fd_sync_error_read = -1;
}
if (p->fd_sync_error_write != -1) {
close(p->fd_sync_error_write);
p->fd_sync_error_write = -1;
}
}
/**
@ -83,6 +93,8 @@ int HAMLIB_API port_open(hamlib_port_t *p)
p->fd = -1;
p->fd_sync_write = -1;
p->fd_sync_read = -1;
p->fd_sync_error_write = -1;
p->fd_sync_error_read = -1;
if (p->async)
{
@ -98,6 +110,18 @@ int HAMLIB_API port_open(hamlib_port_t *p)
p->fd_sync_read = sync_pipe_fds[0];
p->fd_sync_write = sync_pipe_fds[1];
status = pipe2(sync_pipe_fds, O_NONBLOCK);
if (status != 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: synchronous data error code pipe open status=%d, err=%s\n", __func__,
status, strerror(errno));
close_sync_data_pipe(p);
RETURNFUNC(-RIG_EINTERNAL);
}
p->fd_sync_error_read = sync_pipe_fds[0];
p->fd_sync_error_write = sync_pipe_fds[1];
rig_debug(RIG_DEBUG_VERBOSE, "%s: created synchronous data pipe\n", __func__);
}
@ -440,7 +464,7 @@ static ssize_t port_read_generic(hamlib_port_t *p, void *buf, size_t count, int
}
//! @cond Doxygen_Suppress
#define port_write_generic(p,b,c,d) write((d) ? (p)->fd : (p)->fd_sync_write,(b),(c))
#define port_write(p,b,c) write((p)->fd,(b),(c))
#define port_select(p,n,r,w,e,t) select((n),(r),(w),(e),(t))
//! @endcond
@ -475,7 +499,7 @@ static ssize_t port_read_generic(hamlib_port_t *p, void *buf, size_t count, int
* it could work very well also with any file handle, like a socket.
*/
static int write_block_generic(hamlib_port_t *p, const unsigned char *txbuffer, size_t count, int direct)
int HAMLIB_API write_block(hamlib_port_t *p, const unsigned char *txbuffer, size_t count)
{
int ret;
@ -513,7 +537,7 @@ static int write_block_generic(hamlib_port_t *p, const unsigned char *txbuffer,
for (i = 0; i < count; i++)
{
ret = port_write_generic(p, txbuffer + i, 1, direct);
ret = port_write(p, txbuffer + i, 1);
if (ret != 1)
{
@ -532,7 +556,7 @@ static int write_block_generic(hamlib_port_t *p, const unsigned char *txbuffer,
}
else
{
ret = port_write_generic(p, txbuffer, count, direct);
ret = port_write(p, txbuffer, count);
if (ret != count)
{
@ -573,44 +597,16 @@ static int write_block_generic(hamlib_port_t *p, const unsigned char *txbuffer,
return RIG_OK;
}
/**
* \brief Write a block of characters to an fd.
* \param p rig port descriptor
* \param txbuffer command sequence to be sent
* \param count number of bytes to send
* \return 0 = OK, <0 = NOK
*
* Write a block of count characters to port file descriptor,
* with a pause between each character if write_delay is > 0
*
* The write_delay is for Yaesu type rigs..require 5 character
* sequence to be sent with 50-200msec between each char.
*
* Also, post_write_delay is for some Yaesu rigs (eg: FT747) that
* get confused with sequential fast writes between cmd sequences.
*
* input:
*
* fd - file descriptor to write to
* txbuffer - pointer to a command sequence array
* count - count of byte to send from the txbuffer
* write_delay - write delay in ms between 2 chars
* post_write_delay - minimum delay between two writes
* post_write_date - timeval of last write
*
* Actually, this function has nothing specific to serial comm,
* it could work very well also with any file handle, like a socket.
*/
int HAMLIB_API write_block(hamlib_port_t *p, const unsigned char *txbuffer, size_t count)
{
return write_block_generic(p, txbuffer, count, 1);
}
int HAMLIB_API write_block_sync(hamlib_port_t *p, const unsigned char *txbuffer, size_t count)
{
return write_block_generic(p, txbuffer, count, 0);
// TODO: Macro for write()
return (int) write((p)->fd_sync_write, txbuffer, count);
}
int HAMLIB_API write_block_sync_error(hamlib_port_t *p, const unsigned char *txbuffer, size_t count)
{
// TODO: Macro for write()
return (int) write((p)->fd_sync_error_write, txbuffer, count);
}
static int read_block_generic(hamlib_port_t *p, unsigned char *rxbuffer, size_t count, int direct)
@ -757,6 +753,43 @@ int HAMLIB_API read_block_direct(hamlib_port_t *p, unsigned char *rxbuffer, size
return read_block_generic(p, rxbuffer, count, 1);
}
static int flush_and_read_last_byte(int fd)
{
fd_set rfds, efds;
ssize_t bytes_read;
struct timeval tv_timeout;
int retval;
char data;
do {
tv_timeout.tv_sec = 0;
tv_timeout.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
efds = rfds;
retval = port_select(p, fd + 1, &rfds, NULL, &efds, &tv_timeout);
if (retval < 0)
{
return -RIG_ETIMEOUT;
}
if (retval == 0)
{
return -RIG_EIO;
}
if (FD_ISSET(fd, &efds))
{
return -RIG_EIO;
}
bytes_read = read(fd, &data, 1);
} while (bytes_read > 0);
return data;
}
static int read_string_generic(hamlib_port_t *p,
unsigned char *rxbuffer,
size_t rxmax,
@ -766,10 +799,10 @@ static int read_string_generic(hamlib_port_t *p,
int direct)
{
fd_set rfds, efds;
int fd;
int fd, errorfd, maxfd;
struct timeval tv, tv_timeout, start_time, end_time, elapsed_time;
int total_count = 0;
int i=0;
int i = 0;
rig_debug(RIG_DEBUG_TRACE, "%s called, rxmax=%d\n", __func__, (int)rxmax);
@ -787,6 +820,8 @@ static int read_string_generic(hamlib_port_t *p,
}
fd = direct ? p->fd : p->fd_sync_read;
errorfd = direct ? -1 : p->fd_sync_error_read;
maxfd = (fd > errorfd) ? fd : errorfd;
/*
* Wait up to timeout ms.
@ -808,9 +843,13 @@ static int read_string_generic(hamlib_port_t *p,
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
if (!direct)
{
FD_SET(errorfd, &rfds);
}
efds = rfds;
retval = port_select(p, fd + 1, &rfds, NULL, &efds, &tv);
retval = port_select(p, maxfd + 1, &rfds, NULL, &efds, &tv);
if (retval == 0)
{
@ -820,7 +859,10 @@ static int read_string_generic(hamlib_port_t *p,
gettimeofday(&end_time, NULL);
timersub(&end_time, &start_time, &elapsed_time);
dump_hex((unsigned char *) rxbuffer, total_count);
if (direct)
{
dump_hex((unsigned char *) rxbuffer, total_count);
}
if (!flush_flag) {
rig_debug(RIG_DEBUG_WARN,
"%s(): Timed out %d.%03d seconds after %d chars\n",
@ -838,7 +880,10 @@ static int read_string_generic(hamlib_port_t *p,
if (retval < 0)
{
dump_hex(rxbuffer, total_count);
if (direct)
{
dump_hex(rxbuffer, total_count);
}
rig_debug(RIG_DEBUG_ERR,
"%s(): select() error after %d chars: %s\n",
__func__,
@ -857,9 +902,25 @@ static int read_string_generic(hamlib_port_t *p,
return -RIG_EIO;
}
if (!direct)
{
if (FD_ISSET(errorfd, &efds))
{
rig_debug(RIG_DEBUG_ERR,
"%s(): fd error from sync error pipe after %d chars\n",
__func__,
total_count);
return -RIG_EIO;
}
if (FD_ISSET(errorfd, &rfds))
{
return flush_and_read_last_byte(errorfd);
}
}
/*
* read 1 character from the rig, (check if in stop set)
* read 1 character from the rig, (check if in stop set)
* The file descriptor must have been set up non blocking.
*/
do
@ -875,7 +936,10 @@ static int read_string_generic(hamlib_port_t *p,
/* if we get 0 bytes or an error something is wrong */
if (rd_count <= 0)
{
dump_hex((unsigned char *) rxbuffer, total_count);
if (direct)
{
dump_hex((unsigned char *) rxbuffer, total_count);
}
rig_debug(RIG_DEBUG_ERR,
"%s(): read() failed - %s\n",
__func__,
@ -901,10 +965,13 @@ static int read_string_generic(hamlib_port_t *p,
*/
rxbuffer[total_count] = '\000';
rig_debug(RIG_DEBUG_TRACE,
"%s(): RX %d characters\n",
__func__,
total_count);
if (direct)
{
rig_debug(RIG_DEBUG_TRACE,
"%s(): RX %d characters\n",
__func__,
total_count);
}
dump_hex((unsigned char *) rxbuffer, total_count);

Wyświetl plik

@ -45,6 +45,10 @@ extern HAMLIB_EXPORT(int) write_block_sync(hamlib_port_t *p,
const unsigned char *txbuffer,
size_t count);
extern HAMLIB_EXPORT(int) write_block_sync_error(hamlib_port_t *p,
const unsigned char *txbuffer,
size_t count);
extern HAMLIB_EXPORT(int) read_string(hamlib_port_t *p,
unsigned char *rxbuffer,
size_t rxmax,

Wyświetl plik

@ -288,6 +288,33 @@ unsigned long long HAMLIB_API from_bcd_be(const unsigned char bcd_data[],
return f;
}
size_t HAMLIB_API to_hex(size_t source_length, const unsigned char *source_data, size_t dest_length, char *dest_data)
{
size_t i;
size_t length = source_length;
const unsigned char *source = source_data;
char *dest = dest_data;
if (source_length == 0 || dest_length == 0)
{
return 0;
}
if (source_length * 2 > dest_length)
{
length = dest_length / 2 - 1;
}
for (i = 0; i < length; i++)
{
sprintf(dest, "%02X", source[0]);
source++;
dest += 2;
}
return length;
}
/**
* \brief Convert duration of one morse code dot (element) to milliseconds at the given speed.
* \param wpm morse code speed in words per minute
@ -2288,6 +2315,15 @@ void *HAMLIB_API rig_get_function_ptr(rig_model_t rig_model,
case RIG_FUNCTION_SET_VFO_OPT:
return caps->set_vfo_opt;
case RIG_FUNCTION_READ_FRAME_DIRECT:
return caps->read_frame_direct;
case RIG_FUNCTION_IS_ASYNC_FRAME:
return caps->is_async_frame;
case RIG_FUNCTION_PROCESS_ASYNC_FRAME:
return caps->process_async_frame;
default:
rig_debug(RIG_DEBUG_ERR, "Unknown function?? function=%d\n", rig_function);
}

Wyświetl plik

@ -31,8 +31,8 @@
* thus not ensure mutual exclusion.
* Fix it when making Hamlib reentrant! --SF
*/
#define Hold_Decode(rig) {(rig)->state.hold_decode = 1;}
#define Unhold_Decode(rig) {(rig)->state.hold_decode = 0;}
#define set_transaction_active(rig) {(rig)->state.transaction_active = 1;}
#define set_transaction_inactive(rig) {(rig)->state.transaction_active = 0;}
__BEGIN_DECLS
@ -72,6 +72,11 @@ extern HAMLIB_EXPORT(unsigned long long) from_bcd_be(const unsigned char
bcd_data[],
unsigned bcd_len);
extern HAMLIB_EXPORT(size_t) to_hex(size_t source_length,
const unsigned char *source_data,
size_t dest_length,
char *dest_data);
extern HAMLIB_EXPORT(double) morse_code_dot_to_millis(int wpm);
extern HAMLIB_EXPORT(int) dot10ths_to_millis(int dot10ths, int wpm);
extern HAMLIB_EXPORT(int) millis_to_dot10ths(int millis, int wpm);

Wyświetl plik

@ -78,6 +78,7 @@
#include <hamlib/rig.h>
#include "network.h"
#include "misc.h"
#include "snapshot_data.h"
#ifdef __MINGW32__
@ -88,6 +89,36 @@ static int wsstarted;
#define NET_BUFFER_SIZE 8192
//! @endcond
#define MULTICAST_PUBLISHER_DATA_PACKET_TYPE_POLL 0x01
#define MULTICAST_PUBLISHER_DATA_PACKET_TYPE_TRANSCEIVE 0x02
#define MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM 0x03
#pragma pack(push,1)
typedef struct multicast_publisher_data_packet_s
{
uint8_t type;
uint8_t padding;
uint16_t data_length;
} __attribute__((packed)) multicast_publisher_data_packet;
#pragma pack(pop)
typedef struct multicast_publisher_args_s
{
RIG *rig;
int socket_fd;
const char *multicast_addr;
int multicast_port;
int data_write_fd;
int data_read_fd;
} multicast_publisher_args;
typedef struct multicast_publisher_priv_data_s
{
pthread_t thread_id;
multicast_publisher_args args;
} multicast_publisher_priv_data;
static void handle_error(enum rig_debug_level_e lvl, const char *msg)
{
int e;
@ -405,148 +436,328 @@ int network_close(hamlib_port_t *rp)
}
//! @endcond
volatile int multicast_server_run = 1;
pthread_t multicast_server_threadId;
extern void sync_callback(int lock);
struct multicast_server_args_s
{
RIG *rig;
} multicast_server_args;
#ifdef HAVE_PTHREAD
//! @cond Doxygen_Suppress
// our multicast server loop
void *multicast_server(void *arg)
static int multicast_publisher_write_data(int fd, size_t length, const unsigned char *data)
{
struct multicast_server_args_s *args = (struct multicast_server_args_s *)arg;
RIG *rig = args->rig;
rig_debug(RIG_DEBUG_TRACE, "%s(%d): Starting multicast server\n", __FILE__,
__LINE__);
// we can and should use a really small cache time while we are polling
// rig_set_cache_timeout_ms(rig, HAMLIB_CACHE_ALL, 100);
ssize_t result;
// this is really verbose since it runs very quickly
// so we only spit out WARN unless CACHE has been selected
//if (!rig_need_debug(RIG_DEBUG_CACHE)) { rig_set_debug(RIG_DEBUG_WARN); }
freq_t freqMain = 0, freqSub = 0, freqMainLast = 0, freqSubLast = 0;
rmode_t modeMain = RIG_MODE_NONE, modeSub = RIG_MODE_NONE,
modeMainLast = RIG_MODE_NONE, modeSubLast = RIG_MODE_NONE;
pbwidth_t widthMain = 0, widthSub = 0, widthMainLast = 0, widthSubLast = 0;
split_t split, splitLast = -1;
do
result = write(fd, data, length);
if (result < 0)
{
int retval;
int updateOccurred;
updateOccurred = 0;
if (rig->caps->get_freq)
{
retval = rig_get_freq(rig, RIG_VFO_A, &freqMain);
if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s(%d): rig_get_freqA error %s\n", __FILE__, __LINE__, rigerror(retval)); }
retval = rig_get_freq(rig, RIG_VFO_B, &freqSub);
if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s(%d): rig_get_freqB error %s\n", __FILE__, __LINE__, rigerror(retval)); }
if (freqMain != freqMainLast || freqSub != freqSubLast)
{
rig_debug(RIG_DEBUG_WARN,
"%s(%d) freqMain=%.0f was %.0f, freqSub=%.0f was %.0f\n", __FILE__, __LINE__,
freqMain, freqMainLast, freqSub, freqSubLast);
updateOccurred = 1;
freqMainLast = freqMain;
freqSubLast = freqSub;
}
}
if (rig->caps->get_mode)
{
retval = rig_get_mode(rig, RIG_VFO_A, &modeMain, &widthMain);
if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s(%d): rig_get_modeA error %s\n", __FILE__, __LINE__, rigerror(retval)); }
retval = rig_get_mode(rig, RIG_VFO_B, &modeSub, &widthSub);
if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s(%d): rig_get_modeB error %s\n", __FILE__, __LINE__, rigerror(retval)); }
if (modeMain != modeMainLast || modeSub != modeSubLast)
{
rig_debug(RIG_DEBUG_TRACE, "%s(%d) modeMain=%s was %s, modeSub=%s was %s\n",
__FILE__, __LINE__, rig_strrmode(modeMain), rig_strrmode(modeMainLast),
rig_strrmode(modeSub), rig_strrmode(modeSubLast));
updateOccurred = 1;
modeMainLast = modeMain;
modeSubLast = modeSub;
}
if (widthMain != widthMainLast || widthSub != widthSubLast)
{
rig_debug(RIG_DEBUG_WARN,
"%s(%d) widthMain=%ld was %ld, widthSub=%ld was %ld\n", __FILE__, __LINE__,
widthMain, widthMainLast, widthSub, widthSubLast);
updateOccurred = 1;
widthMainLast = widthMain;
widthSubLast = widthSub;
}
}
if (rig->caps->get_split_vfo)
{
vfo_t tx_vfo;
retval = rig_get_split_vfo(rig, RIG_VFO_A, &split, &tx_vfo);
if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s(%d): rig_get_modeA error %s\n", __FILE__, __LINE__, rigerror(retval)); }
if (split != splitLast)
{
rig_debug(RIG_DEBUG_WARN, "%s(%d) split=%d was %d\n", __FILE__, __LINE__, split,
splitLast);
updateOccurred = 1;
splitLast = split;
}
}
if (updateOccurred)
{
rig_debug(RIG_DEBUG_WARN, "%s(%d): update occurred...time to send multicast\n",
__FILE__, __LINE__);
}
hl_usleep(100 * 1000);
rig_debug(RIG_DEBUG_ERR, "%s: error writing to multicast publisher data pipe, status=%ld, err=%s\n", __func__,
result, strerror(errno));
RETURNFUNC(-RIG_EIO);
}
while (multicast_server_run);
rig_debug(RIG_DEBUG_TRACE, "%s(%d): Stopping multicast server\n", __FILE__,
if (result != length)
{
rig_debug(RIG_DEBUG_ERR, "%s: could not write to multicast publisher data pipe, expected %ld bytes, wrote %ld bytes\n",
__func__, length, result);
RETURNFUNC(-RIG_EIO);
}
RETURNFUNC(RIG_OK);
}
static int multicast_publisher_read_data(int fd, size_t length, unsigned char *data)
{
fd_set rfds, efds;
struct timeval timeout;
ssize_t result;
int retval;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
efds = rfds;
retval = select(fd + 1, &rfds, NULL, &efds, &timeout);
if (retval == 0)
{
RETURNFUNC(-RIG_ETIMEOUT);
}
if (retval < 0)
{
rig_debug(RIG_DEBUG_ERR,
"%s(): select() failed when reading multicast publisher data: %s\n",
__func__,
strerror(errno));
return -RIG_EIO;
}
if (FD_ISSET(fd, &efds))
{
rig_debug(RIG_DEBUG_ERR, "%s(): fd error when reading multicast publisher data\n", __func__);
return -RIG_EIO;
}
result = read(fd, data, length);
if (result < 0)
{
if (errno == EAGAIN)
{
RETURNFUNC(-RIG_ETIMEOUT);
}
rig_debug(RIG_DEBUG_ERR, "%s: error reading multicast publisher data: %s\n", __func__, strerror(errno));
RETURNFUNC(-RIG_EIO);
}
if (result != length)
{
rig_debug(RIG_DEBUG_ERR, "%s: could not read from multicast publisher data pipe, expected %ld bytes, read %ld bytes\n",
__func__, length, result);
RETURNFUNC(-RIG_EIO);
}
RETURNFUNC(RIG_OK);
}
static int multicast_publisher_write_packet_header(RIG *rig, multicast_publisher_data_packet *packet)
{
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *mcast_publisher_priv;
ssize_t result;
if (rs->multicast_publisher_priv_data == NULL)
{
// Silently ignore if multicast publisher is not enabled
RETURNFUNC(RIG_OK);
}
mcast_publisher_priv = (multicast_publisher_priv_data *) rs->multicast_publisher_priv_data;
result = multicast_publisher_write_data(
mcast_publisher_priv->args.data_write_fd, sizeof(multicast_publisher_data_packet), (unsigned char *) packet);
if (result != RIG_OK)
{
RETURNFUNC(result);
}
RETURNFUNC(RIG_OK);
}
int network_publish_rig_poll_data(RIG *rig)
{
multicast_publisher_data_packet packet = {
.type = MULTICAST_PUBLISHER_DATA_PACKET_TYPE_POLL,
.padding = 0,
.data_length = 0,
};
return multicast_publisher_write_packet_header(rig, &packet);
}
int network_publish_rig_transceive_data(RIG *rig)
{
multicast_publisher_data_packet packet = {
.type = MULTICAST_PUBLISHER_DATA_PACKET_TYPE_TRANSCEIVE,
.padding = 0,
.data_length = 0,
};
return multicast_publisher_write_packet_header(rig, &packet);
}
int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line)
{
int result;
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *mcast_publisher_priv;
multicast_publisher_data_packet packet = {
.type = MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM,
.padding = 0,
.data_length = sizeof(struct rig_spectrum_line) + line->spectrum_data_length,
};
if (rs->multicast_publisher_priv_data == NULL)
{
// Silently ignore if multicast publisher is not enabled
RETURNFUNC(RIG_OK);
}
result = multicast_publisher_write_packet_header(rig, &packet);
if (result != RIG_OK)
{
RETURNFUNC(result);
}
mcast_publisher_priv = (multicast_publisher_priv_data *) rs->multicast_publisher_priv_data;
result = multicast_publisher_write_data(
mcast_publisher_priv->args.data_write_fd, sizeof(struct rig_spectrum_line), (unsigned char *) line);
if (result != RIG_OK)
{
RETURNFUNC(result);
}
result = multicast_publisher_write_data(
mcast_publisher_priv->args.data_write_fd, line->spectrum_data_length, line->spectrum_data);
if (result != RIG_OK)
{
RETURNFUNC(result);
}
RETURNFUNC(RIG_OK);
}
static int multicast_publisher_read_packet(int fd, uint8_t *type, struct rig_spectrum_line *spectrum_line, unsigned char *spectrum_data)
{
int result;
multicast_publisher_data_packet packet;
result = multicast_publisher_read_data(fd, sizeof(packet), (unsigned char *) &packet);
if (result < 0)
{
RETURNFUNC(result);
}
switch (packet.type)
{
case MULTICAST_PUBLISHER_DATA_PACKET_TYPE_POLL:
case MULTICAST_PUBLISHER_DATA_PACKET_TYPE_TRANSCEIVE:
break;
case MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM:
result = multicast_publisher_read_data(
fd, sizeof(struct rig_spectrum_line), (unsigned char *) spectrum_line);
if (result < 0)
{
RETURNFUNC(result);
}
if (packet.data_length - sizeof(struct rig_spectrum_line) != spectrum_line->spectrum_data_length)
{
rig_debug(RIG_DEBUG_ERR, "%s: multicast publisher data error, expected %ld bytes of spectrum data, got %ld bytes\n",
__func__, spectrum_line->spectrum_data_length, packet.data_length - sizeof(struct rig_spectrum_line));
RETURNFUNC(-RIG_EPROTO);
}
spectrum_line->spectrum_data = spectrum_data;
result = multicast_publisher_read_data(fd, spectrum_line->spectrum_data_length, spectrum_data);
if (result < 0)
{
RETURNFUNC(result);
}
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unexpected multicast publisher data packet type: %d\n", __func__, packet.type);
RETURNFUNC(-RIG_EPROTO);
}
*type = packet.type;
RETURNFUNC(RIG_OK);
}
void *multicast_publisher(void *arg)
{
struct multicast_publisher_args_s *args = (struct multicast_publisher_args_s *)arg;
RIG *rig = args->rig;
struct rig_state *rs = &rig->state;
struct rig_spectrum_line spectrum_line;
unsigned char spectrum_data[2048];
char snapshot_buffer[16 * 1024];
uint8_t packet_type;
struct sockaddr_in dest_addr;
int socket_fd = args->socket_fd;
int result;
ssize_t send_result;
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast publisher\n", __FILE__, __LINE__);
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr);
dest_addr.sin_port = htons(args->multicast_port);
while (rs->multicast_publisher_run)
{
result = multicast_publisher_read_packet(args->data_read_fd, &packet_type, &spectrum_line, spectrum_data);
if (result != RIG_OK)
{
if (result == -RIG_ETIMEOUT)
{
continue;
}
// TODO: how to detect closing of pipe, indicate with error code
// TODO: error handling, flush pipe in case of error?
hl_usleep(500 * 1000);
continue;
}
result = snapshot_serialize(sizeof(snapshot_buffer), snapshot_buffer, rig,
packet_type == MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM ? &spectrum_line : NULL);
if (result != RIG_OK)
{
rig_debug(RIG_DEBUG_ERR, "%s: error serializing rig snapshot data, result=%d\n", __func__, result);
continue;
}
rig_debug(RIG_DEBUG_TRACE, "%s: sending rig snapshot data: %s\n", __func__, snapshot_buffer);
send_result = sendto(
socket_fd,
snapshot_buffer,
strlen(snapshot_buffer),
0,
(struct sockaddr *) &dest_addr,
sizeof(dest_addr)
);
if (send_result < 0) {
rig_debug(RIG_DEBUG_ERR, "%s: error sending UDP packet: %s\n", __func__, strerror(errno));
}
}
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping multicast publisher\n", __FILE__,
__LINE__);
return NULL;
}
static void multicast_publisher_close_data_pipe(multicast_publisher_priv_data *mcast_publisher_priv)
{
if (mcast_publisher_priv->args.data_read_fd != -1) {
close(mcast_publisher_priv->args.data_read_fd);
mcast_publisher_priv->args.data_read_fd = -1;
}
if (mcast_publisher_priv->args.data_write_fd != -1) {
close(mcast_publisher_priv->args.data_write_fd);
mcast_publisher_priv->args.data_write_fd = -1;
}
}
//! @endcond
/**
* \brief Open multicast server using rig.state data
* \brief Start multicast publisher
*
* Open Open multicast server using rig.state data.
* NB: The signal PIPE will be ignored for the whole application.
* Start multicast publisher.
*
* \param rp Port data structure (must spec port id eg hostname:port -- hostname defaults to 224.0.1.1)
* \param default_port Default network socket port
* \param multicast_addr UDP address
* \param multicast_port UDP socket port
* \return RIG_OK or < 0 if error
*/
int network_multicast_server(RIG *rig, const char *multicast_addr,
int default_port, enum multicast_item_e items)
int network_multicast_publisher_start(RIG *rig, const char *multicast_addr,
int multicast_port, enum multicast_item_e items)
{
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *mcast_publisher_priv;
int socket_fd;
int data_pipe_fds[2];
int status;
//ENTERFUNC;
rig_debug(RIG_DEBUG_VERBOSE,
"%s(%d):network_multicast_server under development\n", __FILE__, __LINE__);
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d):ADDR=%s, port=%d\n", __FILE__, __LINE__,
multicast_addr, default_port);
ENTERFUNC;
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d):address=%s, port=%d\n", __FILE__, __LINE__,
multicast_addr, multicast_port);
if (strcmp(multicast_addr, "0.0.0.0") == 0)
{
@ -555,45 +766,134 @@ int network_multicast_server(RIG *rig, const char *multicast_addr,
return RIG_OK; // don't start it
}
if (multicast_server_threadId != 0)
if (rs->multicast_publisher_priv_data != NULL)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d): multicast_server already running\n", __FILE__,
rig_debug(RIG_DEBUG_ERR, "%s(%d): multicast publisher already running\n", __FILE__,
__LINE__);
RETURNFUNC(-RIG_EINVAL);
}
status = network_init();
if (status != RIG_OK) { RETURNFUNC(status); }
if (status != RIG_OK)
{
RETURNFUNC(status);
}
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_fd < 0) {
rig_debug(RIG_DEBUG_ERR, "%s: error opening new UDP socket: %s", __func__, strerror(errno));
RETURNFUNC(-RIG_EIO);
}
if (items & RIG_MULTICAST_TRANSCEIVE)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_TRANSCEIVE enabled\n", __FILE__,
__LINE__);
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_TRANSCEIVE enabled\n", __FILE__, __LINE__);
}
if (items & RIG_MULTICAST_SPECTRUM)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_SPECTRUM enabled\n", __FILE__,
__LINE__);
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_SPECTRUM enabled\n", __FILE__, __LINE__);
}
else
{
rig_debug(RIG_DEBUG_ERR, "%s(%d) unknown MULTICAST item requested=0x%x\n",
__FILE__, __LINE__, items);
__FILE__, __LINE__, items);
}
multicast_server_args.rig = rig;
int err = pthread_create(&multicast_server_threadId, NULL, multicast_server,
&multicast_server_args);
rs->snapshot_packet_sequence_number = 0;
rs->multicast_publisher_run = 1;
rs->multicast_publisher_priv_data = calloc(1, sizeof(multicast_publisher_priv_data));
if (rs->multicast_publisher_priv_data == NULL)
{
close(socket_fd);
RETURNFUNC(-RIG_ENOMEM);
}
status = pipe2(data_pipe_fds, O_NONBLOCK);
if (status != 0)
{
free(rs->multicast_publisher_priv_data);
rs->multicast_publisher_priv_data = NULL;
close(socket_fd);
rig_debug(RIG_DEBUG_ERR, "%s: multicast publisher data pipe open status=%d, err=%s\n", __func__,
status, strerror(errno));
RETURNFUNC(-RIG_EINTERNAL);
}
mcast_publisher_priv = (multicast_publisher_priv_data *) rs->multicast_publisher_priv_data;
mcast_publisher_priv->args.socket_fd = socket_fd;
mcast_publisher_priv->args.multicast_addr = multicast_addr;
mcast_publisher_priv->args.multicast_port = multicast_port;
mcast_publisher_priv->args.rig = rig;
mcast_publisher_priv->args.data_read_fd = data_pipe_fds[0];
mcast_publisher_priv->args.data_write_fd = data_pipe_fds[1];
int err = pthread_create(&mcast_publisher_priv->thread_id, NULL, multicast_publisher,
&mcast_publisher_priv->args);
if (err)
{
multicast_publisher_close_data_pipe(mcast_publisher_priv);
free(mcast_publisher_priv);
rs->multicast_publisher_priv_data = NULL;
close(socket_fd);
rig_debug(RIG_DEBUG_ERR, "%s(%d) pthread_create error %s\n", __FILE__, __LINE__,
strerror(errno));
return -RIG_EINTERNAL;
RETURNFUNC(-RIG_EINTERNAL);
}
RETURNFUNC(RIG_OK);
}
/**
* \brief Stop multicast publisher
*
* Stop multicast publisher
*
* \return RIG_OK or < 0 if error
*/
int network_multicast_publisher_stop(RIG *rig)
{
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *mcast_publisher_priv;
ENTERFUNC;
rs->multicast_publisher_run = 0;
mcast_publisher_priv = (multicast_publisher_priv_data *) rs->multicast_publisher_priv_data;
if (mcast_publisher_priv == NULL)
{
RETURNFUNC(RIG_OK);
}
if (mcast_publisher_priv->thread_id != 0)
{
int err = pthread_join(mcast_publisher_priv->thread_id, NULL);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error %s\n", __FILE__, __LINE__,
strerror(errno));
// just ignore it
}
mcast_publisher_priv->thread_id = 0;
}
multicast_publisher_close_data_pipe(mcast_publisher_priv);
if (mcast_publisher_priv->args.socket_fd >= 0)
{
close(mcast_publisher_priv->args.socket_fd);
mcast_publisher_priv->args.socket_fd = -1;
}
free(rs->multicast_publisher_priv_data);
rs->multicast_publisher_priv_data = NULL;
RETURNFUNC(RIG_OK);
}
#endif
/** @} */

Wyświetl plik

@ -29,9 +29,13 @@ __BEGIN_DECLS
/* Hamlib internal use, see rig.c */
int network_open(hamlib_port_t *p, int default_port);
HAMLIB_EXPORT(int) network_multicast_server(RIG *rig, const char *multicast_addr, int default_port, enum multicast_item_e items);
int network_close(hamlib_port_t *rp);
void network_flush(hamlib_port_t *rp);
int network_publish_rig_poll_data(RIG *rig);
int network_publish_rig_transceive_data(RIG *rig);
int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line);
HAMLIB_EXPORT(int) network_multicast_publisher_start(RIG *rig, const char *multicast_addr, int multicast_port, enum multicast_item_e items);
HAMLIB_EXPORT(int) network_multicast_publisher_stop(RIG *rig);
__END_DECLS

678
src/rig.c
Wyświetl plik

@ -77,6 +77,7 @@
#include "misc.h"
#include "sprintflst.h"
#include "hamlibdatetime.h"
#include "cache.h"
/**
* \brief Hamlib release number
@ -196,7 +197,8 @@ static const char *const rigerror_table[] =
"Communication bus collision",
"NULL RIG handle or invalid pointer parameter",
"Invalid VFO",
"Argument out of domain of func"
"Argument out of domain of func",
"Function deprecated"
};
@ -228,14 +230,19 @@ void rig_unlock() {};
#endif
#ifdef HAVE_PTHREAD
volatile int async_data_handler_thread_run = 0;
pthread_t async_data_handler_thread_id;
struct async_data_handler_args_s
typedef struct async_data_handler_args_s
{
RIG *rig;
} async_data_handler_args;
typedef struct async_data_handler_priv_data_s
{
pthread_t thread_id;
async_data_handler_args args;
} async_data_handler_priv_data;
static int async_data_handler_start(RIG *rig);
static int async_data_handler_stop(RIG *rig);
void *async_data_handler(void *arg);
#endif
@ -396,30 +403,6 @@ static int rig_check_rig_caps()
return rc;
}
static void cache_show(RIG *rig, const char *func, int line)
{
rig_debug(RIG_DEBUG_CACHE,
"%s(%d): freqMainA=%.0f, modeMainA=%s, widthMainA=%d\n", func, line,
rig->state.cache.freqMainA, rig_strrmode(rig->state.cache.modeMainA),
(int)rig->state.cache.widthMainA);
rig_debug(RIG_DEBUG_CACHE,
"%s(%d): freqMainB=%.0f, modeMainB=%s, widthMainB=%d\n", func, line,
rig->state.cache.freqMainB, rig_strrmode(rig->state.cache.modeMainB),
(int)rig->state.cache.widthMainB);
if (rig->state.vfo_list & RIG_VFO_SUB_A)
{
rig_debug(RIG_DEBUG_CACHE,
"%s(%d): freqSubA=%.0f, modeSubA=%s, widthSubA=%d\n", func, line,
rig->state.cache.freqSubA, rig_strrmode(rig->state.cache.modeSubA),
(int)rig->state.cache.widthSubA);
rig_debug(RIG_DEBUG_CACHE,
"%s(%d): freqSubB=%.0f, modeSubB=%s, widthSubB=%d\n", func, line,
rig->state.cache.freqSubB, rig_strrmode(rig->state.cache.modeSubB),
(int)rig->state.cache.widthSubB);
}
}
/**
* \brief allocate a new RIG handle
* \param rig_model The rig model for this new handle
@ -479,9 +462,15 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model)
rs->pttport.fd = -1;
rs->comm_state = 0;
rs->rigport.type.rig = caps->port_type; /* default from caps */
#ifdef HAVE_PTHREAD
rs->rigport.async = caps->async_data_supported;
#else
rs->rigport.async = 0;
#endif
rs->rigport.fd_sync_write = -1;
rs->rigport.fd_sync_read = -1;
rs->rigport.fd_sync_error_write = -1;
rs->rigport.fd_sync_error_read = -1;
switch (caps->port_type)
{
@ -530,8 +519,12 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model)
rs->vfo_comp = 0.0; /* override it with preferences */
rs->current_vfo = RIG_VFO_CURR; /* we don't know yet! */
rs->tx_vfo = RIG_VFO_CURR; /* we don't know yet! */
rs->transceive = RIG_TRN_OFF;
rs->poll_interval = 500;
#ifdef HAVE_PTHREAD
rs->async_data = caps->async_data_supported;
#else
rs->async_data = 0;
#endif
rs->poll_interval = 0; // disable polling by default
rs->lo_freq = 0;
rs->cache.timeout_ms = 500; // 500ms cache timeout by default
@ -1054,20 +1047,11 @@ int HAMLIB_API rig_open(RIG *rig)
RETURNFUNC(status);
}
if (caps->async_data_supported)
status = async_data_handler_start(rig);
if (status < 0)
{
async_data_handler_thread_run = 1;
async_data_handler_args.rig = rig;
int err = pthread_create(&async_data_handler_thread_id, NULL,
async_data_handler, &async_data_handler_args);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d) pthread_create error: %s\n", __FILE__, __LINE__,
strerror(errno));
port_close(&rs->rigport, rs->rigport.type.rig);
return -RIG_EINTERNAL;
}
port_close(&rs->rigport, rs->rigport.type.rig);
RETURNFUNC(status);
}
add_opened_rig(rig);
@ -1084,7 +1068,7 @@ int HAMLIB_API rig_open(RIG *rig)
if (status != RIG_OK)
{
// TODO: stop async reader
async_data_handler_stop(rig);
port_close(&rs->rigport, rs->rigport.type.rig);
RETURNFUNC(status);
}
@ -1142,23 +1126,6 @@ int HAMLIB_API rig_open(RIG *rig)
rig_set_parm(rig, RIG_PARM_SCREENSAVER, parm_value);
}
#if 0
/*
* Check the current tranceive state of the rig
*/
if (rs->transceive == RIG_TRN_RIG)
{
int retval, trn;
retval = rig_get_trn(rig, &trn);
if (retval == RIG_OK && trn == RIG_TRN_RIG)
{
add_trn_rig(rig);
}
}
#endif
// read frequency to update internal status
// freq_t freq;
// if (caps->get_freq) rig_get_freq(rig, RIG_VFO_A, &freq);
@ -1188,44 +1155,6 @@ int HAMLIB_API rig_close(RIG *rig)
ENTERFUNC;
// terminate the multicast server
extern int multicast_server_run;
multicast_server_run = 0;
#ifdef HAVE_PTHREAD
extern pthread_t multicast_server_threadId;
if (multicast_server_threadId != 0)
{
int err = pthread_join(multicast_server_threadId, NULL);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error %s\n", __FILE__, __LINE__,
strerror(errno));
// just ignore it
}
multicast_server_threadId = 0;
}
#endif
async_data_handler_thread_run = 0;
if (async_data_handler_thread_id != 0)
{
int err = pthread_join(async_data_handler_thread_id, NULL);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error: %s\n", __FILE__, __LINE__,
strerror(errno));
// just ignore the error
}
async_data_handler_thread_id = 0;
}
if (!rig || !rig->caps)
{
RETURNFUNC(-RIG_EINVAL);
@ -1239,12 +1168,6 @@ int HAMLIB_API rig_close(RIG *rig)
RETURNFUNC(-RIG_EINVAL);
}
if (rs->transceive != RIG_TRN_OFF)
{
TRACE;
rig_set_trn(rig, RIG_TRN_OFF);
}
/*
* Let the backend say 73s to the rig.
* and ignore the return code.
@ -1254,6 +1177,8 @@ int HAMLIB_API rig_close(RIG *rig)
caps->rig_close(rig);
}
async_data_handler_stop(rig);
/*
* FIXME: what happens if PTT and rig ports are the same?
* (eg. ptt_type = RIG_PTT_SERIAL)
@ -1483,393 +1408,6 @@ int HAMLIB_API rig_get_twiddle(RIG *rig, int *seconds)
RETURNFUNC(RIG_OK);
}
static int set_cache_mode(RIG *rig, vfo_t vfo, mode_t mode, pbwidth_t width)
{
ENTERFUNC;
cache_show(rig, __func__, __LINE__);
if (vfo == RIG_VFO_CURR)
{
// if CURR then update this before we figure out the real VFO
vfo = rig->state.current_vfo;
}
// pick a sane default
if (vfo == RIG_VFO_NONE || vfo == RIG_VFO_CURR) { vfo = RIG_VFO_A; }
if (vfo == RIG_VFO_SUB && rig->state.cache.satmode) { vfo = RIG_VFO_SUB_A; };
switch (vfo)
{
case RIG_VFO_ALL: // we'll use NONE to reset all VFO caches
elapsed_ms(&rig->state.cache.time_modeMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainC, HAMLIB_ELAPSED_INVALIDATE);
break;
case RIG_VFO_A:
case RIG_VFO_MAIN:
case RIG_VFO_MAIN_A:
rig->state.cache.modeMainA = mode;
if (width > 0) { rig->state.cache.widthMainA = width; }
elapsed_ms(&rig->state.cache.time_modeMainA, HAMLIB_ELAPSED_SET);
elapsed_ms(&rig->state.cache.time_widthMainA, HAMLIB_ELAPSED_SET);
break;
case RIG_VFO_B:
case RIG_VFO_SUB:
case RIG_VFO_MAIN_B:
rig->state.cache.modeMainB = mode;
if (width > 0) { rig->state.cache.widthMainB = width; }
elapsed_ms(&rig->state.cache.time_modeMainB, HAMLIB_ELAPSED_SET);
elapsed_ms(&rig->state.cache.time_widthMainB, HAMLIB_ELAPSED_SET);
break;
case RIG_VFO_C:
case RIG_VFO_MAIN_C:
rig->state.cache.modeMainC = mode;
if (width > 0) { rig->state.cache.widthMainC = width; }
elapsed_ms(&rig->state.cache.time_modeMainC, HAMLIB_ELAPSED_SET);
elapsed_ms(&rig->state.cache.time_widthMainC, HAMLIB_ELAPSED_SET);
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unknown vfo=%s\n", __func__, rig_strvfo(vfo));
RETURNFUNC(-RIG_EINTERNAL);
}
cache_show(rig, __func__, __LINE__);
RETURNFUNC(RIG_OK);
}
static int set_cache_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
int flag = HAMLIB_ELAPSED_SET;
if (rig_need_debug(RIG_DEBUG_CACHE))
{
ENTERFUNC;
cache_show(rig, __func__, __LINE__);
}
rig_debug(RIG_DEBUG_CACHE, "%s: vfo=%s, current_vfo=%s\n", __func__,
rig_strvfo(vfo), rig_strvfo(rig->state.current_vfo));
if (vfo == RIG_VFO_CURR)
{
// if CURR then update this before we figure out the real VFO
vfo = rig->state.current_vfo;
}
// if freq == 0 then we are asking to invalidate the cache
if (freq == 0) { flag = HAMLIB_ELAPSED_INVALIDATE; }
// pick a sane default
if (vfo == RIG_VFO_NONE || vfo == RIG_VFO_CURR) { vfo = RIG_VFO_A; }
if (vfo == RIG_VFO_SUB && rig->state.cache.satmode) { vfo = RIG_VFO_SUB_A; };
if (rig_need_debug(RIG_DEBUG_CACHE))
{
rig_debug(RIG_DEBUG_CACHE, "%s: set vfo=%s to freq=%.0f\n", __func__,
rig_strvfo(vfo), freq);
}
switch (vfo)
{
case RIG_VFO_ALL: // we'll use NONE to reset all VFO caches
elapsed_ms(&rig->state.cache.time_freqMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqMainC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqSubA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqSubB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqSubC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_freqMem, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_vfo, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_modeMainC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainA, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainB, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_widthMainC, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_ptt, HAMLIB_ELAPSED_INVALIDATE);
elapsed_ms(&rig->state.cache.time_split, HAMLIB_ELAPSED_INVALIDATE);
break;
case RIG_VFO_A:
case RIG_VFO_MAIN:
case RIG_VFO_MAIN_A:
rig->state.cache.freqMainA = freq;
elapsed_ms(&rig->state.cache.time_freqMainA, flag);
break;
case RIG_VFO_B:
case RIG_VFO_MAIN_B:
case RIG_VFO_SUB:
rig->state.cache.freqMainB = freq;
elapsed_ms(&rig->state.cache.time_freqMainB, flag);
break;
case RIG_VFO_C:
case RIG_VFO_MAIN_C:
rig->state.cache.freqMainC = freq;
elapsed_ms(&rig->state.cache.time_freqMainC, flag);
break;
case RIG_VFO_SUB_A:
rig->state.cache.freqSubA = freq;
elapsed_ms(&rig->state.cache.time_freqSubA, flag);
break;
case RIG_VFO_SUB_B:
rig->state.cache.freqSubB = freq;
elapsed_ms(&rig->state.cache.time_freqSubB, flag);
break;
case RIG_VFO_SUB_C:
rig->state.cache.freqSubC = freq;
elapsed_ms(&rig->state.cache.time_freqSubC, flag);
break;
case RIG_VFO_MEM:
rig->state.cache.freqMem = freq;
elapsed_ms(&rig->state.cache.time_freqMem, flag);
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unknown vfo?, vfo=%s\n", __func__,
rig_strvfo(vfo));
RETURNFUNC(-RIG_EINVAL);
}
if (rig_need_debug(RIG_DEBUG_CACHE))
{
cache_show(rig, __func__, __LINE__);
RETURNFUNC(RIG_OK);
}
return RIG_OK;
}
/**
* \brief get cached values for a VFO
* \param rig The rig handle
* \param vfo The VFO to get information from
* \param freq The frequency is stored here
* \param cache_ms_freq The age of the last frequency update in ms
* \param mode The mode is stored here
* \param cache_ms_mode The age of the last mode update in ms
* \param width The width is stored here
* \param cache_ms_width The age of the last width update in ms
*
* Use this to query the cache and then determine to actually fetch data from
* the rig.
*
* \note All pointers must be given. No pointer can be left at NULL
*
* \return RIG_OK if the operation has been successful, otherwise
* a negative value if an error occurred (in which case, cause is
* set appropriately).
*
*/
int rig_get_cache(RIG *rig, vfo_t vfo, freq_t *freq, int *cache_ms_freq,
rmode_t *mode, int *cache_ms_mode, pbwidth_t *width, int *cache_ms_width)
{
if (CHECK_RIG_ARG(rig) || !freq || !cache_ms_freq ||
!mode || !cache_ms_mode || !width || !cache_ms_width)
{
RETURNFUNC(-RIG_EINVAL);
}
if (rig_need_debug(RIG_DEBUG_CACHE))
{
ENTERFUNC;
}
rig_debug(RIG_DEBUG_CACHE, "%s: vfo=%s, current_vfo=%s\n", __func__,
rig_strvfo(vfo), rig_strvfo(rig->state.current_vfo));
if (vfo == RIG_VFO_CURR)
{
vfo = rig->state.current_vfo;
}
else if (vfo == RIG_VFO_OTHER)
{
switch (vfo)
{
case RIG_VFO_OTHER:
vfo = RIG_VFO_OTHER;
break;
case RIG_VFO_A:
vfo = RIG_VFO_B;
break;
case RIG_VFO_MAIN_A:
vfo = RIG_VFO_MAIN_B;
break;
case RIG_VFO_MAIN:
vfo = RIG_VFO_SUB;
break;
case RIG_VFO_B:
vfo = RIG_VFO_A;
break;
case RIG_VFO_MAIN_B:
vfo = RIG_VFO_MAIN_A;
break;
case RIG_VFO_SUB_A:
vfo = RIG_VFO_SUB_B;
break;
case RIG_VFO_SUB_B:
vfo = RIG_VFO_SUB_A;
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unknown vfo=%s\n", __func__, rig_strvfo(vfo));
}
}
// pick a sane default
if (vfo == RIG_VFO_CURR || vfo == RIG_VFO_NONE) { vfo = RIG_VFO_A; }
// If we're in satmode we map SUB to SUB_A
if (vfo == RIG_VFO_SUB && rig->state.cache.satmode) { vfo = RIG_VFO_SUB_A; };
switch (vfo)
{
case RIG_VFO_CURR:
*freq = rig->state.cache.freqCurr;
*mode = rig->state.cache.modeCurr;
*width = rig->state.cache.widthCurr;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqCurr,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeCurr,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthCurr,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_OTHER:
*freq = rig->state.cache.freqOther;
*mode = rig->state.cache.modeOther;
*width = rig->state.cache.widthOther;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqOther,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeOther,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthOther,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_A:
case RIG_VFO_MAIN:
case RIG_VFO_MAIN_A:
*freq = rig->state.cache.freqMainA;
*mode = rig->state.cache.modeMainA;
*width = rig->state.cache.widthMainA;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqMainA,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeMainA,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthMainA,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_B:
case RIG_VFO_SUB:
case RIG_VFO_MAIN_B:
*freq = rig->state.cache.freqMainB;
*mode = rig->state.cache.modeMainB;
*width = rig->state.cache.widthMainB;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqMainB,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeMainB,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthMainB,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_SUB_A:
*freq = rig->state.cache.freqSubA;
*mode = rig->state.cache.modeSubA;
*width = rig->state.cache.widthSubA;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqSubA,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeSubA,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthSubA,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_SUB_B:
*freq = rig->state.cache.freqSubB;
*mode = rig->state.cache.modeSubB;
*width = rig->state.cache.widthSubB;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqSubB,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeSubB,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthSubB,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_C:
//case RIG_VFO_MAINC: // not used by any rig yet
*freq = rig->state.cache.freqMainC;
*mode = rig->state.cache.modeMainC;
*width = rig->state.cache.widthMainC;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqMainC,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeMainC,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthMainC,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_SUB_C:
*freq = rig->state.cache.freqSubC;
*mode = rig->state.cache.modeSubC;
*width = rig->state.cache.widthSubC;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqSubC,
HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeSubC,
HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthSubC,
HAMLIB_ELAPSED_GET);
break;
case RIG_VFO_MEM:
*freq = rig->state.cache.freqMem;
*mode = rig->state.cache.modeMem;
*width = rig->state.cache.widthMem;
*cache_ms_freq = elapsed_ms(&rig->state.cache.time_freqMem, HAMLIB_ELAPSED_GET);
*cache_ms_mode = elapsed_ms(&rig->state.cache.time_modeMem, HAMLIB_ELAPSED_GET);
*cache_ms_width = elapsed_ms(&rig->state.cache.time_widthMem,
HAMLIB_ELAPSED_GET);
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unknown vfo?, vfo=%s\n", __func__,
rig_strvfo(vfo));
RETURNFUNC(-RIG_EINVAL);
}
rig_debug(RIG_DEBUG_CACHE, "%s: vfo=%s, freq=%.0f, mode=%s, width=%d\n", __func__, rig_strvfo(vfo),
(double)*freq, rig_strrmode(*mode), (int)*width);
if (rig_need_debug(RIG_DEBUG_CACHE))
{
RETURNFUNC(RIG_OK);
}
return RIG_OK;
}
// detect if somebody is twiddling the VFO
// indicator is last set freq doesn't match current freq
// so we have to query freq every time we set freq or vfo to handle this
@ -1904,7 +1442,7 @@ static int twiddling(RIG *rig)
rig->state.twiddle_time = time(NULL); // update last twiddle time
rig->state.current_freq = curr_freq; // we have a new freq to remember
set_cache_freq(rig, RIG_VFO_CURR, curr_freq);
rig_set_cache_freq(rig, RIG_VFO_CURR, curr_freq);
}
elapsed = time(NULL) - rig->state.twiddle_time;
@ -2010,7 +1548,7 @@ int HAMLIB_API rig_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
if (retcode != RIG_OK) { RETURNFUNC(retcode); }
set_cache_freq(rig, vfo, (freq_t)0);
rig_set_cache_freq(rig, vfo, (freq_t)0);
#if 0 // this verification seems to be causing bad behavior on some rigs
@ -2099,7 +1637,7 @@ int HAMLIB_API rig_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
#endif
)
{
set_cache_freq(rig, RIG_VFO_ALL, (freq_t)0);
rig_set_cache_freq(rig, RIG_VFO_ALL, (freq_t)0);
TRACE;
retcode = rig_get_freq(rig, vfo, &freq_new);
@ -2118,7 +1656,7 @@ int HAMLIB_API rig_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
// update our current freq too
if (vfo == RIG_VFO_CURR || vfo == rig->state.current_vfo) { rig->state.current_freq = freq_new; }
set_cache_freq(rig, vfo, freq_new);
rig_set_cache_freq(rig, vfo, freq_new);
if (vfo != RIG_VFO_CURR)
{
@ -2170,7 +1708,7 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) called vfo=%s\n", __func__, __LINE__,
rig_strvfo(vfo));
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
curr_vfo = rig->state.current_vfo; // save vfo for restore later
@ -2199,7 +1737,7 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
RETURNFUNC(RIG_OK);
}
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
// there are some rigs that can't get VFOA freq while VFOB is transmitting
// so we'll return the cached VFOA freq for them
@ -2233,7 +1771,7 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
&cache_ms_width);
//rig_debug(RIG_DEBUG_TRACE, "%s: cache check1 age=%dms\n", __func__, cache_ms_freq);
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
if (*freq != 0 && cache_ms_freq < rig->state.cache.timeout_ms)
{
@ -2272,7 +1810,7 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
retcode = caps->get_freq(rig, vfo, freq);
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
// sometimes a network rig like FLRig will return freq=0
// so we'll just reuse the cache for that condition
@ -2284,8 +1822,8 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
if (retcode == RIG_OK)
{
set_cache_freq(rig, vfo, *freq);
cache_show(rig, __func__, __LINE__);
rig_set_cache_freq(rig, vfo, *freq);
rig_cache_show(rig, __func__, __LINE__);
}
}
else
@ -2305,7 +1843,7 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
RETURNFUNC(retcode);
}
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
TRACE;
retcode = caps->get_freq(rig, vfo, freq);
@ -2319,9 +1857,9 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
if (RIG_OK == retcode)
{
cache_show(rig, __func__, __LINE__);
set_cache_freq(rig, vfo, *freq);
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
rig_set_cache_freq(rig, vfo, *freq);
rig_cache_show(rig, __func__, __LINE__);
/* return the first error code */
retcode = rc2;
}
@ -2344,9 +1882,9 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
*freq += rig->state.lo_freq;
}
cache_show(rig, __func__, __LINE__);
set_cache_freq(rig, vfo, *freq);
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
rig_set_cache_freq(rig, vfo, *freq);
rig_cache_show(rig, __func__, __LINE__);
ELAPSED2;
RETURNFUNC(retcode);
@ -2470,7 +2008,7 @@ int HAMLIB_API rig_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
if (retcode != RIG_OK) { RETURNFUNC(retcode); }
set_cache_mode(rig, vfo, mode, width);
rig_set_cache_mode(rig, vfo, mode, width);
ELAPSED2;
RETURNFUNC(retcode);
@ -2528,14 +2066,14 @@ int HAMLIB_API rig_get_mode(RIG *rig,
}
*mode = RIG_MODE_NONE;
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
int cache_ms_freq, cache_ms_mode, cache_ms_width;
rig_get_cache(rig, vfo, &freq, &cache_ms_freq, mode, &cache_ms_mode, width,
&cache_ms_width);
rig_debug(RIG_DEBUG_TRACE, "%s: %s cache check age=%dms\n", __func__,
rig_strvfo(vfo), cache_ms_mode);
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
if ((*mode != RIG_MODE_NONE && cache_ms_mode < rig->state.cache.timeout_ms)
&& cache_ms_width < rig->state.cache.timeout_ms)
@ -2560,7 +2098,7 @@ int HAMLIB_API rig_get_mode(RIG *rig,
retcode = caps->get_mode(rig, vfo, mode, width);
rig_debug(RIG_DEBUG_TRACE, "%s: retcode after get_mode=%d\n", __func__,
retcode);
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
}
else
{
@ -2578,7 +2116,7 @@ int HAMLIB_API rig_get_mode(RIG *rig,
TRACE;
retcode = caps->set_vfo(rig, vfo == RIG_VFO_CURR ? RIG_VFO_A : vfo);
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
if (retcode != RIG_OK)
{
@ -2603,7 +2141,7 @@ int HAMLIB_API rig_get_mode(RIG *rig,
rig_debug(RIG_DEBUG_TRACE, "%s(%d): debug\n", __func__, __LINE__);
rig->state.current_mode = *mode;
rig->state.current_width = *width;
cache_show(rig, __func__, __LINE__);
rig_cache_show(rig, __func__, __LINE__);
}
if (*width == RIG_PASSBAND_NORMAL && *mode != RIG_MODE_NONE)
@ -2612,8 +2150,8 @@ int HAMLIB_API rig_get_mode(RIG *rig,
*width = rig_passband_normal(rig, *mode);
}
set_cache_mode(rig, vfo, *mode, *width);
cache_show(rig, __func__, __LINE__);
rig_set_cache_mode(rig, vfo, *mode, *width);
rig_cache_show(rig, __func__, __LINE__);
ELAPSED2;
RETURNFUNC(retcode);
@ -2889,12 +2427,12 @@ int HAMLIB_API rig_set_vfo(RIG *rig, vfo_t vfo)
rig_debug(RIG_DEBUG_TRACE, "%s: retcode from rig_get_freq = %.10000s\n",
__func__,
rigerror(retcode));
set_cache_freq(rig, vfo, curr_freq);
rig_set_cache_freq(rig, vfo, curr_freq);
}
else
{
// if no get_freq clear all cache to be sure we refresh whatever we can
set_cache_freq(rig, RIG_VFO_ALL, (freq_t)0);
rig_set_cache_freq(rig, RIG_VFO_ALL, (freq_t)0);
}
#if 0 // with new cache should not have to expire here anymore
@ -7201,6 +6739,76 @@ HAMLIB_EXPORT(void) sync_callback(int lock)
#define MAX_FRAME_LENGTH 1024
static int async_data_handler_start(RIG *rig)
{
const struct rig_caps *caps = rig->caps;
struct rig_state *rs = &rig->state;
async_data_handler_priv_data *async_data_handler_priv;
ENTERFUNC;
#ifdef HAVE_PTHREAD
if (caps->async_data_supported)
{
rs->async_data_handler_thread_run = 1;
rs->async_data_handler_priv_data = calloc(1, sizeof(async_data_handler_priv_data));
if (rs->async_data_handler_priv_data == NULL)
{
RETURNFUNC(-RIG_ENOMEM);
}
async_data_handler_priv = (async_data_handler_priv_data *) rs->async_data_handler_priv_data;
async_data_handler_priv->args.rig = rig;
int err = pthread_create(&async_data_handler_priv->thread_id, NULL,
async_data_handler, &async_data_handler_priv->args);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d) pthread_create error: %s\n", __FILE__, __LINE__,
strerror(errno));
RETURNFUNC(-RIG_EINTERNAL);
}
}
#endif
RETURNFUNC(RIG_OK);
}
static int async_data_handler_stop(RIG *rig)
{
struct rig_state *rs = &rig->state;
async_data_handler_priv_data *async_data_handler_priv;
ENTERFUNC;
#ifdef HAVE_PTHREAD
rs->async_data_handler_thread_run = 0;
async_data_handler_priv = (async_data_handler_priv_data *) rs->async_data_handler_priv_data;
if (async_data_handler_priv != NULL)
{
if (async_data_handler_priv->thread_id != 0)
{
int err = pthread_join(async_data_handler_priv->thread_id, NULL);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error: %s\n", __FILE__, __LINE__,
strerror(errno));
// just ignore the error
}
async_data_handler_priv->thread_id = 0;
}
free(rs->async_data_handler_priv_data);
rs->async_data_handler_priv_data = NULL;
}
#endif
RETURNFUNC(RIG_OK);
}
void *async_data_handler(void *arg)
{
struct async_data_handler_args_s *args = (struct async_data_handler_args_s *)arg;
@ -7209,10 +6817,14 @@ void *async_data_handler(void *arg)
struct rig_state *rs = &rig->state;
int result;
rig_debug(RIG_DEBUG_TRACE, "%s(%d): Starting async data handler thread\n", __FILE__,
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting async data handler thread\n", __FILE__,
__LINE__);
while (async_data_handler_thread_run)
// TODO: check how to enable "transceive" on recent Kenwood/Yaesu rigs
// TODO: add initial support for async in Kenwood kenwood_transaction (+one) functions -> add transaction_active flag usage
// TODO: add initial support for async in Yaesu newcat_get_cmd/set_cmd (+validate) functions -> add transaction_active flag usage
while (rs->async_data_handler_thread_run)
{
int frame_length;
int async_frame;
@ -7220,8 +6832,19 @@ void *async_data_handler(void *arg)
result = rig->caps->read_frame_direct(rig, sizeof(frame), frame);
if (result < 0)
{
// TODO: error handling
rig_debug(RIG_DEBUG_ERR, "%s: read_frame_direct() failed, result=%d\n", __func__, result);
// TODO: it may be necessary to have mutex locking on transaction_active flag
if (rs->transaction_active)
{
unsigned char data = (unsigned char) result;
write_block_sync_error(&rs->rigport, &data, 1);
}
if (result != -RIG_ETIMEOUT)
{
// TODO: error handling -> store errors in rig state -> to be exposed in async snapshot packets
rig_debug(RIG_DEBUG_ERR, "%s: read_frame_direct() failed, result=%d\n", __func__, result);
hl_usleep(500 * 1000);
}
continue;
}
@ -7236,7 +6859,8 @@ void *async_data_handler(void *arg)
result = rig->caps->process_async_frame(rig, frame_length, frame);
if (result < 0)
{
// TODO: error handling
// TODO: error handling -> store errors in rig state -> to be exposed in async snapshot packets
rig_debug(RIG_DEBUG_ERR, "%s: process_async_frame() failed, result=%d\n", __func__, result);
continue;
}
}
@ -7245,14 +6869,14 @@ void *async_data_handler(void *arg)
result = write_block_sync(&rs->rigport, frame, frame_length);
if (result < 0)
{
// TODO: error handling
// TODO: error handling? can writing to a pipe really fail in ways we can recover from?
rig_debug(RIG_DEBUG_ERR, "%s: write_block_sync() failed, result=%d\n", __func__, result);
continue;
}
}
}
rig_debug(RIG_DEBUG_TRACE, "%s(%d): Stopping async data handler thread\n", __FILE__,
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping async data handler thread\n", __FILE__,
__LINE__);
return NULL;

347
src/snapshot_data.c 100644
Wyświetl plik

@ -0,0 +1,347 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <hamlib/rig.h>
#include "misc.h"
#include "snapshot_data.h"
#include "hamlibdatetime.h"
#include "cJSON.h"
// Maximum number of data bytes in a single spectrum line
#define SPECTRUM_DATA_MAX_LENGTH 2048
#define MAX_VFO_COUNT 4
#define SPECTRUM_MODE_FIXED "FIXED"
#define SPECTRUM_MODE_CENTER "CENTER"
static int snapshot_serialize_vfo(cJSON *vfo_node, RIG *rig, vfo_t vfo)
{
freq_t freq;
int freq_ms, mode_ms, width_ms;
rmode_t mode;
pbwidth_t width;
ptt_t ptt;
split_t split;
vfo_t split_vfo;
int result;
int is_rx, is_tx;
cJSON *node;
// TODO: This data should match rig_get_info command response
node = cJSON_AddStringToObject(vfo_node, "name", rig_strvfo(vfo));
if (node == NULL)
{
goto error;
}
result = rig_get_cache(rig, vfo, &freq, &freq_ms, &mode, &mode_ms, &width, &width_ms);
if (result == RIG_OK)
{
node = cJSON_AddNumberToObject(vfo_node, "freq", freq);
if (node == NULL)
{
goto error;
}
node = cJSON_AddStringToObject(vfo_node, "mode", rig_strrmode(mode));
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(vfo_node, "width", (double) width);
if (node == NULL)
{
goto error;
}
}
ptt = rig->state.cache.ptt;
node = cJSON_AddBoolToObject(vfo_node, "ptt", ptt == RIG_PTT_OFF ? 0 : 1);
if (node == NULL)
{
goto error;
}
split = rig->state.cache.split;
split_vfo = rig->state.cache.split_vfo;
is_rx = (split == RIG_SPLIT_OFF && vfo == rig->state.current_vfo) || (split == RIG_SPLIT_ON && vfo != split_vfo);
node = cJSON_AddBoolToObject(vfo_node, "rx", is_rx);
if (node == NULL)
{
goto error;
}
is_tx = (split == RIG_SPLIT_OFF && vfo == rig->state.current_vfo) || (split == RIG_SPLIT_ON && vfo == split_vfo);
node = cJSON_AddBoolToObject(vfo_node, "tx", is_tx);
if (node == NULL)
{
goto error;
}
RETURNFUNC(RIG_OK);
error:
RETURNFUNC(-RIG_EINTERNAL);
}
static int snapshot_serialize_spectrum(cJSON *spectrum_node, RIG *rig, struct rig_spectrum_line *spectrum_line)
{
// Spectrum data is represented as a hexadecimal ASCII string where each data byte is represented as 2 ASCII letters
char spectrum_data_string[SPECTRUM_DATA_MAX_LENGTH * 2];
cJSON *node;
int i;
struct rig_spectrum_scope *scopes = rig->caps->spectrum_scopes;
char *name = "?";
for (i = 0; scopes[i].name != NULL; i++)
{
if (scopes[i].id == spectrum_line->id)
{
name = scopes[i].name;
}
}
node = cJSON_AddNumberToObject(spectrum_node, "id", spectrum_line->id);
if (node == NULL)
{
goto error;
}
node = cJSON_AddStringToObject(spectrum_node, "name", name);
if (node == NULL)
{
goto error;
}
node = cJSON_AddStringToObject(spectrum_node, "type",
spectrum_line->spectrum_mode == RIG_SPECTRUM_MODE_CENTER ?
SPECTRUM_MODE_CENTER : SPECTRUM_MODE_FIXED);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "minLevel", spectrum_line->data_level_min);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "maxLevel", spectrum_line->data_level_max);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "minStrength", spectrum_line->signal_strength_min);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "maxStrength", spectrum_line->signal_strength_max);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "centerFreq", spectrum_line->center_freq);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "span", spectrum_line->span_freq);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "lowFreq", spectrum_line->low_edge_freq);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "highFreq", spectrum_line->high_edge_freq);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(spectrum_node, "length", (double) spectrum_line->spectrum_data_length);
if (node == NULL)
{
goto error;
}
to_hex(spectrum_line->spectrum_data_length, spectrum_line->spectrum_data,
sizeof(spectrum_data_string), spectrum_data_string);
node = cJSON_AddStringToObject(spectrum_node, "data", spectrum_data_string);
if (node == NULL)
{
goto error;
}
RETURNFUNC(RIG_OK);
error:
RETURNFUNC(-RIG_EINTERNAL);
}
int snapshot_serialize(size_t buffer_length, char *buffer, RIG *rig, struct rig_spectrum_line *spectrum_line)
{
cJSON *root_node;
cJSON *rig_node, *vfos_array, *vfo_node, *spectra_array, *spectrum_node;
cJSON *node;
cJSON_bool bool_result;
int vfo_count = 2;
vfo_t vfos[MAX_VFO_COUNT];
int result;
int i;
vfos[0] = RIG_VFO_A;
vfos[1] = RIG_VFO_B;
root_node = cJSON_CreateObject();
if (root_node == NULL)
{
RETURNFUNC(-RIG_EINTERNAL);
}
node = cJSON_AddStringToObject(root_node, "app", PACKAGE_NAME);
if (node == NULL)
{
goto error;
}
node = cJSON_AddStringToObject(root_node, "version", PACKAGE_VERSION " " HAMLIBDATETIME);
if (node == NULL)
{
goto error;
}
node = cJSON_AddNumberToObject(root_node, "seq", rig->state.snapshot_packet_sequence_number);
if (node == NULL)
{
goto error;
}
// TODO: What content should CRC be based on?
node = cJSON_AddNumberToObject(root_node, "crc", 0);
if (node == NULL)
{
goto error;
}
rig_node = cJSON_CreateObject();
if (rig_node == NULL)
{
goto error;
}
cJSON_AddItemToObject(root_node, "rig", rig_node);
node = cJSON_AddStringToObject(rig_node, "id", "rig_id");
if (node == NULL)
{
goto error;
}
node = cJSON_AddStringToObject(rig_node, "status", "");
if (node == NULL)
{
goto error;
}
node = cJSON_AddStringToObject(rig_node, "errorMsg", "");
if (node == NULL)
{
goto error;
}
node = cJSON_AddStringToObject(rig_node, "name", rig->caps->model_name);
if (node == NULL)
{
goto error;
}
node = cJSON_AddBoolToObject(rig_node, "split", rig->state.cache.split == RIG_SPLIT_ON ? 1 : 0);
if (node == NULL)
{
goto error;
}
node = cJSON_AddStringToObject(rig_node, "splitVfo", rig_strvfo(rig->state.cache.split_vfo));
if (node == NULL)
{
goto error;
}
node = cJSON_AddBoolToObject(rig_node, "satMode", 0);
if (node == NULL)
{
goto error;
}
vfos_array = cJSON_CreateArray();
if (vfos_array == NULL)
{
goto error;
}
for (i = 0; i < vfo_count; i++)
{
vfo_node = cJSON_CreateObject();
result = snapshot_serialize_vfo(vfo_node, rig, vfos[i]);
if (result != RIG_OK)
{
cJSON_Delete(vfo_node);
goto error;
}
cJSON_AddItemToArray(vfos_array, vfo_node);
}
cJSON_AddItemToObject(root_node, "vfos", vfos_array);
if (spectrum_line != NULL)
{
spectra_array = cJSON_CreateArray();
if (spectra_array == NULL)
{
goto error;
}
spectrum_node = cJSON_CreateObject();
result = snapshot_serialize_spectrum(spectrum_node, rig, spectrum_line);
if (result != RIG_OK)
{
cJSON_Delete(spectrum_node);
goto error;
}
cJSON_AddItemToArray(spectra_array, spectrum_node);
cJSON_AddItemToObject(root_node, "spectra", spectra_array);
}
bool_result = cJSON_PrintPreallocated(root_node, buffer, (int) buffer_length, 0);
cJSON_Delete(root_node);
if (!bool_result)
{
RETURNFUNC(-RIG_EINVAL);
}
rig->state.snapshot_packet_sequence_number++;
RETURNFUNC(RIG_OK);
error:
cJSON_Delete(root_node);
RETURNFUNC(-RIG_EINTERNAL);
}

Wyświetl plik

@ -0,0 +1,6 @@
#ifndef _SNAPSHOT_DATA_H
#define _SNAPSHOT_DATA_H
int snapshot_serialize(size_t buffer_length, char *buffer, RIG *rig, struct rig_spectrum_line *spectrum_line);
#endif

Wyświetl plik

@ -98,7 +98,7 @@
/** \brief rig: ?? */
#define TOK_VFO_COMP TOKEN_FRONTEND(110)
/** \brief rig: polling interval (units?) */
/** \brief rig: Rig state poll interval in milliseconds */
#define TOK_POLL_INTERVAL TOKEN_FRONTEND(111)
/** \brief rig: lo frequency of any transverters */
#define TOK_LO_FREQ TOKEN_FRONTEND(112)
@ -106,7 +106,7 @@
#define TOK_RANGE_SELECTED TOKEN_FRONTEND(121)
/** \brief rig: Range Name */
#define TOK_RANGE_NAME TOKEN_FRONTEND(122)
/** \brief rig: Cache timeout */
/** \brief rig: Cache timeout in milliseconds */
#define TOK_CACHE_TIMEOUT TOKEN_FRONTEND(123)
/** \brief rig: Auto power on rig_open when supported */
#define TOK_AUTO_POWER_ON TOKEN_FRONTEND(124)

Wyświetl plik

@ -247,8 +247,8 @@ int dumpcaps(RIG *rig, FILE *fout)
caps->targetable_vfo ? "Y" : "N");
fprintf(fout,
"Has transceive: %s\n",
caps->transceive ? "Y" : "N");
"Has async data support: %s\n",
caps->async_data_supported ? "Y" : "N");
fprintf(fout, "Announce: 0x%x\n", caps->announces);
fprintf(fout,

Wyświetl plik

@ -4023,218 +4023,25 @@ declare_proto_rig(get_channel)
}
static int myfreq_event(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg)
{
ENTERFUNC;
printf("Event: freq changed to %"PRIll"Hz on %s\n",
(int64_t)freq,
rig_strvfo(vfo));
RETURNFUNC(0);
}
static int mymode_event(RIG *rig,
vfo_t vfo,
rmode_t mode,
pbwidth_t width,
rig_ptr_t arg)
{
ENTERFUNC;
printf("Event: mode changed to %s, width %liHz on %s\n",
rig_strrmode(mode),
width, rig_strvfo(vfo));
RETURNFUNC(0);
}
static int myvfo_event(RIG *rig, vfo_t vfo, rig_ptr_t arg)
{
ENTERFUNC;
printf("Event: vfo changed to %s\n", rig_strvfo(vfo));
RETURNFUNC(0);
}
static int myptt_event(RIG *rig, vfo_t vfo, ptt_t ptt, rig_ptr_t arg)
{
ENTERFUNC;
printf("Event: PTT changed to %i on %s\n", ptt, rig_strvfo(vfo));
RETURNFUNC(0);
}
static int mydcd_event(RIG *rig, vfo_t vfo, dcd_t dcd, rig_ptr_t arg)
{
ENTERFUNC;
printf("Event: DCD changed to %i on %s\n", dcd, rig_strvfo(vfo));
RETURNFUNC(0);
}
static int print_spectrum_line(char *str, size_t length,
struct rig_spectrum_line *line)
{
int data_level_max = line->data_level_max / 2;
int aggregate_count = line->spectrum_data_length / 120;
int aggregate_value = 0;
int i, c;
int charlen = strlen("");
str[0] = '\0';
for (i = 0, c = 0; i < line->spectrum_data_length; i++)
{
int current = line->spectrum_data[i];
aggregate_value = current > aggregate_value ? current : aggregate_value;
if (i > 0 && i % aggregate_count == 0)
{
if (c + charlen >= length)
{
break;
}
int level = aggregate_value * 10 / data_level_max;
if (level >= 8)
{
strcpy(str + c, "");
c += charlen;
}
else if (level >= 6)
{
strcpy(str + c, "");
c += charlen;
}
else if (level >= 4)
{
strcpy(str + c, "");
c += charlen;
}
else if (level >= 2)
{
strcpy(str + c, "");
c += charlen;
}
else if (level >= 0)
{
strcpy(str + c, " ");
c += 1;
}
aggregate_value = 0;
}
}
return c;
}
static int myspectrum_event(RIG *rig, struct rig_spectrum_line *line,
rig_ptr_t arg)
{
ENTERFUNC;
if (rig_need_debug(RIG_DEBUG_ERR))
{
char spectrum_debug[line->spectrum_data_length * 4];
print_spectrum_line(spectrum_debug, sizeof(spectrum_debug), line);
rig_debug(RIG_DEBUG_ERR, "%s: ASCII Spectrum Scope: %s\n", __func__,
spectrum_debug);
}
// TODO: Push out spectrum data via multicast server once it is implemented
RETURNFUNC(0);
}
/* 'A' */
/**
* \deprecated Function deprecated. Use the new async data functionality instead.
*/
declare_proto_rig(set_trn)
{
int trn;
ENTERFUNC;
if (!strcmp(arg1, "?"))
{
fprintf(fout, "OFF RIG POLL\n");
RETURNFUNC(RIG_OK);
}
if (!strcmp(arg1, "OFF"))
{
trn = RIG_TRN_OFF;
}
else if (!strcmp(arg1, "RIG") || !strcmp(arg1, "ON"))
{
trn = RIG_TRN_RIG;
}
else if (!strcmp(arg1, "POLL"))
{
trn = RIG_TRN_POLL;
}
else
{
RETURNFUNC(-RIG_EINVAL);
}
if (trn != RIG_TRN_OFF)
{
rig_set_freq_callback(rig, myfreq_event, NULL);
rig_set_mode_callback(rig, mymode_event, NULL);
rig_set_vfo_callback(rig, myvfo_event, NULL);
rig_set_ptt_callback(rig, myptt_event, NULL);
rig_set_dcd_callback(rig, mydcd_event, NULL);
rig_set_spectrum_callback(rig, myspectrum_event, NULL);
}
RETURNFUNC(RIG_OK);
//RETURNFUNC(rig_set_trn(rig, trn));
RETURNFUNC(-RIG_EDEPRECATED);
}
/* 'a' */
/**
* \deprecated Function deprecated. Use the new async data functionality instead.
*/
declare_proto_rig(get_trn)
{
int status;
int trn;
static const char *trn_txt[] =
{
"OFF",
"RIG",
"POLL"
};
ENTERFUNC;
status = rig_get_trn(rig, &trn);
if (status != RIG_OK)
{
RETURNFUNC(status);
}
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
{
fprintf(fout, "%s: ", cmd->arg1);
}
if (trn >= 0 && trn <= 2)
{
fprintf(fout, "%s%c", trn_txt[trn], resp_sep);
}
RETURNFUNC(status);
RETURNFUNC(-RIG_EDEPRECATED);
}

Wyświetl plik

@ -111,6 +111,7 @@ static struct option long_options[] =
{"uplink", 1, 0, 'x'},
{"debug-time-stamps", 0, 0, 'Z'},
{"multicast-addr", 1, 0, 'M'},
{"multicast-port", 1, 0, 'n'},
{0, 0, 0, 0}
};
@ -145,6 +146,7 @@ static int volatile ctrl_c;
const char *portno = "4532";
const char *src_addr = NULL; /* INADDR_ANY */
const char *multicast_addr = "0.0.0.0";
int multicast_port = 4532;
#define MAXCONFLEN 1024
@ -558,6 +560,21 @@ int main(int argc, char *argv[])
multicast_addr = optarg;
break;
case 'n':
if (!optarg)
{
usage(); /* wrong arg count */
exit(1);
}
multicast_port = atoi(optarg);
if (multicast_port == 0)
{
fprintf(stderr, "Invalid multicast port: %s\n", optarg);
exit(1);
}
break;
default:
usage(); /* unknown option? */
exit(1);
@ -756,9 +773,8 @@ int main(int argc, char *argv[])
saved_result = result;
enum multicast_item_e items = RIG_MULTICAST_POLL | RIG_MULTICAST_TRANSCEIVE |
RIG_MULTICAST_SPECTRUM;
retcode = network_multicast_server(my_rig, multicast_addr, 4532, items);
enum multicast_item_e items = RIG_MULTICAST_POLL | RIG_MULTICAST_TRANSCEIVE | RIG_MULTICAST_SPECTRUM;
retcode = network_multicast_publisher_start(my_rig, multicast_addr, multicast_port, items);
if (retcode != RIG_OK)
{
@ -927,7 +943,6 @@ int main(int argc, char *argv[])
rig_debug(RIG_DEBUG_ERR, "%s: select() failed: %s\n", __func__,
strerror(errno_stored));
// TODO: FIXME: Why does this select() return EINTR after any command when set_trn RIG is enabled?
if (errno == EINTR)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: ignoring interrupted system call\n",
@ -995,6 +1010,8 @@ int main(int argc, char *argv[])
}
while (retcode == 0 && !ctrl_c);
network_multicast_publisher_stop(my_rig);
#ifdef HAVE_PTHREAD
/* allow threads to finish current action */
mutex_rigctld(1);
@ -1271,7 +1288,8 @@ void usage(void)
" -W, --twiddle_rit suppress VFOB getfreq so RIT can be twiddled\n"
" -x, --uplink set uplink get_freq ignore, 1=Sub, 2=Main\n"
" -Z, --debug-time-stamps enable time stamps for debug messages\n"
" -M, --multicast-addr=addr set multicast addr, default 0.0.0.0 (off), recommend 224.0.1.1\n"
" -M, --multicast-addr=addr set multicast UDP address, default 0.0.0.0 (off), recommend 224.0.1.1\n"
" -n, --multicast-port=port set multicast UDP port, default 4532\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n\n",
portno);