mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 00:48:11 +01:00 
			
		
		
		
	cleanup + beginning of hermes source
This commit is contained in:
		| @@ -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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										39
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								readme.md
									
									
									
									
									
								
							| @@ -274,34 +274,41 @@ 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    | ✅              | ✅                     | ✅                         | | ||||
| | limesdr_source   | Working    | liblimesuite      | OPT_BUILD_LIMESDR_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   | ⛔              | ✅                     | ✅                         | | ||||
| | soapy_source     | Working    | soapysdr          | OPT_BUILD_SOAPY_SOURCE     | ✅              | ✅                     | ✅                         | | ||||
| | spyserver_source | Working    | -                 | OPT_BUILD_SPYSERVER_SOURCE | ✅              | ✅                     | ✅                         | | ||||
| | plutosdr_source  | Working    | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE  | ✅              | ✅                     | ✅                         | | ||||
| | 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 | ✅              | ✅                     | ✅                         | | ||||
|  | ||||
| ## 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); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user