mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-24 18:08:27 +01:00
cleanup + beginning of hermes source
This commit is contained in:
parent
8eed0fcc9c
commit
a9e8db2a24
@ -30,6 +30,7 @@ option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Dependencies:
|
||||
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF)
|
||||
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
|
||||
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
||||
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
|
||||
option(OPT_BUILD_SDRPP_SERVER_SOURCE "Build SDR++ Server Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_RFSPACE_SOURCE "Build RFspace Source Module (no dependencies required)" ON)
|
||||
@ -92,6 +93,10 @@ if (OPT_BUILD_HACKRF_SOURCE)
|
||||
add_subdirectory("source_modules/hackrf_source")
|
||||
endif (OPT_BUILD_HACKRF_SOURCE)
|
||||
|
||||
if (OPT_BUILD_HERMES_SOURCE)
|
||||
add_subdirectory("source_modules/hermes_source")
|
||||
endif (OPT_BUILD_HERMES_SOURCE)
|
||||
|
||||
if (OPT_BUILD_LIMESDR_SOURCE)
|
||||
add_subdirectory("source_modules/limesdr_source")
|
||||
endif (OPT_BUILD_LIMESDR_SOURCE)
|
||||
|
@ -1,2 +0,0 @@
|
||||
#include "new_networking.h"
|
||||
|
@ -1,96 +0,0 @@
|
||||
#pragma once
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
/*
|
||||
Ryzerth's Epic Networking Functions
|
||||
*/
|
||||
|
||||
namespace net {
|
||||
enum SocketType {
|
||||
SOCK_TYPE_TCP,
|
||||
SOCK_TYPE_UDP
|
||||
};
|
||||
|
||||
struct ReadHandler {
|
||||
int count;
|
||||
void* buf;
|
||||
void (*handle)(int count, void* buf, void* ctx);
|
||||
void* ctx;
|
||||
};
|
||||
|
||||
class SocketClass {
|
||||
public:
|
||||
SocketClass(struct addrinfo* localAddr, struct addrinfo* remoteAddr, SocketType sockType);
|
||||
~SocketClass();
|
||||
|
||||
int read(int count, void* buf, int timeout);
|
||||
int write(int count, void* buf);
|
||||
|
||||
void readAsync(int count, void* buf, void (*handle)(int count, void* buf, void* ctx), void* ctx);
|
||||
|
||||
bool isOpen();
|
||||
void close();
|
||||
|
||||
private:
|
||||
void readWorker();
|
||||
|
||||
bool open = false;
|
||||
|
||||
struct addrinfo* laddr;
|
||||
struct addrinfo* raddr;
|
||||
SocketType type;
|
||||
|
||||
std::queue<ReadHandler> readQueue;
|
||||
std::thread readThread;
|
||||
std::mutex readMtx;
|
||||
std::condition_variable readCnd;
|
||||
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<SocketClass> Socket;
|
||||
|
||||
namespace tcp {
|
||||
struct AcceptHandler {
|
||||
void (*handle)(Socket client, void* ctx);
|
||||
void* ctx;
|
||||
};
|
||||
|
||||
class ListenerClass {
|
||||
public:
|
||||
ListenerClass(struct addrinfo* addr);
|
||||
~ListenerClass();
|
||||
|
||||
Socket accept(int count, void* buf, int timeout);
|
||||
void acceptAsync(void (*handle)(int count, void* buf, void* ctx), void* ctx);
|
||||
|
||||
bool isOpen();
|
||||
void close();
|
||||
|
||||
private:
|
||||
void acceptWorker();
|
||||
|
||||
bool open = false;
|
||||
|
||||
struct addrinfo* addr;
|
||||
|
||||
std::queue<AcceptHandler> acceptQueue;
|
||||
std::thread acceptThread;
|
||||
std::mutex acceptMtx;
|
||||
std::condition_variable acceptCnd;
|
||||
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ListenerClass> Listener;
|
||||
|
||||
Socket connect(std::string host, int port);
|
||||
Listener listen(std::string host, int port);
|
||||
}
|
||||
|
||||
namespace udp {
|
||||
Socket open(std::string remoteHost, int remotePort, std::string localHost = "", int localPort = -1);
|
||||
}
|
||||
}
|
15
readme.md
15
readme.md
@ -275,33 +275,40 @@ Modules in beta are still included in releases for the most part but not enabled
|
||||
## Sources
|
||||
|
||||
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|
||||
|------------------|------------|-------------------|----------------------------|:---------------:|:-----------------------:|:---------------------------:|
|
||||
|---------------------|------------|-------------------|----------------------------|:---------------:|:-----------------------:|:---------------------------:|
|
||||
| airspy_source | Working | libairspy | OPT_BUILD_AIRSPY_SOURCE | ✅ | ✅ | ✅ |
|
||||
| airspyhf_source | Working | libairspyhf | OPT_BUILD_AIRSPYHF_SOURCE | ✅ | ✅ | ✅ |
|
||||
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ⚠️ (not Debian Buster) | ✅ |
|
||||
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
||||
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
|
||||
| hermes_source | Unfinished | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
|
||||
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
|
||||
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
|
||||
| rfspace_source | Working | - | OPT_BUILD_RFSPACE_SOURCE | ✅ | ✅ | ✅ |
|
||||
| rtl_sdr_source | Working | librtlsdr | OPT_BUILD_RTL_SDR_SOURCE | ✅ | ✅ | ✅ |
|
||||
| rtl_tcp_source | Working | - | OPT_BUILD_RTL_TCP_SOURCE | ✅ | ✅ | ✅ |
|
||||
| sdrplay_source | Working | SDRplay API | OPT_BUILD_SDRPLAY_SOURCE | ⛔ | ✅ | ✅ |
|
||||
| sdrpp_server_source | Working | - | OPT_BUILD_SPYSERVER_SOURCE | ✅ | ✅ | ✅ |
|
||||
| soapy_source | Working | soapysdr | OPT_BUILD_SOAPY_SOURCE | ✅ | ✅ | ✅ |
|
||||
| spyserver_source | Working | - | OPT_BUILD_SPYSERVER_SOURCE | ✅ | ✅ | ✅ |
|
||||
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
|
||||
|
||||
## Sinks
|
||||
|
||||
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|
||||
|--------------------|------------|--------------|------------------------------|:---------------:|:----------------:|:---------------------------:|
|
||||
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
|
||||
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
|
||||
| new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
| portaudio_sink | Beta | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
|
||||
## Decoders
|
||||
|
||||
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|
||||
|---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:|
|
||||
| dmr_decoder | Unfinished | - | OPT_BUILD_DMR_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
| falcon9_decoder | Unfinished | ffplay | OPT_BUILD_FALCON9_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
| kgsstv_decoder | Unfinished | - | OPT_BUILD_KGSSTV_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
| m17_decoder | Beta | - | OPT_BUILD_M17_DECODER | ⛔ | ✅ | ⛔ |
|
||||
| meteor_demodulator | Working | - | OPT_BUILD_METEOR_DEMODULATOR | ✅ | ✅ | ⛔ |
|
||||
| radio | Working | - | OPT_BUILD_RADIO | ✅ | ✅ | ✅ |
|
||||
@ -314,8 +321,10 @@ Modules in beta are still included in releases for the most part but not enabled
|
||||
| discord_integration | Working | - | OPT_BUILD_DISCORD_PRESENCE | ✅ | ✅ | ⛔ |
|
||||
| frequency_manager | Working | - | OPT_BUILD_FREQUENCY_MANAGER | ✅ | ✅ | ✅ |
|
||||
| recorder | Working | - | OPT_BUILD_RECORDER | ✅ | ✅ | ✅ |
|
||||
| rigctl_server | Working | - | OPT_BUILD_RIGCTL_SERVER | ✅ | ✅ | ⛔ |
|
||||
| rigctl_client | Unfinished | - | OPT_BUILD_RIGCTL_CLIENT | ⛔ | ⛔ | ⛔ |
|
||||
| rigctl_server | Working | - | OPT_BUILD_RIGCTL_SERVER | ✅ | ✅ | ✅ |
|
||||
| scanner | Beta | - | OPT_BUILD_SCANNER | ✅ | ✅ | ✅ |
|
||||
| scheduler | Unfinished | - | OPT_BUILD_SCHEDULER | ⛔ | ⛔ | ⛔ |
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
|
25
source_modules/hermes_source/CMakeLists.txt
Normal file
25
source_modules/hermes_source/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(hermes_source)
|
||||
|
||||
file(GLOB SRC "src/*.cpp")
|
||||
|
||||
add_library(hermes_source SHARED ${SRC})
|
||||
target_link_libraries(hermes_source PRIVATE sdrpp_core)
|
||||
set_target_properties(hermes_source PROPERTIES PREFIX "")
|
||||
|
||||
target_include_directories(hermes_source PRIVATE "src/")
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(hermes_source PRIVATE /O2 /Ob2 /std:c++17 /EHsc)
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
target_compile_options(hermes_source PRIVATE -O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup)
|
||||
else ()
|
||||
target_compile_options(hermes_source PRIVATE -O3 -std=c++17)
|
||||
endif ()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(hermes_source PRIVATE wsock32 ws2_32)
|
||||
endif()
|
||||
|
||||
# Install directives
|
||||
install(TARGETS hermes_source DESTINATION lib/sdrpp/plugins)
|
203
source_modules/hermes_source/src/hermes.cpp
Normal file
203
source_modules/hermes_source/src/hermes.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include "hermes.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace hermes {
|
||||
Client::Client(std::shared_ptr<net::Socket> sock) {
|
||||
this->sock = sock;
|
||||
|
||||
// Start worker
|
||||
workerThread = std::thread(&Client::worker, this);
|
||||
}
|
||||
|
||||
void Client::close() {
|
||||
if (!open) { return; }
|
||||
sock->close();
|
||||
|
||||
// Wait for worker to exit
|
||||
if (workerThread.joinable()) { workerThread.join(); }
|
||||
|
||||
open = false;
|
||||
}
|
||||
|
||||
void Client::start() {
|
||||
sendMetisControl((MetisControl)(METIS_CTRL_IQ | METIS_CTRL_NO_WD));
|
||||
}
|
||||
|
||||
void Client::stop() {
|
||||
sendMetisControl(METIS_CTRL_NONE);
|
||||
}
|
||||
|
||||
void Client::setFrequency(double freq) {
|
||||
writeReg(HL_REG_TX1_NCO_FREQ, freq);
|
||||
}
|
||||
|
||||
void Client::setGain(int gain) {
|
||||
writeReg(HL_REG_RX_LNA, gain | (1 << 6));
|
||||
}
|
||||
|
||||
void Client::sendMetisUSB(uint8_t endpoint, void* frame0, void* frame1) {
|
||||
// Build packet
|
||||
uint32_t seq = usbSeq++;
|
||||
MetisUSBPacket pkt;
|
||||
pkt.hdr.signature = htons(HERMES_METIS_SIGNATURE);
|
||||
pkt.hdr.type = METIS_PKT_USB;
|
||||
pkt.endpoint = endpoint;
|
||||
pkt.seq = htonl(seq);
|
||||
if (frame0) { memcpy(pkt.frame[0], frame0, 512); }
|
||||
else { memset(pkt.frame[0], 0, 512); }
|
||||
if (frame1) { memcpy(pkt.frame[1], frame1, 512); }
|
||||
else { memset(pkt.frame[1], 0, 512); }
|
||||
|
||||
// Send packet
|
||||
sock->send((uint8_t*)&pkt, sizeof(pkt));
|
||||
}
|
||||
|
||||
void Client::sendMetisControl(MetisControl ctrl) {
|
||||
// Build packet
|
||||
MetisControlPacket pkt;
|
||||
pkt.hdr.signature = htons(HERMES_METIS_SIGNATURE);
|
||||
pkt.hdr.type = METIS_PKT_CONTROL;
|
||||
pkt.ctrl = ctrl;
|
||||
|
||||
// Send packet
|
||||
sock->send((uint8_t*)&pkt, sizeof(pkt));
|
||||
}
|
||||
|
||||
uint32_t Client::readReg(uint8_t addr) {
|
||||
uint8_t frame[512];
|
||||
memset(frame, 0, sizeof(frame));
|
||||
|
||||
HPSDRUSBHeader* hdr = (HPSDRUSBHeader*)frame;
|
||||
hdr->sync[0] = HERMES_HPSDR_USB_SYNC;
|
||||
hdr->sync[1] = HERMES_HPSDR_USB_SYNC;
|
||||
hdr->sync[2] = HERMES_HPSDR_USB_SYNC;
|
||||
hdr->c0 = (addr << 1) | (1 << 7);
|
||||
|
||||
sendMetisUSB(2, frame);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::writeReg(uint8_t addr, uint32_t val) {
|
||||
uint8_t frame[512];
|
||||
memset(frame, 0, sizeof(frame));
|
||||
|
||||
HPSDRUSBHeader* hdr = (HPSDRUSBHeader*)frame;
|
||||
hdr->sync[0] = HERMES_HPSDR_USB_SYNC;
|
||||
hdr->sync[1] = HERMES_HPSDR_USB_SYNC;
|
||||
hdr->sync[2] = HERMES_HPSDR_USB_SYNC;
|
||||
hdr->c0 = addr << 1;
|
||||
*(uint32_t*)hdr->c = htonl(val);
|
||||
|
||||
sendMetisUSB(2, frame);
|
||||
}
|
||||
|
||||
void Client::worker() {
|
||||
uint8_t rbuf[2048];
|
||||
MetisUSBPacket* pkt = (MetisUSBPacket*)rbuf;
|
||||
while (true) {
|
||||
// Wait for a packet or exit if connection closed
|
||||
int len = sock->recv(rbuf, 2048);
|
||||
if (len <= 0) { break; }
|
||||
|
||||
// Ignore anything that's not a USB packet
|
||||
if (htons(pkt->hdr.signature) != HERMES_METIS_SIGNATURE || pkt->hdr.type != METIS_PKT_USB) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse frames
|
||||
for (int frn = 0; frn < 2; frn++) {
|
||||
uint8_t* frame = pkt->frame[frn];
|
||||
HPSDRUSBHeader* hdr = (HPSDRUSBHeader*)frame;
|
||||
|
||||
// Make sure this is a valid frame by checking the sync
|
||||
if (hdr->sync[0] != 0x7F || hdr->sync[1] != 0x7F || hdr->sync[2] != 0x7F) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this is a response
|
||||
if (hdr->c0 & (1 << 7)) {
|
||||
uint8_t reg = (hdr->c0 >> 1) & 0x3F;
|
||||
spdlog::warn("Got response! Reg={0}, Seq={1}", reg, htonl(pkt->seq));
|
||||
}
|
||||
|
||||
// Decode and send IQ to stream
|
||||
// TODO: More efficient way?
|
||||
uint8_t* iq = &frame[8];
|
||||
for (int i = 0; i < 63; i++) {
|
||||
// Convert to 32bit
|
||||
int32_t si = ((uint32_t)iq[(i*8) + 0] << 16) | ((uint32_t)iq[(i*8) + 1] << 8) | (uint32_t)iq[(i*8) + 2];
|
||||
int32_t sq = ((uint32_t)iq[(i*8) + 3] << 16) | ((uint32_t)iq[(i*8) + 4] << 8) | (uint32_t)iq[(i*8) + 5];
|
||||
|
||||
// Sign extend
|
||||
si = (si << 8) >> 8;
|
||||
sq = (sq << 8) >> 8;
|
||||
|
||||
// Convert to float (IQ swapper for some reason... I means in-phase :facepalm:)
|
||||
out.writeBuf[i].im = (float)si / (float)0x1000000;
|
||||
out.writeBuf[i].re = (float)sq / (float)0x1000000;
|
||||
}
|
||||
out.swap(63);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Info> discover() {
|
||||
auto sock = net::openudp("192.168.0.255", 1024);
|
||||
|
||||
// Build discovery packet
|
||||
uint8_t discoveryPkt[64];
|
||||
memset(discoveryPkt, 0, sizeof(discoveryPkt));
|
||||
*(uint16_t*)&discoveryPkt[0] = htons(HERMES_METIS_SIGNATURE);
|
||||
discoveryPkt[2] = METIS_PKT_DISCOVER;
|
||||
|
||||
// Send the packet 5 times to make sure it's received
|
||||
for (int i = 0; i < HERMES_DISCOVER_REPEAT; i++) {
|
||||
sock->send(discoveryPkt, sizeof(discoveryPkt));
|
||||
}
|
||||
|
||||
std::vector<Info> devices;
|
||||
|
||||
while (true) {
|
||||
// Wait for a response
|
||||
uint8_t resp[1024];
|
||||
int len = sock->recv(resp, sizeof(resp), false, HERMES_DISCOVER_TIMEOUT);
|
||||
|
||||
// Give up if timeout or error
|
||||
if (len <= 0) { break; }
|
||||
|
||||
// Verify that it is a valid response
|
||||
if (len < 60) { continue; }
|
||||
if (resp[0] != 0xEF || resp[1] != 0xFE) { continue; }
|
||||
|
||||
// Analyze
|
||||
Info info;
|
||||
memcpy(info.mac, &resp[3], 6);
|
||||
info.gatewareVerMaj = resp[0x09];
|
||||
info.gatewareVerMin = resp[0x15];
|
||||
|
||||
// Check if the device is already in the list
|
||||
bool found = false;
|
||||
for (const auto& d : devices) {
|
||||
if (!memcmp(info.mac, d.mac, 6)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) { continue; }
|
||||
|
||||
devices.push_back(info);
|
||||
}
|
||||
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
std::shared_ptr<Client> open(std::string host, int port) {
|
||||
// Open UDP socket
|
||||
auto sock = net::openudp(host, port);
|
||||
|
||||
// TODO: Check if open successful
|
||||
return std::make_shared<Client>(sock);
|
||||
}
|
||||
}
|
148
source_modules/hermes_source/src/hermes.h
Normal file
148
source_modules/hermes_source/src/hermes.h
Normal file
@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
#include "net.h"
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#define HERMES_DISCOVER_REPEAT 5
|
||||
#define HERMES_DISCOVER_TIMEOUT 1000
|
||||
#define HERMES_METIS_SIGNATURE 0xEFFE
|
||||
#define HERMES_HPSDR_USB_SYNC 0x7F
|
||||
|
||||
namespace hermes {
|
||||
enum MetisPacketType {
|
||||
METIS_PKT_USB = 0x01,
|
||||
METIS_PKT_DISCOVER = 0x02,
|
||||
METIS_PKT_CONTROL = 0x04
|
||||
};
|
||||
|
||||
enum MetisControl {
|
||||
METIS_CTRL_NONE = 0,
|
||||
METIS_CTRL_IQ = (1 << 0),
|
||||
METIS_CTRL_WIDEBAND = (1 << 1),
|
||||
METIS_CTRL_NO_WD = (1 << 7)
|
||||
};
|
||||
|
||||
enum BoardID {
|
||||
BOARD_ID_HERMES_EMUL = 1,
|
||||
BOARD_ID_HL2 = 6
|
||||
};
|
||||
|
||||
struct Info {
|
||||
uint8_t mac[6];
|
||||
uint8_t gatewareVerMaj;
|
||||
uint8_t gatewareVerMin;
|
||||
BoardID boardId;
|
||||
};
|
||||
|
||||
enum HermesLiteReg {
|
||||
HL_REG_TX1_NCO_FREQ = 0x01,
|
||||
HL_REG_RX1_NCO_FREQ = 0x02,
|
||||
HL_REG_RX2_NCO_FREQ = 0x03,
|
||||
HL_REG_RX3_NCO_FREQ = 0x04,
|
||||
HL_REG_RX4_NCO_FREQ = 0x05,
|
||||
HL_REG_RX5_NCO_FREQ = 0x06,
|
||||
HL_REG_RX6_NCO_FREQ = 0x07,
|
||||
HL_REG_RX7_NCO_FREQ = 0x08,
|
||||
|
||||
HL_REG_RX_LNA = 0x0A,
|
||||
HL_REG_TX_LNA = 0x0E,
|
||||
HL_REG_CWX = 0x0F,
|
||||
HL_REG_CW_HANG_TIME = 0x10,
|
||||
|
||||
HL_REG_RX8_NCO_FREQ = 0x12,
|
||||
HL_REG_RX9_NCO_FREQ = 0x13,
|
||||
HL_REG_RX10_NCO_FREQ = 0x14,
|
||||
HL_REG_RX11_NCO_FREQ = 0x15,
|
||||
HL_REG_RX12_NCO_FREQ = 0x16,
|
||||
|
||||
HL_REG_PREDISTORTION = 0x2B,
|
||||
|
||||
HL_REG_RESET_ON_DCNT = 0x3A,
|
||||
HL_REG_AD9866_SPI = 0x3B,
|
||||
HL_REG_I2C_1 = 0x3C,
|
||||
HL_REG_I2C_2 = 0x3D,
|
||||
HL_REG_ERROR = 0x3F,
|
||||
};
|
||||
|
||||
enum HermesLiteSamplerate {
|
||||
HL_SAMP_RATE_48KHZ = 0,
|
||||
HL_SAMP_RATE_96KHZ = 1,
|
||||
HL_SAMP_RATE_192KHZ = 2,
|
||||
HL_SAMP_RATE_384KHZ = 3
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct HPSDRUSBHeader {
|
||||
uint8_t sync[3];
|
||||
uint8_t c0;
|
||||
uint8_t c[4];
|
||||
};
|
||||
|
||||
struct MetisPacketHeader {
|
||||
uint16_t signature;
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
struct MetisUSBPacket {
|
||||
MetisPacketHeader hdr;
|
||||
uint8_t endpoint;
|
||||
uint32_t seq;
|
||||
uint8_t frame[2][512];
|
||||
};
|
||||
|
||||
struct MetisControlPacket {
|
||||
MetisPacketHeader hdr;
|
||||
uint8_t ctrl;
|
||||
uint8_t rsvd[60];
|
||||
};
|
||||
|
||||
// struct MetisDiscoverPacket {
|
||||
// MetisPacketHeader hdr;
|
||||
// union {
|
||||
// {
|
||||
// uint16_t mac[6];
|
||||
// uint8_t gatewareMajVer;
|
||||
// uint8_t boardId;
|
||||
// };
|
||||
// uint8_t rsvd[60];
|
||||
// };
|
||||
// };
|
||||
#pragma pack(pop)
|
||||
|
||||
class Client {
|
||||
public:
|
||||
Client(std::shared_ptr<net::Socket> sock);
|
||||
|
||||
void close();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void setFrequency(double freq);
|
||||
void setGain(int gain);
|
||||
|
||||
dsp::stream<dsp::complex_t> out;
|
||||
|
||||
//private:
|
||||
void sendMetisUSB(uint8_t endpoint, void* frame0, void* frame1 = NULL);
|
||||
void sendMetisControl(MetisControl ctrl);
|
||||
|
||||
uint32_t readReg(uint8_t addr);
|
||||
void writeReg(uint8_t addr, uint32_t val);
|
||||
|
||||
void worker();
|
||||
|
||||
bool open = true;
|
||||
|
||||
std::thread workerThread;
|
||||
std::shared_ptr<net::Socket> sock;
|
||||
uint32_t usbSeq = 0;
|
||||
|
||||
};
|
||||
|
||||
std::vector<Info> discover();
|
||||
std::shared_ptr<Client> open(std::string host, int port);
|
||||
}
|
201
source_modules/hermes_source/src/main.cpp
Normal file
201
source_modules/hermes_source/src/main.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
#include "hermes.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <config.h>
|
||||
#include <gui/smgui.h>
|
||||
#include <gui/widgets/stepped_slider.h>
|
||||
#include <dsp/routing/stream_link.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "hermes_source",
|
||||
/* Description: */ "Hermes Lite 2 source module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ 1
|
||||
};
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
class HermesSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
HermesSourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
lnk.init(NULL, &stream);
|
||||
|
||||
sampleRate = 384000.0;
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
handler.deselectHandler = menuDeselected;
|
||||
handler.menuHandler = menuHandler;
|
||||
handler.startHandler = start;
|
||||
handler.stopHandler = stop;
|
||||
handler.tuneHandler = tune;
|
||||
handler.stream = &stream;
|
||||
|
||||
refresh();
|
||||
|
||||
// TODO: Select device
|
||||
|
||||
sigpath::sourceManager.registerSource("Hermes", &handler);
|
||||
}
|
||||
|
||||
~HermesSourceModule() {
|
||||
stop(this);
|
||||
sigpath::sourceManager.unregisterSource("Hermes");
|
||||
}
|
||||
|
||||
void postInit() {}
|
||||
|
||||
enum AGCMode {
|
||||
AGC_MODE_OFF,
|
||||
AGC_MODE_LOW,
|
||||
AGC_MODE_HIGG
|
||||
};
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
devList.clear();
|
||||
devListTxt = "";
|
||||
|
||||
// TOOD: Update dev list
|
||||
}
|
||||
|
||||
// TODO: Implement select functions
|
||||
|
||||
private:
|
||||
static void menuSelected(void* ctx) {
|
||||
HermesSourceModule* _this = (HermesSourceModule*)ctx;
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
spdlog::info("HermesSourceModule '{0}': Menu Select!", _this->name);
|
||||
}
|
||||
|
||||
static void menuDeselected(void* ctx) {
|
||||
HermesSourceModule* _this = (HermesSourceModule*)ctx;
|
||||
spdlog::info("HermesSourceModule '{0}': Menu Deselect!", _this->name);
|
||||
}
|
||||
|
||||
static void start(void* ctx) {
|
||||
HermesSourceModule* _this = (HermesSourceModule*)ctx;
|
||||
if (_this->running) { return; }
|
||||
|
||||
// TODO: Implement start
|
||||
_this->dev = hermes::open("192.168.0.144", 1024);
|
||||
|
||||
// TODO: STOP USING A LINK, FIND A BETTER WAY
|
||||
_this->lnk.setInput(&_this->dev->out);
|
||||
_this->lnk.start();
|
||||
_this->dev->start();
|
||||
|
||||
// TODO: Check if the USB commands are accepted before start
|
||||
_this->dev->setFrequency(_this->freq);
|
||||
_this->dev->setGain(_this->gain);
|
||||
_this->dev->writeReg(0, 3 << 24);
|
||||
|
||||
_this->running = true;
|
||||
spdlog::info("HermesSourceModule '{0}': Start!", _this->name);
|
||||
}
|
||||
|
||||
static void stop(void* ctx) {
|
||||
HermesSourceModule* _this = (HermesSourceModule*)ctx;
|
||||
if (!_this->running) { return; }
|
||||
_this->running = false;
|
||||
|
||||
// TODO: Implement stop
|
||||
_this->dev->stop();
|
||||
_this->dev->close();
|
||||
_this->lnk.stop();
|
||||
|
||||
spdlog::info("HermesSourceModule '{0}': Stop!", _this->name);
|
||||
}
|
||||
|
||||
static void tune(double freq, void* ctx) {
|
||||
HermesSourceModule* _this = (HermesSourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
// TODO: Check if dev exists
|
||||
_this->dev->setFrequency(_this->freq);
|
||||
}
|
||||
_this->freq = freq;
|
||||
spdlog::info("HermesSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||
}
|
||||
|
||||
static void menuHandler(void* ctx) {
|
||||
HermesSourceModule* _this = (HermesSourceModule*)ctx;
|
||||
|
||||
if (_this->running) { SmGui::BeginDisabled(); }
|
||||
|
||||
// TODO: Device selection
|
||||
if (SmGui::Button("Discover")) {
|
||||
auto disc = hermes::discover();
|
||||
spdlog::warn("Found {0} devices!", disc.size());
|
||||
}
|
||||
|
||||
if (_this->running) { SmGui::EndDisabled(); }
|
||||
|
||||
// TODO: Device parameters
|
||||
|
||||
if (SmGui::SliderInt("Gain##hermes_source", &_this->gain, 0, 60)) {
|
||||
_this->dev->setGain(_this->gain);
|
||||
}
|
||||
|
||||
if (SmGui::Button("Hermes Test")) {
|
||||
_this->dev->readReg(hermes::HL_REG_RX1_NCO_FREQ);
|
||||
}
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
dsp::routing::StreamLink<dsp::complex_t> lnk;
|
||||
double sampleRate;
|
||||
SourceManager::SourceHandler handler;
|
||||
bool running = false;
|
||||
double freq;
|
||||
|
||||
int gain = 0;
|
||||
|
||||
std::shared_ptr<hermes::Client> dev;
|
||||
|
||||
std::vector<uint64_t> devList;
|
||||
std::string devListTxt;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
json def = json({});
|
||||
def["devices"] = json({});
|
||||
def["device"] = "";
|
||||
config.setPath(core::args["root"].s() + "/hermes_config.json");
|
||||
config.load(def);
|
||||
config.enableAutoSave();
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new HermesSourceModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
delete (HermesSourceModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
294
source_modules/hermes_source/src/net.cpp
Normal file
294
source_modules/hermes_source/src/net.cpp
Normal file
@ -0,0 +1,294 @@
|
||||
#include "net.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WOULD_BLOCK (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
#else
|
||||
#define WOULD_BLOCK (errno == EWOULDBLOCK)
|
||||
#endif
|
||||
|
||||
namespace net {
|
||||
bool _init = false;
|
||||
|
||||
// === Private functions ===
|
||||
|
||||
void init() {
|
||||
if (_init) { return; }
|
||||
#ifdef _WIN32
|
||||
// Initialize WinSock2
|
||||
WSADATA wsa;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsa)) {
|
||||
throw std::runtime_error("Could not initialize WinSock2");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// Disable SIGPIPE to avoid closing when the remote host disconnects
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
_init = true;
|
||||
}
|
||||
|
||||
bool queryHost(uint32_t* addr, std::string host) {
|
||||
hostent* ent = gethostbyname(host.c_str());
|
||||
if (!ent || !ent->h_addr_list[0]) { return false; }
|
||||
*addr = *(uint32_t*)ent->h_addr_list[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
void closeSocket(SockHandle_t sock) {
|
||||
#ifdef _WIN32
|
||||
closesocket(sock);
|
||||
#else
|
||||
shutdown(sock, SHUT_RDWR);
|
||||
close(sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
// === Socket functions ===
|
||||
|
||||
Socket::Socket(SockHandle_t sock, struct sockaddr_in* raddr) {
|
||||
this->sock = sock;
|
||||
if (raddr) {
|
||||
this->raddr = (struct sockaddr_in*)malloc(sizeof(sockaddr_in));
|
||||
memcpy(this->raddr, raddr, sizeof(sockaddr_in));
|
||||
}
|
||||
}
|
||||
|
||||
Socket::~Socket() {
|
||||
close();
|
||||
if (raddr) { free(raddr); }
|
||||
}
|
||||
|
||||
void Socket::close() {
|
||||
if (!open) { return; }
|
||||
open = false;
|
||||
closeSocket(sock);
|
||||
}
|
||||
|
||||
bool Socket::isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
SocketType Socket::type() {
|
||||
return raddr ? SOCKET_TYPE_UDP : SOCKET_TYPE_TCP;
|
||||
}
|
||||
|
||||
int Socket::send(const uint8_t* data, size_t len) {
|
||||
return sendto(sock, (const char*)data, len, 0, (sockaddr*)raddr, sizeof(sockaddr_in));
|
||||
}
|
||||
|
||||
int Socket::sendstr(const std::string& str) {
|
||||
return send((const uint8_t*)str.c_str(), str.length());
|
||||
}
|
||||
|
||||
int Socket::recv(uint8_t* data, size_t maxLen, bool forceLen, int timeout) {
|
||||
// Create FD set
|
||||
fd_set set;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(sock, &set);
|
||||
|
||||
// Define timeout
|
||||
timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeout * 1000;
|
||||
|
||||
int read = 0;
|
||||
bool blocking = (timeout != NONBLOCKING);
|
||||
do {
|
||||
// Wait for data or error if
|
||||
if (blocking) {
|
||||
int err = select(sock+1, &set, NULL, &set, (timeout > 0) ? &tv : NULL);
|
||||
if (err <= 0) { return err; }
|
||||
}
|
||||
|
||||
// Receive
|
||||
int err = ::recv(sock, (char*)&data[read], maxLen - read, 0);
|
||||
if (err <= 0 && !WOULD_BLOCK) {
|
||||
close();
|
||||
return err;
|
||||
}
|
||||
read += err;
|
||||
}
|
||||
while (blocking && forceLen && read < maxLen);
|
||||
return read;
|
||||
}
|
||||
|
||||
int Socket::recvline(std::string& str, int maxLen, int timeout) {
|
||||
// Disallow nonblocking mode
|
||||
if (timeout < 0) { return -1; }
|
||||
|
||||
str.clear();
|
||||
int read = 0;
|
||||
while (true) {
|
||||
char c;
|
||||
int err = recv((uint8_t*)&c, 1, timeout);
|
||||
if (err <= 0) { return err; }
|
||||
if (c == '\n') { break; }
|
||||
str += c;
|
||||
read++;
|
||||
if (maxLen && read >= maxLen) { break; }
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
// === Listener functions ===
|
||||
|
||||
Listener::Listener(SockHandle_t sock) {
|
||||
this->sock = sock;
|
||||
}
|
||||
|
||||
Listener::~Listener() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void Listener::stop() {
|
||||
closeSocket(sock);
|
||||
open = false;
|
||||
}
|
||||
|
||||
bool Listener::listening() {
|
||||
return open;
|
||||
}
|
||||
|
||||
std::shared_ptr<Socket> Listener::accept(int timeout) {
|
||||
// Create FD set
|
||||
fd_set set;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(sock, &set);
|
||||
|
||||
// Define timeout
|
||||
timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeout * 1000;
|
||||
|
||||
// Wait for data or error
|
||||
// TODO: Support non-blockign mode
|
||||
int err = select(sock+1, &set, NULL, &set, (timeout > 0) ? &tv : NULL);
|
||||
if (err <= 0) { return NULL; }
|
||||
|
||||
// Accept
|
||||
SockHandle_t s = ::accept(sock, NULL, 0);
|
||||
if (!s) {
|
||||
stop();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Enable nonblocking mode
|
||||
#ifdef _WIN32
|
||||
u_long enabled = 1;
|
||||
ioctlsocket(s, FIONBIO, &enabled);
|
||||
#else
|
||||
fcntl(s, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
return std::make_shared<Socket>(s);
|
||||
}
|
||||
|
||||
// === Creation functions ===
|
||||
|
||||
std::shared_ptr<Listener> listen(std::string host, int port) {
|
||||
// Init library if needed
|
||||
init();
|
||||
|
||||
// Get host address
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (!queryHost((uint32_t*)&addr.sin_addr.s_addr, host)) { return NULL; }
|
||||
|
||||
// Create socket
|
||||
SockHandle_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
// TODO: Support non-blockign mode
|
||||
|
||||
#ifndef _WIN32
|
||||
// Allow port reusing if the app was killed or crashed
|
||||
// and the socket is stuck in TIME_WAIT state.
|
||||
// This option has a different meaning on Windows,
|
||||
// so we use it only for non-Windows systems
|
||||
int enable = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
|
||||
closeSocket(s);
|
||||
throw std::runtime_error("Could not configure socket");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Bind socket to the port
|
||||
if (bind(s, (sockaddr*)&addr, sizeof(addr))) {
|
||||
closeSocket(s);
|
||||
throw std::runtime_error("Could not bind socket");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Enable listening
|
||||
if (::listen(s, SOMAXCONN) != 0) {
|
||||
throw std::runtime_error("Could start listening for connections");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return listener class
|
||||
return std::make_shared<Listener>(s);
|
||||
}
|
||||
|
||||
std::shared_ptr<Socket> connect(std::string host, int port) {
|
||||
// Init library if needed
|
||||
init();
|
||||
|
||||
// Get host address
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (!queryHost((uint32_t*)&addr.sin_addr.s_addr, host)) { return NULL; }
|
||||
|
||||
// Create socket
|
||||
SockHandle_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
// Connect to server
|
||||
if (::connect(s, (sockaddr*)&addr, sizeof(addr))) {
|
||||
closeSocket(s);
|
||||
throw std::runtime_error("Could not connect");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Enable nonblocking mode
|
||||
#ifdef _WIN32
|
||||
u_long enabled = 1;
|
||||
ioctlsocket(s, FIONBIO, &enabled);
|
||||
#else
|
||||
fcntl(s, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
// Return socket class
|
||||
return std::make_shared<Socket>(s);
|
||||
}
|
||||
|
||||
std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost, int lport) {
|
||||
// Init library if needed
|
||||
init();
|
||||
|
||||
// Get local address
|
||||
struct sockaddr_in laddr;
|
||||
laddr.sin_family = AF_INET;
|
||||
laddr.sin_port = htons(lport);
|
||||
if (!queryHost((uint32_t*)&laddr.sin_addr.s_addr, lhost)) { return NULL; }
|
||||
|
||||
// Get remote address
|
||||
struct sockaddr_in raddr;
|
||||
raddr.sin_family = AF_INET;
|
||||
raddr.sin_port = htons(rport);
|
||||
if (!queryHost((uint32_t*)&raddr.sin_addr.s_addr, rhost)) { return NULL; }
|
||||
|
||||
// Create socket
|
||||
SockHandle_t s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
// Bind socket to local port
|
||||
if (bind(s, (sockaddr*)&laddr, sizeof(laddr))) {
|
||||
closeSocket(s);
|
||||
throw std::runtime_error("Could not bind socket");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return socket class
|
||||
return std::make_shared<Socket>(s, &raddr);
|
||||
}
|
||||
}
|
155
source_modules/hermes_source/src/net.h
Normal file
155
source_modules/hermes_source/src/net.h
Normal file
@ -0,0 +1,155 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
namespace net {
|
||||
#ifdef _WIN32
|
||||
typedef SOCKET SockHandle_t;
|
||||
#else
|
||||
typedef int SockHandle_t;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
NO_TIMEOUT = -1,
|
||||
NONBLOCKING = 0
|
||||
};
|
||||
|
||||
enum SocketType {
|
||||
SOCKET_TYPE_TCP,
|
||||
SOCKET_TYPE_UDP
|
||||
};
|
||||
|
||||
class Socket {
|
||||
public:
|
||||
Socket(SockHandle_t sock, struct sockaddr_in* raddr = NULL);
|
||||
~Socket();
|
||||
|
||||
/**
|
||||
* Close socket. The socket can no longer be used after this.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Check if the socket is open.
|
||||
* @return True if open, false if closed.
|
||||
*/
|
||||
bool isOpen();
|
||||
|
||||
/**
|
||||
* Get socket type. Either TCP or UDP.
|
||||
* @return Socket type.
|
||||
*/
|
||||
SocketType type();
|
||||
|
||||
/**
|
||||
* Send data on socket.
|
||||
* @param data Data to be sent.
|
||||
* @param len Number of bytes to be sent.
|
||||
* @return Number of bytes sent.
|
||||
*/
|
||||
int send(const uint8_t* data, size_t len);
|
||||
|
||||
/**
|
||||
* Send string on socket. Terminating null byte is not sent, include one in the string if you need it.
|
||||
* @param str String to be sent.
|
||||
* @return Number of bytes sent.
|
||||
*/
|
||||
int sendstr(const std::string& str);
|
||||
|
||||
/**
|
||||
* Receive data from socket.
|
||||
* @param data Buffer to read the data into.
|
||||
* @param maxLen Maximum number of bytes to read.
|
||||
* @param forceLen Read the maximum number of bytes even if it requires multiple receive operations.
|
||||
* @param timeout Timeout in milliseconds. Use NO_TIMEOUT or NONBLOCKING here if needed.
|
||||
* @return Number of bytes read. 0 means timed out or closed. -1 means would block or error.
|
||||
*/
|
||||
int recv(uint8_t* data, size_t maxLen, bool forceLen = false, int timeout = NO_TIMEOUT);
|
||||
|
||||
/**
|
||||
* Receive line from socket.
|
||||
* @param str String to read the data into.
|
||||
* @param maxLen Maximum line length allowed, 0 for no limit.
|
||||
* @param timeout Timeout in milliseconds. Use NO_TIMEOUT or NONBLOCKING here if needed.
|
||||
* @return Length of the returned string. 0 means timed out or closed. -1 means would block or error.
|
||||
*/
|
||||
int recvline(std::string& str, int maxLen = 0, int timeout = NO_TIMEOUT);
|
||||
|
||||
private:
|
||||
struct sockaddr_in* raddr = NULL;
|
||||
SockHandle_t sock;
|
||||
bool open = true;
|
||||
|
||||
};
|
||||
|
||||
class Listener {
|
||||
public:
|
||||
Listener(SockHandle_t sock);
|
||||
~Listener();
|
||||
|
||||
/**
|
||||
* Stop listening. The listener can no longer be used after this.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* CHeck if the listener is still listening.
|
||||
* @return True if listening, false if not.
|
||||
*/
|
||||
bool listening();
|
||||
|
||||
/**
|
||||
* Accept connection.
|
||||
* @param timeout Timeout in milliseconds. 0 means no timeout.
|
||||
* @return Socket of the connection. NULL means timed out or closed.
|
||||
*/
|
||||
std::shared_ptr<Socket> accept(int timeout = NO_TIMEOUT);
|
||||
|
||||
private:
|
||||
SockHandle_t sock;
|
||||
bool open = true;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create TCP listener.
|
||||
* @param host Hostname or IP to listen on ("0.0.0.0" for Any).
|
||||
* @param port Port to listen on.
|
||||
* @return Listener instance on success, null otherwise.
|
||||
*/
|
||||
std::shared_ptr<Listener> listen(std::string host, int port);
|
||||
|
||||
/**
|
||||
* Create TCP connection.
|
||||
* @param host Remote hostname or IP address.
|
||||
* @param port Remote port.
|
||||
* @return Socket instance on success, null otherwise.
|
||||
*/
|
||||
std::shared_ptr<Socket> connect(std::string host, int port);
|
||||
|
||||
/**
|
||||
* Create UDP socket.
|
||||
* @param rhost Remote hostname or IP address.
|
||||
* @param rport Remote port.
|
||||
* @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any).
|
||||
* @param lpost Local port used to bind the socket (optional, 0 to allocate automatically).
|
||||
* @return Socket instance on success, null otherwise.
|
||||
*/
|
||||
std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost = "0.0.0.0", int lport = 0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user