kopia lustrzana https://github.com/Hamlib/Hamlib
Add first implementation of multicast functions and multicastclient.c test program.
Next step will be to implement this when rig is opened. This will turn Hamlib into a polling system in addition to polling -- caching will handle repeated requests This should mean basic functions of freq, mode, split, ptt (and others when implemented) will have almost immediate responses as only the cached values should be used. https://github.com/Hamlib/Hamlib/issues/695pull/1289/head
rodzic
f94ca77399
commit
e3c2a8d6ca
|
@ -2,15 +2,15 @@ Works on Windows and Linux
|
|||
|
||||
Requires you get dotnet installed of course
|
||||
|
||||
For Wiundows install Visual Studio or such to get dotnet
|
||||
For Windows install Visual Studio or such to get dotnet
|
||||
|
||||
On Ubuntu 20.04 it was this
|
||||
On Ubuntu 21.04 it was this
|
||||
apt install snap
|
||||
apt install mono-complete
|
||||
snap install dotnet-sdk --classic --channel=5.0
|
||||
snap install dotnet-sdk --classic --channel=6.0
|
||||
snap alias dotnet-sdk.dotnet dotnet
|
||||
snap install dotnet-runtime-50 --classic
|
||||
snap alias dotnet-runtime-50.dotnet dotnet
|
||||
snap install dotnet-runtime-60 --classic
|
||||
snap alias dotnet-runtime-60.dotnet dotnet
|
||||
export DOTNET_ROOT=/snap/dotnet-sdk/current
|
||||
|
||||
Once dotnet is OK
|
||||
|
@ -19,4 +19,20 @@ dotnet build
|
|||
|
||||
You should then be able to run
|
||||
|
||||
./bin/Debug/net5.0/multicast
|
||||
./bin/Debug/net6.0/multicast
|
||||
|
||||
======================================================
|
||||
Waiting for Net 7.0/8.0 to be in Ubunut main packages
|
||||
Following did not work
|
||||
|
||||
sudo apt remove 'dotnet*' 'aspnet*' 'netstandard*'
|
||||
touch /etc/apt/preferences
|
||||
// add to preferences
|
||||
Package: dotnet* aspnet* netstandard*
|
||||
Pin: origin "archive.ubuntu.com"
|
||||
Pin-Priority: -10
|
||||
// end preferences
|
||||
snap remove dotnet-sdk
|
||||
snap install dotnet-sdk --classic --channel=7.0
|
||||
snap alias dotnet-runtime-70.dotnet dotnet
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace HamlibMultiCast
|
|||
{
|
||||
public class HamlibMulticastClient
|
||||
{
|
||||
public string __comment1__;
|
||||
public class VFO
|
||||
{
|
||||
public string Name;
|
||||
|
@ -17,19 +18,25 @@ namespace HamlibMultiCast
|
|||
public int Width;
|
||||
public bool RX;
|
||||
public bool TX;
|
||||
public int WidthLower;
|
||||
public int WidthUpper;
|
||||
}
|
||||
public string __comment_spectrum__;
|
||||
public class SpectrumClass
|
||||
{
|
||||
public string Name;
|
||||
public int Length;
|
||||
public string __comment_spectrum_data__;
|
||||
public string Data;
|
||||
public string Type;
|
||||
public int MinLevel;
|
||||
public int MaxLevel;
|
||||
public int MinStrength;
|
||||
public int MaxStrength;
|
||||
public string __comment_spectrum_center__;
|
||||
public double CenterFreq;
|
||||
public int Span;
|
||||
public string __comment_spectrum_fixed__;
|
||||
public double LowFreq;
|
||||
public double HighFreq;
|
||||
}
|
||||
|
@ -38,14 +45,26 @@ namespace HamlibMultiCast
|
|||
public string Command;
|
||||
public string Status;
|
||||
}
|
||||
public string __comment_gps__;
|
||||
public class GPS {
|
||||
public double Latitude;
|
||||
public double Longitude;
|
||||
public double Altitude;
|
||||
}
|
||||
public string ID;
|
||||
public string VFOCurr;
|
||||
public List<VFO> VFOs { get; set; }
|
||||
public bool Split;
|
||||
public string __comment_time__;
|
||||
public string Time;
|
||||
public bool SatMode;
|
||||
public string Rig;
|
||||
public string App;
|
||||
public string __comment_version__;
|
||||
public string Version;
|
||||
public string __comment_seq__;
|
||||
public UInt32 Seq;
|
||||
public string __comment_crc__;
|
||||
public string CRC;
|
||||
public List<SpectrumClass> Spectra { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,93 +1,16 @@
|
|||
"ID": "FT-991:/dev/ttyUSB0",
|
||||
"Time": 2023-05-05T04:32:47.834952-0000,
|
||||
"Sequence": 892,
|
||||
"VFOCurr": "VFOA",
|
||||
"VFOs": [
|
||||
{
|
||||
"__comment1__": "customizable rig identification -- will allow multiple rigs to be on the multicast",
|
||||
"ID": "Rig#1",
|
||||
"VFOs": [
|
||||
{
|
||||
"Name": "VFOA",
|
||||
"Freq": 14074000,
|
||||
"Mode": "USB",
|
||||
"Width": 2800,
|
||||
"WidthLower": 200,
|
||||
"WidthUpper": 3000,
|
||||
"RX": true,
|
||||
"TX": false
|
||||
},
|
||||
{
|
||||
"Name": "VFOB",
|
||||
"Freq": 14076000,
|
||||
"Mode": "USB",
|
||||
"Width": 2800,
|
||||
"WidthLower": 200,
|
||||
"WidthUpper": 3000,
|
||||
"RX": false,
|
||||
"TX": true
|
||||
}],
|
||||
|
||||
"__comment_spectrum__": "Rigs that have spectrum output may include this data",
|
||||
"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_spectrum_center__": "If Type=CENTER, the following fields will be present:",
|
||||
"CenterFreq": 14267000,
|
||||
"Span": 25000,
|
||||
|
||||
"__comment_spectrum_fixed__": "If SpectrumType=FIXED, the following fields will be present:",
|
||||
"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_spectrum_center__": "If Type=CENTER, the following fields will be present:",
|
||||
"CenterFreq": 14267000,
|
||||
"Span": 25000,
|
||||
|
||||
"__comment_spectrum_fixed__": "If SpectrumType=FIXED, the following fields will be present:",
|
||||
"LowFreq": 14000000,
|
||||
"HighFreq": 14250000
|
||||
}],
|
||||
"LastCommand": {
|
||||
"ID": "MyApp 123",
|
||||
"Command": "set_freq VFOA 14074000",
|
||||
"Status": "OK"
|
||||
},
|
||||
"__comment_gps__": "Rigs that have gps output may include this data Lat -180 to 180 Lat -90 to 90, altitude in meters",
|
||||
"GPS":
|
||||
{
|
||||
"Latitude": 31.6543,
|
||||
"Longitude": -91.6543,
|
||||
"Altitude": 640.1,
|
||||
"__comment_gps__": "Time is in UTC gps synchronized for this block",
|
||||
"Time": "2023-01-18T14:29:18.740561"
|
||||
},
|
||||
|
||||
"Split": true,
|
||||
"SatMode": false,
|
||||
"__comment_Time__": "Time is from internal rig not GPS synced so may be inaccurate -- see GPS Time",
|
||||
"Time": "2023-01-18T14:29:18.740561",
|
||||
"Rig": "Dummy",
|
||||
"App": "Hamlib",
|
||||
"__comment_version__": "protocol version date YYYYMMDD",
|
||||
"Version": "20210520",
|
||||
"__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"
|
||||
{
|
||||
,
|
||||
"Name": "VFOA",
|
||||
"Freq": 14086800,
|
||||
"Mode": "AM",
|
||||
"Width": 9000,
|
||||
"RX": True,
|
||||
"TX": False
|
||||
}
|
||||
|
||||
|
|
|
@ -2464,6 +2464,21 @@ struct rig_cache {
|
|||
int satmode; // if rig is in satellite mode
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Multicast data items the are unique per rig instantiation
|
||||
* This is meant for internal Hamlib use only
|
||||
*/
|
||||
#include <multicast.h>
|
||||
struct multicast_s
|
||||
{
|
||||
int multicast_running;
|
||||
int sock;
|
||||
int seqnumber;
|
||||
int runflag; // = 0;
|
||||
struct ip_mreq mreq; // = {0};
|
||||
pthread_t threadid;
|
||||
struct sockaddr_in dest_addr; // = {0};
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Rig state containing live data and customized fields.
|
||||
|
@ -2594,6 +2609,7 @@ struct rig_state {
|
|||
char client_version[32]; /*!<! Allow client to report version for compatibility checks/capability */
|
||||
freq_t offset_vfoa; /*!< Offset to apply to VFOA/Main set_freq */
|
||||
freq_t offset_vfob; /*!< Offset to apply to VFOB/Sub set_freq */
|
||||
struct multicast_s *multicast; /*!< Pointer to multicast server data */
|
||||
};
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
|
|
@ -0,0 +1,396 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
//#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "hamlib/rig.h"
|
||||
#include "misc.h"
|
||||
#include "multicast.h"
|
||||
|
||||
#define RIG_MULTICAST_ADDR "224.0.0.1"
|
||||
#define RIG_MULTICAST_PORT 4532
|
||||
|
||||
#if 0
|
||||
static int multicast_running;
|
||||
static int sock;
|
||||
static int seqnumber;
|
||||
static int runflag = 0;
|
||||
static struct ip_mreq mreq = {0};
|
||||
static pthread_t threadid;
|
||||
static struct sockaddr_in dest_addr = {0};
|
||||
#endif
|
||||
|
||||
|
||||
static int multicast_status_changed(RIG *rig)
|
||||
{
|
||||
int retval;
|
||||
#if 0
|
||||
freq_t freq, freqsave = rig->state.cache.freqMainA;
|
||||
|
||||
if ((retval = rig_get_freq(rig, RIG_VFO_A, &freq)) != RIG_OK)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: rig_get_freq:%s\n", __func__, rigerror(retval));
|
||||
}
|
||||
|
||||
if (freq != freqsave) { return 1; }
|
||||
|
||||
#endif
|
||||
|
||||
rmode_t mode, modesave = rig->state.cache.modeMainA;
|
||||
pbwidth_t width, widthsave = rig->state.cache.widthMainA;
|
||||
|
||||
if (rig->state.multicast->seqnumber % 2 == 0
|
||||
&& (retval = rig_get_mode(rig, RIG_VFO_A, &mode, &width)) != RIG_OK)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: rig_get_freq:%s\n", __func__, rigerror(retval));
|
||||
}
|
||||
|
||||
if (mode != modesave) { return 1; }
|
||||
|
||||
if (width != widthsave) { return 1; }
|
||||
|
||||
ptt_t ptt, pttsave = rig->state.cache.ptt;
|
||||
|
||||
if (rig->state.multicast->seqnumber % 2 == 0
|
||||
&& (retval = rig_get_ptt(rig, RIG_VFO_A, &ptt)) != RIG_OK)
|
||||
if (ptt != pttsave) { return 1; }
|
||||
|
||||
split_t split, splitsave = rig->state.cache.split;
|
||||
vfo_t txvfo;
|
||||
|
||||
if (rig->state.multicast->seqnumber % 2 == 0
|
||||
&& (retval = rig_get_split_vfo(rig, RIG_VFO_A, &split, &txvfo)) != RIG_OK)
|
||||
if (split != splitsave) { return 1; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void json_add_string(char *msg, const char *key, const char *value)
|
||||
{
|
||||
if (strlen(msg) != 0)
|
||||
{
|
||||
strcat(msg, ",\n");
|
||||
}
|
||||
|
||||
strcat(msg, "\"");
|
||||
strcat(msg, key);
|
||||
strcat(msg, "\": ");
|
||||
strcat(msg, "\"");
|
||||
strcat(msg, value);
|
||||
strcat(msg, "\"");
|
||||
}
|
||||
|
||||
void json_add_int(char *msg, const char *key, const int number)
|
||||
{
|
||||
if (strlen(msg) != 0)
|
||||
{
|
||||
strcat(msg, ",\n");
|
||||
}
|
||||
|
||||
strcat(msg, "\"");
|
||||
strcat(msg, key);
|
||||
strcat(msg, "\": ");
|
||||
char tmp[64];
|
||||
sprintf(tmp, "%d", number);
|
||||
strcat(msg, tmp);
|
||||
}
|
||||
|
||||
void json_add_double(char *msg, const char *key, const double value)
|
||||
{
|
||||
if (strlen(msg) != 0)
|
||||
{
|
||||
strcat(msg, ",\n");
|
||||
}
|
||||
|
||||
strcat(msg, "\"");
|
||||
strcat(msg, key);
|
||||
strcat(msg, "\": ");
|
||||
char tmp[64];
|
||||
sprintf(tmp, "%f", value);
|
||||
strcat(msg, tmp);
|
||||
}
|
||||
|
||||
void json_add_boolean(char *msg, const char *key, const int value)
|
||||
{
|
||||
if (strlen(msg) != 0)
|
||||
{
|
||||
strcat(msg, ",\n");
|
||||
}
|
||||
|
||||
strcat(msg, "\"");
|
||||
strcat(msg, key);
|
||||
strcat(msg, "\": ");
|
||||
char tmp[64];
|
||||
sprintf(tmp, "%s", value == 0 ? "False" : "True");
|
||||
strcat(msg, tmp);
|
||||
}
|
||||
|
||||
void json_add_time(char *msg)
|
||||
{
|
||||
char mydate[256];
|
||||
date_strget(mydate, sizeof(mydate), 0);
|
||||
|
||||
if (strlen(msg) != 0)
|
||||
{
|
||||
strcat(msg, ",\n");
|
||||
}
|
||||
|
||||
strcat(msg, "\"Time\": ");
|
||||
strcat(msg, mydate);
|
||||
}
|
||||
|
||||
void json_add_vfoA(RIG *rig, char *msg)
|
||||
{
|
||||
strcat(msg, "{\n");
|
||||
json_add_string(msg, "Name", "VFOA");
|
||||
json_add_int(msg, "Freq", rig->state.cache.freqMainA);
|
||||
|
||||
if (strlen(rig_strrmode(rig->state.cache.modeMainA)) > 0)
|
||||
{
|
||||
json_add_string(msg, "Mode", rig_strrmode(rig->state.cache.modeMainA));
|
||||
}
|
||||
|
||||
if (rig->state.cache.widthMainA > 0)
|
||||
{
|
||||
json_add_int(msg, "Width", rig->state.cache.widthMainA);
|
||||
}
|
||||
|
||||
json_add_boolean(msg, "RX", !rig->state.cache.ptt);
|
||||
json_add_boolean(msg, "TX", rig->state.cache.ptt);
|
||||
strcat(msg, "\n}\n");
|
||||
}
|
||||
|
||||
void json_add_vfoB(RIG *rig, char *msg)
|
||||
{
|
||||
strcat(msg, "{\n");
|
||||
json_add_string(msg, "Name", "VFOB");
|
||||
json_add_int(msg, "Freq", rig->state.cache.freqMainB);
|
||||
|
||||
if (strlen(rig_strrmode(rig->state.cache.modeMainB)) > 0)
|
||||
{
|
||||
json_add_string(msg, "Mode", rig_strrmode(rig->state.cache.modeMainB));
|
||||
}
|
||||
|
||||
if (rig->state.cache.widthMainB > 0)
|
||||
{
|
||||
json_add_int(msg, "Width", rig->state.cache.widthMainB);
|
||||
}
|
||||
|
||||
json_add_boolean(msg, "RX", !rig->state.cache.ptt);
|
||||
json_add_boolean(msg, "TX", rig->state.cache.ptt);
|
||||
strcat(msg, "\n},\n");
|
||||
}
|
||||
|
||||
static int multicast_send_json(RIG *rig)
|
||||
{
|
||||
char msg[8192]; // could be pretty big
|
||||
char buf[4096];
|
||||
// sprintf(msg,"%s:f=%.1f", date_strget(msg, (int)sizeof(msg), 0), f);
|
||||
msg[0] = 0;
|
||||
snprintf(buf, sizeof(buf), "%s:%s", rig->caps->model_name,
|
||||
rig->state.rigport.pathname);
|
||||
json_add_string(msg, "ID", buf);
|
||||
json_add_time(msg);
|
||||
json_add_int(msg, "Sequence", rig->state.multicast->seqnumber++);
|
||||
json_add_string(msg, "VFOCurr", rig_strvfo(rig->state.current_vfo));
|
||||
json_add_int(msg, "Split", rig->state.cache.split);
|
||||
strcat(msg, ",\n\"VFOs\": [\n{\n");
|
||||
json_add_vfoA(rig, msg);
|
||||
|
||||
// send the thing
|
||||
multicast_send(rig, (unsigned char *)msg, strlen(msg));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *multicast_thread(void *vrig)
|
||||
{
|
||||
int retval;
|
||||
RIG *rig = (RIG *)vrig;
|
||||
rig_debug(RIG_DEBUG_TRACE, "%s: multicast_thread started\n", __func__);
|
||||
|
||||
// do the 1st packet all the time
|
||||
multicast_status_changed(rig);
|
||||
multicast_send_json(rig);
|
||||
int loopcount = 4;
|
||||
|
||||
while (rig->state.multicast->runflag)
|
||||
{
|
||||
hl_usleep(100 * 1000);
|
||||
freq_t freq, freqsave = 0;
|
||||
|
||||
if ((retval = rig_get_freq(rig, RIG_VFO_A, &freq)) != RIG_OK)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: rig_get_freq:%s\n", __func__, rigerror(retval));
|
||||
}
|
||||
|
||||
if (freq != freqsave || loopcount-- == 0)
|
||||
{
|
||||
multicast_status_changed(rig);
|
||||
multicast_send_json(rig);
|
||||
loopcount = 4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int multicast_init(RIG *rig, char *addr, int port)
|
||||
{
|
||||
if (rig->state.multicast == NULL)
|
||||
{
|
||||
rig->state.multicast = calloc(1, sizeof(struct multicast_s));
|
||||
}
|
||||
else if (rig->state.multicast->multicast_running) { return RIG_OK; } // we only need one port
|
||||
|
||||
if (addr == NULL) { addr = RIG_MULTICAST_ADDR; }
|
||||
|
||||
if (port == 0) { port = RIG_MULTICAST_PORT; }
|
||||
|
||||
// Create a UDP socket
|
||||
rig->state.multicast->sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
if (rig->state.multicast->sock < 0)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: socket: %s\n", __func__, strerror(errno));
|
||||
return -RIG_EIO;
|
||||
}
|
||||
|
||||
// Set the SO_REUSEADDR option to allow multiple processes to use the same address
|
||||
int optval = 1;
|
||||
|
||||
if (setsockopt(rig->state.multicast->sock, SOL_SOCKET, SO_REUSEADDR, &optval,
|
||||
sizeof(optval)) < 0)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
|
||||
return -RIG_EIO;
|
||||
}
|
||||
|
||||
// Bind the socket to any available local address and the specified port
|
||||
struct sockaddr_in saddr = {0};
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
saddr.sin_port = htons(port);
|
||||
|
||||
if (bind(rig->state.multicast->sock, (struct sockaddr *)&saddr,
|
||||
sizeof(saddr)) < 0)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: bind: %s\n", __func__, strerror(errno));
|
||||
return -RIG_EIO;
|
||||
}
|
||||
|
||||
// Construct the multicast group address
|
||||
// struct ip_mreq mreq = {0};
|
||||
rig->state.multicast->mreq.imr_multiaddr.s_addr = inet_addr(addr);
|
||||
rig->state.multicast->mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// Set the multicast TTL (time-to-live) to limit the scope of the packets
|
||||
unsigned char ttl = 1;
|
||||
|
||||
if (setsockopt(rig->state.multicast->sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
|
||||
sizeof(ttl)) < 0)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
|
||||
return -RIG_EIO;
|
||||
}
|
||||
|
||||
// Join the multicast group
|
||||
if (setsockopt(rig->state.multicast->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||
&rig->state.multicast->mreq, sizeof(rig->state.multicast->mreq)) < 0)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
|
||||
return -RIG_EIO;
|
||||
}
|
||||
|
||||
// prime the dest_addr for the send routine
|
||||
rig->state.multicast->dest_addr.sin_family = AF_INET;
|
||||
rig->state.multicast->dest_addr.sin_addr.s_addr = inet_addr(addr);
|
||||
rig->state.multicast->dest_addr.sin_port = htons(port);
|
||||
|
||||
printf("starting thread\n");
|
||||
|
||||
rig->state.multicast->runflag = 1;
|
||||
pthread_create(&rig->state.multicast->threadid, NULL, multicast_thread,
|
||||
(void *)rig);
|
||||
rig->state.multicast->multicast_running = 1;
|
||||
return RIG_OK;
|
||||
}
|
||||
|
||||
void multicast_close(RIG *rig)
|
||||
{
|
||||
int retval;
|
||||
|
||||
// Leave the multicast group
|
||||
if ((retval = setsockopt(rig->state.multicast->sock, IPPROTO_IP,
|
||||
IP_DROP_MEMBERSHIP, &rig->state.multicast->mreq,
|
||||
sizeof(rig->state.multicast->mreq))) < 0)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: setsockopt: %s\n", __func__, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
// Close the socket
|
||||
if ((retval = close(rig->state.multicast->sock)))
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: close: %s\n", __func__, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
// if msglen=0 msg is assumed to be a string
|
||||
int multicast_send(RIG *rig, unsigned char *msg, int msglen)
|
||||
{
|
||||
// Construct the message to send
|
||||
if (msglen == 0) { msglen = strlen((char *)msg); }
|
||||
|
||||
// Send the message to the multicast group
|
||||
ssize_t num_bytes = sendto(rig->state.multicast->sock, msg, msglen, 0,
|
||||
(struct sockaddr *)&rig->state.multicast->dest_addr,
|
||||
sizeof(rig->state.multicast->dest_addr));
|
||||
|
||||
if (num_bytes < 0)
|
||||
{
|
||||
rig_debug(RIG_DEBUG_ERR, "%s: sendto: %s\n", __func__, strerror(errno));
|
||||
return -RIG_EIO;
|
||||
}
|
||||
else
|
||||
{
|
||||
// printf("Sent %zd bytes to multicast group %s:%d\n", num_bytes, MULTICAST_ADDR,
|
||||
// PORT);
|
||||
}
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
#define TEST
|
||||
#ifdef TEST
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
RIG *rig;
|
||||
rig_model_t myrig_model;
|
||||
rig_set_debug_level(RIG_DEBUG_NONE);
|
||||
|
||||
if (argc > 1) { myrig_model = atoi(argv[1]); }
|
||||
else
|
||||
{
|
||||
myrig_model = 1035;
|
||||
}
|
||||
|
||||
rig = rig_init(myrig_model);
|
||||
|
||||
if (rig == NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
strncpy(rig->state.rigport.pathname, "/dev/ttyUSB0", HAMLIB_FILPATHLEN - 1);
|
||||
rig->state.rigport.parm.serial.rate = 38400;
|
||||
rig_open(rig);
|
||||
multicast_init(rig, NULL, 0);
|
||||
pthread_join(rig->state.multicast->threadid, NULL);
|
||||
pthread_exit(NULL);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,47 @@
|
|||
//include <stdio.h>
|
||||
//#include <stdlib.h>
|
||||
//#include <string.h>
|
||||
//#include <errno.h>
|
||||
//#include <unistd.h>
|
||||
#include <hamlib/rig.h>
|
||||
//#include <sys/socket.h>
|
||||
//#include <netinet/in.h>
|
||||
//#include <arpa/inet.h>
|
||||
|
||||
#ifndef MULTICAST_H
|
||||
#define MULTICAST_H
|
||||
#if 0 // moved to rig.h
|
||||
struct multicast_s
|
||||
{
|
||||
int multicast_running;
|
||||
int sock;
|
||||
int seqnumber;
|
||||
int runflag; // = 0;
|
||||
struct ip_mreq mreq; // = {0};
|
||||
pthread_t threadid;
|
||||
struct sockaddr_in dest_addr; // = {0};
|
||||
};
|
||||
#endif
|
||||
|
||||
struct multicast_vfo
|
||||
{
|
||||
char *name;
|
||||
double freq;
|
||||
char *mode;
|
||||
int width;
|
||||
int widthLower;
|
||||
int widthUpper;
|
||||
unsigned char rx; // true if in rx mode
|
||||
unsigned char tx; // true in in tx mode
|
||||
};
|
||||
|
||||
struct multicast_broadcast
|
||||
{
|
||||
char *ID;
|
||||
struct multicast_vfo **vfo;
|
||||
};
|
||||
|
||||
// returns # of bytes sent
|
||||
int multicast_init(RIG *rig, char *addr, int port);
|
||||
int multicast_send(RIG *rig, unsigned char *msg, int msglen);
|
||||
#endif //MULTICAST_H
|
|
@ -0,0 +1,88 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define MULTICAST_ADDR "224.0.0.1"
|
||||
#define PORT 4532
|
||||
#define BUFFER_SIZE 1024
|
||||
|
||||
int main()
|
||||
{
|
||||
// Create a UDP socket
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
if (sock < 0)
|
||||
{
|
||||
perror("socket creation failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Set the SO_REUSEADDR option to allow multiple processes to use the same address
|
||||
int optval = 1;
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
|
||||
{
|
||||
perror("setsockopt failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Bind the socket to any available local address and the specified port
|
||||
struct sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(PORT);
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
perror("bind failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Construct the multicast group address
|
||||
struct ip_mreq mreq = {0};
|
||||
mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDR);
|
||||
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// Join the multicast group
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
||||
{
|
||||
perror("setsockopt failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Receive multicast packets in a loop
|
||||
while (1)
|
||||
{
|
||||
char buffer[BUFFER_SIZE];
|
||||
struct sockaddr_in src_addr = {0};
|
||||
socklen_t addrlen = sizeof(src_addr);
|
||||
ssize_t num_bytes = recvfrom(sock, buffer, BUFFER_SIZE - 1, 0,
|
||||
(struct sockaddr *)&src_addr, &addrlen);
|
||||
|
||||
if (num_bytes < 0)
|
||||
{
|
||||
perror("recvfrom failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
buffer[num_bytes] = '\0';
|
||||
printf("Received %zd bytes from %s:%d\n%s\n", num_bytes,
|
||||
inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port), buffer);
|
||||
}
|
||||
|
||||
// Leave the multicast group
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
||||
{
|
||||
perror("setsockopt failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Close the socket
|
||||
close(sock);
|
||||
|
||||
return 0;
|
||||
}
|
Ładowanie…
Reference in New Issue