├── Makefile ├── README.md ├── LICENSE └── hl2_tcp.c /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # hl2_tcp makefile 3 | # 4 | 5 | OS := $(shell uname) 6 | 7 | ifeq ($(OS), Darwin) 8 | CC = clang 9 | LL = -lm 10 | else 11 | ifeq ($(OS), Linux) 12 | CC = cc 13 | LL = -lm 14 | STD = -std=c99 15 | else 16 | $(error OS not detected) 17 | endif 18 | endif 19 | 20 | FILES = hl2_tcp.c 21 | 22 | hl2_tcp: $(FILES) 23 | $(CC) $(FILES) $(LL) -lpthread -Os -o hl2_tcp 24 | clean: 25 | rm hl2_tcp 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## hl2_tcp 2 | 3 | This repository contains source code for hl2_tcp, 4 | a rtl_tcp server for [Hermes Lite 2](http://www.hermeslite.com) 5 | SDR radio. 6 | 7 | The Hermes-Lite is a low-cost 8 | direct conversion 9 | software defined amateur radio HF transceiver 10 | based on the AD9866 broadband modem chip, 11 | an Altera Cyclone IV EP4CE22 FPGA, 12 | and the [HPSDR/Hermes SDR project](http://openhpsdr.org). 13 | 14 | 15 | The hl2_tcp server 16 | can connect to a Hermes Lite 2 SDR 17 | via UDP over ethernet, 18 | transcode received RF IQ samples, 19 | and then serve that data 20 | via the rtl_tcp protocol. 21 | 22 | [rtl_tcp](https://github.com/osmocom/rtl-sdr) 23 | is a command-line tool developed by OsmoCom and others, 24 | for various Realtek RTL2832-based DVB-T USB peripherals, 25 | to serve IQ samples from those USB devices over TCP. 26 | There are multiple SDR applications, 27 | available for Linux, macOS, or Wintel systems, 28 | that can connect to a local or remote SDR radio peripheral 29 | via the rtl_tcp protocol. 30 | This server allows using those SDR applications 31 | with a Hermes Lite 2 SDR, 32 | but only if the application supports the 33 | HL2 sample rates (48k, 96k, 192k, and 384k). 34 | Among the SDR applications 35 | supporting those sample rates 36 | are the iOS and macOS apps: 37 | _rtl_tcp SDR_ 38 | and 39 | _SDR Receiver_, 40 | allowing HL2 SDR reception an iPhone, iPad, or Mac. 41 | 42 | Usage: 43 | 44 | hl2_tcp -d 45 | 46 | Discovers Hermes Lite 2 IP Address. 47 | Prints random diagnostics. 48 | 49 | hl2_tcp -a Hermes_IP_Addr [-p tcp_server_port] [-b 8/16] 50 | 51 | Starts a server for the rtl_tcp protocol 52 | on a local TCP server port (default rtl_tcp port 1234) 53 | and waits for a TCP connection. 54 | Upon opening an rcp_tcp TCP connection, 55 | starts a UDP connection to the Hermes Lite 2 56 | at Hermes_IP_Addr on UDP port 1024, 57 | and transcodes OpenHPSDR/Metis UDP data to rtl_tcp TCP data. 58 | Also prints more random diagnostics. 59 | 60 | Important note: Under macOS Catalina 10.15.7 or later, or macOS Big Sur, 61 | this server may need to be installed and run from an Admin account, 62 | due to new macOS firewall security/privacy features. 63 | 64 | More information on the Hermes Lite 2 can be found on this web sites: 65 | [Hermes-Lite2 wiki](https://github.com/softerhardware/Hermes-Lite2/wiki) 66 | 67 | -- 68 | 69 | rhn@nicholson.com \ 70 | N6YWU \ 71 | http://www.nicholson.com/rhn/ 72 | 73 | License: MPL 2.0 with exhibit B \ 74 | No warrantees implied. 75 | 76 | -- 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /hl2_tcp.c: -------------------------------------------------------------------------------- 1 | // 2 | // hl2_tcp.c 3 | // 4 | #define VERSION "v.1.3.129" // 2020-06-29 01 5 | // macOS : clang -lm -lpthread -Os -o hl2_tcp hl2_tcp.c 6 | // pi : cc -lm -lpthread -Os -o hl2_tcp hl2_tcp.c 7 | // 8 | // Serves IQ data using the rtl_tcp protocol 9 | // from an Hermes Lite 2 on port 1024 10 | // to iPv6 port 1234 11 | // 12 | // initial version 2020-01-27 rhn 13 | // 14 | // Copyright 2017,2020 Ronald H Nicholson Jr. All Rights Reserved. 15 | // This code may only be redistributed under terms of 16 | // the Mozilla Public License 2.0 plus Exhibit B (no copyleft exception) 17 | // See: https://www.mozilla.org/en-US/MPL/2.0/ 18 | 19 | // #define TX_OK 20 | // #define SPECIAL_CMD_OK 21 | // #define NO_MAIN 22 | 23 | #ifdef __clang__ 24 | #endif 25 | 26 | #define TITLE ("hl2_tcp ") 27 | #define SOCKET_READ_TIMEOUT ( 30.0 * 60.0 ) // 30 minutes in seconds 28 | #define SAMPLE_BITS ( 8) // default to match rtl_tcp 29 | // #define SAMPLE_BITS (16) // default to match rsp_tcp 30 | // #define SAMPLE_BITS (32) // HPSDR 24-bit->float32 IQ data ? 31 | #define GAIN8 (4096.0) // default gain ? 32 | #define TCP_PORT (1234) // default rtp_tcp server port 33 | #define HERMES_PORT (1024) // UDP port 34 | 35 | #define RING_BUFFER_ALLOCATION (2L * 1024L * 1024L) // 2MB 36 | #define MAX_NUMBER_OF_START_COMMANDS (8) 37 | #define START_LOOP_DELAY (50000) // microseconds 38 | 39 | #define _POSIX_C_SOURCE 200112L 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | #include 63 | #include 64 | 65 | #ifndef _UNISTD_H_ 66 | int usleep(unsigned long int usec); 67 | #endif 68 | 69 | #ifndef M_PI 70 | #define M_PI (3.14159265358979323846264) 71 | typedef void *caddr_t; 72 | #endif 73 | 74 | static void sighandler(int signum); 75 | void *tcp_connection_handler(void); // main thread 76 | void *udp_rcv_thread_runner(void *param); // on a pthread 77 | 78 | int discover_main(void); 79 | void hl2_stop(void); 80 | void print_hl2_stats(void) ; 81 | void setFilterEnables(void) ; 82 | 83 | uint32_t hl2_ipAddr = 0x7f000001; // 127.0.0.1 == localhost 84 | int portno = TCP_PORT; // 85 | int hl2_running = 0; // Metis response 86 | int udp_running = 0; // udp socket live 87 | int C0_addr = 0; // 88 | volatile int hermes_rx_freq = 14100000; // 100k to 30M 89 | volatile int hermes_tx_freq = 0; // 90 | volatile int hermes_tx_offset = 0; // 91 | volatile int hermes_lna_gain = 19; // LNA gain -12 to +48 dB 92 | int hermes_tx_drive_level = 0; // 0 .. 15 93 | int hermes_enable_power_amp = 0; // 94 | int hermes_Q5_switch_ext_ptt_lp = 0; 95 | int tx_gear_ratio = 1; 96 | int tx_gear_counter = 0; 97 | volatile int seqNum = 0; // tx sequence number 98 | 99 | // Rx and Tx filters; default no HPF ? 100 | int n2adr_filter_rx = 0; 101 | int n2adr_filter_tx = 0; 102 | 103 | int sendErrorFlag = 0; 104 | int sampleBits = SAMPLE_BITS; 105 | int sampleRates = 1; 106 | long int totalSamples = 0; 107 | long sampRate = 192000; // 768000; 108 | long previousSRate = -1; 109 | int gClientSocketID = -1; 110 | 111 | char *ring_buffer_ptr = NULL; 112 | int decimateFlag = 1; 113 | int decimateCntr = 0; 114 | 115 | volatile float gain0 = GAIN8; 116 | 117 | struct sigaction sigact, sigign; 118 | 119 | int tcp_listen_sockfd = -1; 120 | static volatile int do_exit = 0; 121 | float acc_r = 0.0; // accumulated rounding 122 | float sMax = 0.0; // for debug 123 | float sMin = 0.0; 124 | int sendblockcount = 0; 125 | int threads_running = 0; 126 | 127 | char *hl2_ip_string = "127.0.0.1"; // 0x7f000001 == localhost 128 | 129 | // for TX_OK 130 | long int tcp_tx_cmd = 0; 131 | int tx_key_down = 0; // Morse code key 132 | int hl2_tx_on = 0; // tx ptt 133 | int tx_delay = 0; // tx/ptt hold time 134 | int last_key_down = 0; 135 | int tx_param_x = 0; 136 | int tx_param_w = 0; 137 | 138 | #ifdef TX_OK 139 | extern void tx_setup(); 140 | extern void tx_cleanup(); 141 | extern int get_tx_key(void) ; 142 | extern void tx_block_setup(int seqN); 143 | extern void get_tx_sample(int *tx_i, int *tx_q) ; 144 | extern int get_tx_drive(void) ; 145 | extern int get_tx_offset(); 146 | #else 147 | void tx_setup() { return; } 148 | void tx_cleanup() { return; } 149 | #define get_tx_key() (0) 150 | #define tx_block_setup(X) // nop 151 | #define get_tx_sample(X,Y) // nop 152 | #define get_tx_drive() (0) 153 | #define get_tx_offset() (0) 154 | #endif 155 | 156 | #ifdef SPECIAL_CMD_OK 157 | extern int specialCommandCounter; 158 | extern void checkForSpecialCommands(int seqN); // for special commands 159 | #else 160 | int specialCommandCounter = 0; 161 | void checkForSpecialCommands(int n) { return; } 162 | #endif 163 | 164 | #ifdef DEBUG_FILE 165 | char *dump_fname = DEBUG_FILE ; 166 | #endif 167 | 168 | char UsageString1[] 169 | = "Usage: hl2_tcp -a hermes_IPaddr [-p local_port] [-b 8/16]"; 170 | char UsageString2[] 171 | = " hl2_tcp -d "; 172 | 173 | void print_usage_and_exit() 174 | { 175 | printf("%s\n", UsageString1); 176 | printf("%s\n", UsageString2); 177 | exit(0); 178 | } 179 | 180 | int hl2_tcp(void); 181 | 182 | #ifndef NO_MAIN 183 | // int tcp_main(int argc, char *argv[]) 184 | int main(int argc, char *argv[]) 185 | { 186 | if (argc > 1) { 187 | if (strcmp(argv[1], "-d") == 0) { 188 | int e = discover_main(); 189 | return(e); 190 | } 191 | if ((argc % 2) != 1) { 192 | print_usage_and_exit(); 193 | } 194 | for (int arg=3; arg<=argc; arg+=2) { 195 | if (strcmp(argv[arg-2], "-p") == 0) { 196 | portno = atoi(argv[arg-1]); 197 | if (portno == 0) { 198 | printf("invalid port number entry %s\n", argv[arg-1]); 199 | exit(0); 200 | } 201 | } else if (strcmp(argv[arg-2], "-b") == 0) { 202 | if (strcmp(argv[arg-1],"16") == 0) { 203 | sampleBits = 16; 204 | } else if (strcmp(argv[arg-1],"8") == 0) { 205 | sampleBits = 8; 206 | } else { 207 | print_usage_and_exit(); 208 | } 209 | } else if (strcmp(argv[arg-2], "-a") == 0) { 210 | hl2_ip_string = argv[arg-1]; 211 | } else if (strcmp(argv[arg-2], "-x") == 0) { 212 | tx_param_x = atoi(argv[arg-1]); 213 | } else { 214 | print_usage_and_exit(); 215 | } 216 | } 217 | } else { 218 | print_usage_and_exit(); 219 | } 220 | 221 | return(hl2_tcp()); 222 | } 223 | #endif 224 | 225 | int hl2_tcp() 226 | { 227 | struct sockaddr_in6 serv_addr ; 228 | char client_addr_ipv6[100]; 229 | 230 | printf("hl2_tcp Version %s\n", VERSION); 231 | printf("Will look for Hermes Lite 2 at IP: %s UDP Port: %d \n", 232 | hl2_ip_string, HERMES_PORT); 233 | 234 | ring_buffer_ptr = (char *)malloc(RING_BUFFER_ALLOCATION + 4); 235 | if (ring_buffer_ptr == NULL) { exit(-1); } 236 | bzero(ring_buffer_ptr, RING_BUFFER_ALLOCATION + 2); 237 | 238 | printf("Converts Metis to rtl_tcp format %d-bit IQ samples \n", 239 | sampleBits); 240 | printf("Starting hl2_tcp server on TCP port %d\n", portno); 241 | 242 | tx_setup(); 243 | 244 | sigact.sa_handler = sighandler; 245 | sigemptyset(&sigact.sa_mask); 246 | sigact.sa_flags = 0; 247 | sigaction(SIGINT, &sigact, NULL); 248 | sigaction(SIGTERM, &sigact, NULL); 249 | sigaction(SIGQUIT, &sigact, NULL); 250 | #ifdef __APPLE__ 251 | signal(SIGPIPE, SIG_IGN); 252 | #else 253 | sigign.sa_handler = SIG_IGN; 254 | sigaction(SIGPIPE, &sigign, NULL); 255 | #endif 256 | 257 | previousSRate = sampRate; 258 | 259 | // printf("\nhl2_tcp server started on port %d\n", portno); 260 | 261 | tcp_listen_sockfd = socket(AF_INET6, SOCK_STREAM, 0); 262 | if (tcp_listen_sockfd < 0) { 263 | printf("ERROR opening socket"); 264 | return(-1); 265 | } 266 | 267 | struct linger ling = {1,0}; 268 | int rr = 1; 269 | setsockopt(tcp_listen_sockfd, SOL_SOCKET, SO_REUSEADDR, 270 | (char *)&rr, sizeof(int)); 271 | setsockopt(tcp_listen_sockfd, SOL_SOCKET, SO_LINGER, 272 | (char *)&ling, sizeof(ling)); 273 | 274 | bzero((char *) &serv_addr, sizeof(serv_addr)); 275 | serv_addr.sin6_flowinfo = 0; 276 | serv_addr.sin6_family = AF_INET6; 277 | serv_addr.sin6_addr = in6addr_any; 278 | serv_addr.sin6_port = htons(portno); 279 | 280 | // Sockets Layer Call: bind() 281 | if (bind( tcp_listen_sockfd, (struct sockaddr *)&serv_addr, 282 | sizeof(serv_addr) ) < 0) { 283 | printf("ERROR on bind to listen\n"); 284 | return(-1); 285 | } 286 | 287 | listen(tcp_listen_sockfd, 5); 288 | fprintf(stdout, "listening for socket connection \n"); 289 | 290 | while (1) { 291 | 292 | // accept a connection 293 | 294 | struct sockaddr_in6 cli_addr; 295 | socklen_t claddrlen = sizeof(cli_addr); 296 | gClientSocketID = accept( tcp_listen_sockfd, 297 | (struct sockaddr *) &cli_addr, 298 | &claddrlen ); 299 | if (gClientSocketID < 0) { 300 | printf("ERROR on accept\n"); 301 | break; 302 | } 303 | 304 | inet_ntop(AF_INET6, &(cli_addr.sin6_addr), client_addr_ipv6, 100); 305 | printf("\nConnected to client with IP address: %s\n", 306 | client_addr_ipv6); 307 | 308 | tcp_connection_handler(); 309 | 310 | printf("tcp connection ended \n"); 311 | } 312 | 313 | udp_running = -1; 314 | hl2_stop(); 315 | tx_cleanup(); 316 | 317 | fflush(stdout); 318 | return 0; 319 | } // main 320 | 321 | static void sighandler(int signum) 322 | { 323 | fprintf(stderr, "Signal caught, exiting!\n"); 324 | fflush(stderr); 325 | close(tcp_listen_sockfd); 326 | if (gClientSocketID != 0) { 327 | close(gClientSocketID); 328 | gClientSocketID = -1; 329 | } 330 | udp_running = -1; 331 | hl2_stop(); 332 | tx_cleanup(); 333 | exit(-1); 334 | // do_exit = 1; 335 | } 336 | 337 | int stop_send_thread = 0; 338 | int thread_counter = 0; 339 | int thread_running = 0; 340 | 341 | int ring_buffer_size = RING_BUFFER_ALLOCATION; 342 | volatile long int ring_wr_index = 0; 343 | volatile long int ring_rd_index = 0; 344 | 345 | void ring_init() 346 | { 347 | ring_wr_index = 0; 348 | ring_rd_index = 0; 349 | } 350 | 351 | int ring_data_available() 352 | { 353 | long int n = 0; 354 | long int w_index = ring_wr_index; // 355 | long int r_index = ring_rd_index; // 356 | n = w_index - r_index; 357 | if (n < 0) { n += ring_buffer_size; } 358 | if (n < 0) { n = 0; } // error condition ? 359 | if (n >= ring_buffer_size) { n = 0; } // error condition 360 | return(n); 361 | } 362 | 363 | int ring_write(unsigned char *from_ptr, int amount) 364 | { 365 | int wrap = 0; 366 | long int w_index = ring_wr_index; // my index 367 | long int r_index = ring_rd_index; // other threads index 368 | if ( ring_buffer_ptr == NULL ) { return(-1); } 369 | if ( (w_index < 0) 370 | || (w_index >= ring_buffer_size) ) { return(-1); } // error ! 371 | if (decimateFlag > 1) { 372 | int i; 373 | for (i = 0; i < amount; i += 2) { 374 | if (decimateCntr == 0) { 375 | ring_buffer_ptr[w_index ] = from_ptr[i ]; 376 | ring_buffer_ptr[w_index+1] = from_ptr[i+1]; 377 | w_index += 2; 378 | if (w_index >= ring_buffer_size) { w_index = 0; } 379 | } 380 | decimateCntr += 1; 381 | if (decimateCntr >= decimateFlag) { decimateCntr = 0; } 382 | } 383 | } else if (w_index + amount < ring_buffer_size) { 384 | memcpy(&ring_buffer_ptr[w_index], from_ptr, amount); 385 | w_index += amount; 386 | } else { 387 | int i; 388 | for (i = 0; i < amount; i += 1) { 389 | ring_buffer_ptr[w_index] = from_ptr[i]; 390 | w_index += 1; 391 | if (w_index >= ring_buffer_size) { w_index = 0; } 392 | } 393 | } 394 | // 395 | /// ToDo: insert memory barrier here 396 | // 397 | ring_wr_index = w_index; // update lock free input info 398 | int m = ring_data_available(); 399 | if (m > ring_buffer_size/2) { wrap = 1; } 400 | return(wrap); 401 | } 402 | 403 | int ring_read(unsigned char *to_ptr, int amount, int always) 404 | { 405 | int bytes_read = 0; 406 | long int r_index = ring_rd_index; // my index 407 | long int w_index = ring_wr_index; // other threads index 408 | long int available = w_index - r_index; 409 | if (available < 0) { available += ring_buffer_size; } 410 | if (always != 0) { 411 | bzero(to_ptr, amount); 412 | } 413 | if (available <= 0) { return(bytes_read); } 414 | long int n = amount; 415 | if (n > available) { n = available; } // min(n, available) 416 | if (r_index + n < ring_buffer_size) { 417 | memcpy(to_ptr, &ring_buffer_ptr[r_index], n); 418 | r_index += n; 419 | } else { 420 | int i; 421 | for (i = 0; i < n; i += 1) { 422 | to_ptr[i] = ring_buffer_ptr[r_index]; 423 | r_index += 1; 424 | if (r_index >= ring_buffer_size) { r_index = 0; } 425 | } 426 | } 427 | bytes_read = n; 428 | ring_rd_index = r_index; // update lock free extract info 429 | return(bytes_read); 430 | } 431 | 432 | float tmpFPBuf[4*32768]; 433 | uint8_t tmpBuf[ 4*32768]; 434 | long int *param = NULL; 435 | 436 | int tcp_send_poll() 437 | { 438 | int sz0 = 1408; // MTU size or 1008 ?? 439 | int pad = 32768 * 2; 440 | ssize_t b = 0; 441 | int send_sockfd = gClientSocketID ; 442 | 443 | if (send_sockfd > 0) { 444 | if (ring_data_available() >= (sz0 + pad)) { 445 | int sz = ring_read(tmpBuf, sz0, 0); 446 | if (sz > 0) { 447 | if (totalSamples == 0) { 448 | // fprintf(stderr, "hl2 udp IQ data received \n"); 449 | } 450 | #ifdef __APPLE__ 451 | b = send(send_sockfd, tmpBuf, sz, 0); 452 | #else 453 | b = send(send_sockfd, tmpBuf, sz, MSG_NOSIGNAL); 454 | #endif 455 | if (b <= 0) { sendErrorFlag = -1; } 456 | if (totalSamples == 0) { 457 | fprintf(stderr, 458 | "Started rtl_tcp IQ streaming with %ld bytes\n", b); 459 | } 460 | totalSamples += sz; 461 | sendblockcount += 1; 462 | } 463 | pad = 0; 464 | } 465 | } 466 | return(b); 467 | } 468 | 469 | int hl2_udp_setup(void); 470 | void hl2_send_start_cmds(int n); 471 | void hl2_udp_rcv(int loopFlag); 472 | int hl2_udp_rcv_end(void); 473 | 474 | void *tcp_connection_handler() 475 | { 476 | char buffer[256]; 477 | int m = 0; 478 | int printCmdBytes = 0; // 479 | int i; 480 | 481 | if (do_exit != 0) { return(NULL); } 482 | 483 | if (1) { // send 12 or 16-byte rtl_tcp header 484 | ssize_t b = 0; 485 | int sz = 16; 486 | if (sampleBits == 8) { sz = 12; } 487 | // "HL20" in 16 byte header 488 | char header[16] = { 0x48,0x4C,0x32,0x30, 489 | 0x30,0x30,0x30+sampleRates,0x30+sampleBits, 490 | 0,0,0,1, 0,0,0,2 }; 491 | #ifdef __APPLE__ 492 | b = send(gClientSocketID, header, sz, 0); 493 | #else 494 | b = send(gClientSocketID, header, sz, MSG_NOSIGNAL); 495 | #endif 496 | /* 497 | fprintf(stdout, "rtl_tcp header sent , %d bytes\n", n); // 498 | fflush(stdout); 499 | */ 500 | } 501 | 502 | sendErrorFlag = 0; 503 | stop_send_thread = 0; 504 | ring_wr_index = 0; 505 | ring_rd_index = 0; 506 | 507 | param = (long int *)malloc(4 * sizeof(long int)); // ToDo: fix leak 508 | for (i=0;i<4;i++) { param[i] = 0; } 509 | 510 | if (hl2_running == 0) { 511 | ring_init(); 512 | hl2_udp_setup(); 513 | hl2_send_start_cmds(1); 514 | 515 | pthread_t udp_rcv_thread; 516 | if ( pthread_create( &udp_rcv_thread, 517 | NULL , 518 | udp_rcv_thread_runner, 519 | (void *)param ) < 0 ) { 520 | printf("could not create udp streaming thread"); 521 | return(NULL); 522 | } else { 523 | printf("udp streaming thread started \n"); 524 | } 525 | 526 | int n = MAX_NUMBER_OF_START_COMMANDS; 527 | hl2_send_start_cmds(n); 528 | } 529 | 530 | acc_r = 0.0; 531 | totalSamples = 0; 532 | // printf("hl2 start status = %d\n", m); 533 | if (m < 0) { exit(-1); } 534 | usleep(250L * 1000L); 535 | 536 | // set a timeout so receive call won't block forever 537 | struct timeval timeout; 538 | timeout.tv_sec = SOCKET_READ_TIMEOUT; // seconds 539 | timeout.tv_usec = 0; 540 | setsockopt( gClientSocketID, SOL_SOCKET, SO_RCVTIMEO, 541 | &timeout, sizeof(timeout) ); 542 | 543 | ssize_t b = 1; 544 | while ((b > 0) && (sendErrorFlag == 0)) { 545 | int i, j; 546 | // receive 5 byte commands (or a multiple thereof) 547 | memset(buffer,0, 256); 548 | b = recv(gClientSocketID, buffer, 255, 0); 549 | if ((b <= 0) || (sendErrorFlag != 0)) { 550 | udp_running = -1; 551 | close(gClientSocketID); 552 | gClientSocketID = -1; 553 | // fprintf(stdout, "hl2 stop status = %d\n", m); 554 | fflush(stdout); 555 | break; 556 | } 557 | if (b > 0) { 558 | for (i=0; i < b; i+=5) { 559 | // decode 5 byte rtl_tcp command messages 560 | int msg = 0x00ff & buffer[i]; 561 | if (printCmdBytes) { printf("0x%02x ", msg); } 562 | int data = 0; 563 | for (j=1;j<5;j++) { 564 | int byte = (0x00ff & buffer[i+j]); 565 | data = (256 * data) + byte; 566 | if (printCmdBytes) { printf("0x%02x ", byte); } 567 | } 568 | if (printCmdBytes) { printf(" = %d\n", data); } 569 | 570 | if (msg == 0x01) { // set frequency 571 | int f0 = data; 572 | if (f0 >= 100000 && f0 <= 30000000) { 573 | hermes_rx_freq = f0; // hl2 574 | setFilterEnables(); 575 | hermes_tx_offset = get_tx_offset(); 576 | } 577 | fprintf(stdout, "setting frequency to: %d\n", f0); 578 | } else if (msg == 0x02) { // set sample rate 579 | int r = data; 580 | if ( (r == 48000) 581 | || (r == 96000) 582 | || (r == 192000) 583 | || (r == 384000)) { 584 | // if (r != previousSRate) { 585 | sampRate = r; 586 | tx_gear_ratio = r / 48000; 587 | printf("setting samplerate to : %d\n", r); 588 | // } 589 | } else { 590 | // ignore 591 | } 592 | } else if (msg == 0x03) { // other 593 | fprintf(stdout, "message = %d, data = %d\n", msg, data); 594 | } else if (msg == 0x04) { // gain 595 | if ( (sampleBits == 8) 596 | || (sampleBits == 16) ) { 597 | // set gain 598 | // hermes_lna_gain : LNA gain -12 to +48 dB 599 | // 600 | float g1 = data; // data : in 10th dB's 601 | float g2 = 0.1 * (float)(data); // undo 10ths 602 | // fprintf(stdout, "setting gain to: %f dB\n", g2); 603 | float g2h = g2 * 60.0 / 40.0 ; 604 | int g9 = roundf(g2h - 12.0); 605 | hermes_lna_gain = g9; 606 | fprintf(stdout, "set hl2 lna gain to : %d\n", g9); 607 | 608 | if (sampleBits == 16) { 609 | gain0 = GAIN8 * 0.125; 610 | } else { 611 | gain0 = GAIN8 * 8.0; 612 | } 613 | } 614 | #ifdef TX_OK 615 | } else if (msg == 77) { // 0x4d tx command extension 616 | tcp_tx_cmd = 3 * data; // 381 = 127 * 3 617 | // printf("tcp_tx_cmd = %d \n", tcp_tx_cmd); // 618 | #endif 619 | } else { // other 620 | fprintf(stdout, "message = %d, data = %d\n", msg, data); 621 | if (msg == 8) { 622 | fprintf(stdout, "set agc mode ignored\n"); 623 | } 624 | } 625 | } 626 | } 627 | if (b < 0) { 628 | fprintf(stdout, "read socket timeout %ld \n", b); 629 | fflush(stdout); 630 | } 631 | // loop until error (socket close) or timeout 632 | } ; 633 | 634 | if (m) { 635 | fprintf(stdout,"stopping now 00 \n"); 636 | printf("hl2 stop status = %d\n", m); 637 | } 638 | udp_running = -1; 639 | 640 | close(gClientSocketID); 641 | gClientSocketID = -1; 642 | return(param); 643 | } // tcp_connection_handler() 644 | 645 | // uint8_t tmpBuf[4*32768]; 646 | 647 | typedef union 648 | { 649 | uint32_t i; 650 | float f; 651 | } Float32_t; 652 | 653 | float rand_float_co() 654 | { 655 | Float32_t x; 656 | x.i = 0x3f800000 | (rand() & 0x007fffff); 657 | return(x.f - 1.0f); 658 | } 659 | 660 | // 661 | // 662 | 663 | #define FILTER_HPF (0x40) 664 | #define FILTER_160 (0x01) 665 | #define FILTER_80 (0x02) 666 | #define FILTER_40 (0x04) 667 | #define FILTER_30_20 (0x08) 668 | #define FILTER_17_15 (0x10) 669 | #define FILTER_10 (0x20) 670 | 671 | struct sockaddr_in recv_Addr; 672 | socklen_t addrLen = sizeof(recv_Addr); 673 | unsigned char udpBuffer[1600]; // Original Protocol Command & Control 674 | 675 | // 676 | 677 | double samp_db = 0.0; 678 | float rnd0v = 0.0; 679 | float rnd0u = 0.0; 680 | 681 | float tmp_temperature = 0.0; 682 | float tmp_fwd_power = 0.0; 683 | float tmp_rev_power = 0.0; 684 | float tmp_pa_current = 0.0; 685 | int tmp_temp_count = 0; 686 | int tmp_revp_count = 0; 687 | 688 | float hermes_temperature = 0; 689 | float hermes_fwd_power = 0; 690 | float hermes_rev_power = 0; 691 | float hermes_pa_current = 0; 692 | 693 | int hwCmdState = 0; 694 | int hwCmdSeqNum = 0; 695 | unsigned char hwCmd[8] = { 0,0,0,0,0 }; // 1+4 fpga command bytes 696 | 697 | void *udp_rcv_thread_runner(void *param) 698 | { 699 | if (hl2_running == 0) { 700 | hl2_udp_rcv(1); 701 | hl2_udp_rcv_end(); 702 | } 703 | return(param); 704 | } 705 | 706 | // extract command replies, status, and 24-bit IQ 707 | // transcode to 8 or 16 bit IQ 708 | 709 | int handleRcvData(unsigned char *hl2Buf, int n) 710 | { 711 | unsigned char *buf = hl2Buf; 712 | int j, jj; 713 | int rcvSeqNum; 714 | double samp_m2 = 0.0; 715 | double g8 = gain0; // GAIN8; 716 | unsigned char uv[1024]; 717 | 718 | int syncErr = ( (buf[11 - 3] != 0x7F) 719 | || (buf[11 - 2] != 0x7F) 720 | || (buf[11 - 1] != 0x7F)); 721 | if (syncErr) { return(-1); } 722 | rcvSeqNum = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | (buf[7]); 723 | 724 | for (jj=0; jj<1024; jj+=512) { 725 | int replyBit = (buf[jj+11] & 0x80) ; 726 | int dt = rcvSeqNum - hwCmdSeqNum; 727 | if (replyBit) { 728 | int i; 729 | int cmdEcho = (buf[jj+11] & 0x7F) >> 1; 730 | printf("******** # %d hw cmd ack 0x%02x : ", dt, cmdEcho); 731 | for (i=0;i<5;i++) { printf("0x%02x ", buf[jj+11+i]); } 732 | printf("\n"); 733 | cmdEcho = hwCmd[0]; // ToDo: test or fix 734 | if ( cmdEcho == hwCmd[0] ) { 735 | hwCmdState = 0; 736 | // command acknowledged ? 737 | specialCommandCounter -= 1; 738 | // hwCmdSeqNum = rcvSeqNum; 739 | } 740 | if (buf[jj+11] == 0xff) { 741 | hwCmdState = -1; 742 | fprintf(stdout,"******** hw cmd error \n" ); 743 | } 744 | } 745 | } 746 | 747 | int dtype0 = buf[11 ] >> 3; 748 | int dtype1 = buf[11+512] >> 3; 749 | if (dtype0 == 1) { 750 | int jj = 0; 751 | tmp_temperature += ((buf[11+jj+1] << 8) | (buf[11+jj+2])); 752 | tmp_fwd_power += ((buf[11+jj+3] << 8) | (buf[11+jj+4])); 753 | tmp_temp_count += 1; 754 | } 755 | if (dtype1 == 1) { 756 | int jj = 512; 757 | tmp_temperature += ((buf[11+jj+1] << 8) | (buf[11+jj+2])); 758 | tmp_fwd_power += ((buf[11+jj+3] << 8) | (buf[11+jj+4])); 759 | tmp_temp_count += 1; 760 | } 761 | if (dtype0 == 2) { 762 | tmp_rev_power += ((buf[11 + 1] << 8) | (buf[11 + 2])); 763 | tmp_pa_current += ((buf[11 + 3] << 8) | (buf[11 + 4])); 764 | tmp_revp_count += 1; 765 | } 766 | if (dtype1 == 2) { 767 | int jj = 512; 768 | tmp_rev_power += ((buf[11+jj+1] << 8) | (buf[11+jj+2])); 769 | tmp_pa_current += ((buf[11+jj+3] << 8) | (buf[11+jj+4])); 770 | tmp_revp_count += 1; 771 | } 772 | double scale = 32768.0 * 32768.0 * 2.0; // 33 bit shift 773 | double scale_r = 1.0 / scale; 774 | int16_t *tmp16ptr = (int16_t *)&uv[0]; 775 | int k = 0; 776 | int kk = 0; 777 | for (jj=0; jj<1024; jj+=512) { 778 | for (j=8+jj+8; j<8+jj+512; j += 8) { 779 | int imagp, realp; 780 | double u,v; 781 | // reversed order IQ 782 | imagp = buf[j ] << 24 | buf[j + 1] << 16 | buf[j + 2] << 8; 783 | realp = buf[j + 3] << 24 | buf[j + 4] << 16 | buf[j + 5] << 8; 784 | v = realp; 785 | u = imagp; 786 | if (sampleBits == 16) { // for HERMES !!! 787 | float g16; 788 | g16 = 64.0 * 32768.0; // shift 22 bits up 789 | // scale_r == shift 33 bits down 790 | // total shift == 22 - 33 == 11 bits down 791 | // 24 - 11 = 13 bits used (sign + 12 bits?) 792 | float v1 = g16 * scale_r * v; 793 | float u1 = g16 * scale_r * u; 794 | int vv = (int)roundf(v1); 795 | int uu = (int)roundf(u1); 796 | tmp16ptr[kk ] = vv; 797 | tmp16ptr[kk+1] = uu; 798 | kk += 2; // 2 16-bit samples 799 | k += 4; // is 4 bytes 800 | } else { // convert to 8-bit samples for rtl_tcp compatibility 801 | // magnitude for testing 802 | samp_m2 += u*u + v*v; 803 | // scale and add triangular noise shaping (dither) 804 | float vv = g8 * scale_r * v; 805 | float rnd1v = rand_float_co(); 806 | float rv = rnd1v - rnd0v; 807 | vv = vv + rv; 808 | float rvv = roundf(vv); 809 | int vi = (int)rvv; 810 | rnd0v = rnd1v; 811 | float uu = g8 * scale_r * u; 812 | float rnd1u = rand_float_co(); 813 | float ru = rnd1u - rnd0u; 814 | uu = uu + ru; 815 | float ruu = roundf(uu); 816 | int ui = (int)ruu; 817 | rnd0u = rnd1u; 818 | // unsigned 8-bit samples 819 | uv[k] = vi + 128; 820 | uv[k+1] = ui + 128; 821 | k += 2; // 2 bytes for 8-bit IQ 822 | } 823 | } 824 | } 825 | ring_write(&uv[0], k); 826 | // 827 | int nSamples = (2 * (512-8))/4; // == 1008/4 == 252 828 | double samp_rms = sqrt(samp_m2 / nSamples) / scale; 829 | if (samp_rms > 0.0) { 830 | samp_db = 20.0 * log10(samp_rms); 831 | } 832 | // 833 | if (tmp_temp_count >= 16) { 834 | hermes_temperature = tmp_temperature / (float)tmp_temp_count; 835 | hermes_rev_power = tmp_fwd_power / (float)tmp_temp_count; 836 | tmp_temperature = 0.0; 837 | tmp_fwd_power = 0.0; 838 | tmp_temp_count = 0; 839 | } 840 | if (tmp_revp_count >= 16) { 841 | hermes_fwd_power = tmp_rev_power / (float)tmp_revp_count; 842 | tmp_rev_power = 0.0; 843 | 844 | float tmp2_pa_current ; 845 | float tmp3_pa_current ; 846 | tmp2_pa_current = tmp_pa_current / (float)tmp_revp_count; 847 | tmp3_pa_current = 3.26 * tmp2_pa_current / (4096.0 * 50.0 * 0.04); 848 | hermes_pa_current = 1270.0 * tmp3_pa_current / 1000.0; 849 | tmp_pa_current = 0.0; 850 | tmp_revp_count = 0; 851 | } 852 | return(k); 853 | } 854 | 855 | void dump_iq_buf(unsigned char *buf) 856 | { 857 | int k; 858 | // 1032 - 8 = 1024 // 8 -> effe0406 sequence4 859 | // 1024 = 2 * 512 860 | // 512 - 8 = 63 * 4+4 IQ // 8 -> 7f 7f 7f c0 c1 c2 c3 c4 861 | // 63 * 8 = 504 862 | // 504 * 2 = 1008 863 | for (k=0;k<16;k++) { 864 | int c = buf[k]; 865 | fprintf(stdout,"%02x ",c); 866 | } 867 | fprintf(stdout,"\n "); 868 | for (k=512+8;k<512+16;k++) { 869 | int c = buf[k]; 870 | fprintf(stdout,"%02x ",c); 871 | } 872 | fprintf(stdout,"\n"); 873 | int seq0 = buf[4] << 24 | buf[5] << 16 | buf[6] << 8 | buf[7]; 874 | printf("seq = %d \n", seq0); 875 | 876 | #ifdef DEBUG_FILE 877 | double samp_msq = 0.0; // magnitude squared 878 | double samp_rms = 0.0; 879 | double scale = 32768.0 * 32768.0 * 2.0; // 33 bit shift 880 | 881 | if (dump_fname == NULL) { return; } 882 | FILE *f = fopen(dump_fname,"w"); 883 | if (f == NULL) { return; } 884 | // printf("file 0x%08x \n", f); 885 | 886 | for (k=0;k<16;k++) { 887 | int c = buf[k]; 888 | fprintf(f,"%02x ",c); 889 | } 890 | printf(" : "); 891 | for (k=512+8;k<512+16;k++) { 892 | int c = buf[k]; 893 | fprintf(f,"%02x ",c); 894 | } 895 | fprintf(f,"\n"); 896 | 897 | int j; 898 | int xr, xi ; 899 | double v, u; 900 | 901 | for (j=8+8; j<8+512; j += 8) { 902 | xr = buf[j + 3] << 24 | buf[j + 4] << 16 | buf[j + 5] << 8; 903 | xi = buf[j ] << 24 | buf[j + 1] << 16 | buf[j + 2] << 8; 904 | v = xr; 905 | u = xi; 906 | fprintf(f,"%4d 0x%08x 0x%08x \t%lf \t%lf \n", 907 | (j-16)/8, xr, xi, v/scale, u/scale); 908 | samp_msq += u*u + v*v; 909 | } 910 | for (j=8+512+8; j<8+1024; j+= 8) { 911 | xr = buf[j + 3] << 24 | buf[j + 4] << 16 | buf[j + 5] << 8; 912 | xi = buf[j ] << 24 | buf[j + 1] << 16 | buf[j + 2] << 8; 913 | v = xr; 914 | u = xi; 915 | // 916 | fprintf(f,"%4d 0x%08x 0x%08x \t%lf \t%lf \n", 917 | (j-16)/8, xr, xi, v/scale, u/scale); 918 | samp_msq += u*u + v*v; 919 | } 920 | fclose(f); 921 | 922 | int nSamples = (2 * (512-8))/4; // == 1008/4 == 252 923 | samp_rms = sqrt(samp_msq / nSamples) / scale; 924 | if (samp_rms > 0.0) { 925 | samp_db = 20.0 * log10(samp_rms); 926 | } 927 | printf("rms db = %lf \n", samp_db); 928 | #endif // DEBUG_FILE 929 | } 930 | 931 | // 932 | // 933 | 934 | uint32_t getDecimalValueOfIPV4_String(const char* ipAddress) 935 | { 936 | uint8_t ipbytes[4]={}; 937 | int i =0; 938 | int8_t j=3; 939 | while (ipAddress+i && i 3000000) { 979 | n2adr_filter_rx = FILTER_HPF; 980 | } 981 | 982 | n2adr_filter_tx = FILTER_160; 983 | if (hermes_tx_freq > 3500000) { 984 | n2adr_filter_tx = FILTER_80; 985 | } 986 | if (hermes_tx_freq > 7000000) { 987 | n2adr_filter_tx = FILTER_40; 988 | } 989 | if (hermes_tx_freq > 14000000) { 990 | n2adr_filter_tx = FILTER_30_20; 991 | } 992 | if (hermes_tx_freq > 21000000) { 993 | n2adr_filter_tx = FILTER_10; // ToDo: bug 994 | } 995 | } 996 | 997 | void configSendUDP_packet(unsigned char *opccBuf) 998 | { 999 | int i; 1000 | 1001 | bzero(opccBuf, 1032); 1002 | opccBuf[0] = 0xEF; 1003 | opccBuf[1] = 0xFE; 1004 | opccBuf[2] = 0x01; 1005 | opccBuf[3] = 0x02; // Metis UDP/IP for USB EP2 1006 | 1007 | opccBuf[4] = seqNum >> 24 & 0xFF; 1008 | opccBuf[5] = seqNum >> 16 & 0xFF; 1009 | opccBuf[6] = seqNum >> 8 & 0xFF; 1010 | opccBuf[7] = seqNum & 0xFF; 1011 | 1012 | opccBuf[ 8] = 0x7F; 1013 | opccBuf[ 9] = 0x7F; 1014 | opccBuf[ 10] = 0x7F; 1015 | opccBuf[520] = 0x7F; 1016 | opccBuf[521] = 0x7F; 1017 | opccBuf[522] = 0x7F; 1018 | 1019 | tx_key_down = get_tx_key() ? 1 : 0; // 1020 | // 1021 | if (hermes_tx_freq < 1800000) { tx_key_down = 0; } 1022 | if (tx_key_down) { 1023 | hermes_tx_drive_level = get_tx_drive(); // lvl 0..15 1024 | tx_delay = 1.5 * 382.0 * (sampRate/48000.0); // ? 1.5 second delay 1025 | } else { 1026 | // hermes_tx_drive_level = 0; 1027 | if (tx_delay > 0) { tx_delay -= 1; } 1028 | } 1029 | hl2_tx_on = tx_key_down | ((tx_delay > 0) ? 1 : 0); 1030 | if (hl2_tx_on == 0) { 1031 | // hermes_tx_drive_level = 0; // yyy 1032 | } 1033 | 1034 | opccBuf[ 11] = (C0_addr + 0) << 1 | hl2_tx_on; // 1035 | for (i=0;i<4;i++) { opccBuf[ 12+i] = 0; } 1036 | opccBuf[523] = (C0_addr + 1) << 1 | hl2_tx_on; // 1037 | for (i=0;i<4;i++) { opccBuf[524+i] = 0; } 1038 | 1039 | hermes_tx_freq = hermes_rx_freq + hermes_tx_offset ; 1040 | // setFilterEnables(); 1041 | 1042 | if (C0_addr == 0) { 1043 | opccBuf[11] = (C0_addr << 1) | hl2_tx_on; // 0 1044 | 1045 | // [25:24] Speed (00=48kHz, 01=96kHz, 10=192kHz, 11=384kHz) 1046 | int r0 = 0; 1047 | if ( sampRate == 384000) { 1048 | r0 = 3; 1049 | } else if (sampRate == 192000) { 1050 | r0 = 2; 1051 | } else if (sampRate == 96000) { 1052 | r0 = 1; 1053 | } 1054 | opccBuf[12] = r0; 1055 | 1056 | // [23:17] Open Collector Outputs on Penelope or Hermes 1057 | opccBuf[13] = 0; 1058 | if (hl2_tx_on) { // send filter selection on J16 1059 | opccBuf[13] = n2adr_filter_tx << 1; // C2 1060 | } else { 1061 | opccBuf[13] = n2adr_filter_rx << 1; 1062 | } 1063 | 1064 | // [10] VNA fixed RX Gain (0=-6dB, 1=+6dB) 1065 | opccBuf[14] = 0; 1066 | 1067 | // [6:3] Number of Receivers (0000=1 to max 1011=12) 1068 | // [2] Duplex (0=off, 1=on) 1069 | opccBuf[15] = 0x04; // C4 duplex on 1070 | 1071 | opccBuf[523] = ((C0_addr + 1) << 1) | hl2_tx_on; // addr 1 1072 | // transmitter frequency ? 1073 | opccBuf[524] = (hermes_tx_freq >> 24) & 0xFF; // C1 1074 | opccBuf[525] = (hermes_tx_freq >> 16) & 0xFF; // C2 1075 | opccBuf[526] = (hermes_tx_freq >> 8) & 0xFF; // C3 1076 | opccBuf[527] = (hermes_tx_freq ) & 0xFF; // C4 1077 | } 1078 | 1079 | if (C0_addr == 2) { 1080 | // opccBuf[4] = seqNum >> 24 & 0xFF; 1081 | // opccBuf[5] = seqNum >> 16 & 0xFF; 1082 | // opccBuf[6] = seqNum >> 8 & 0xFF; 1083 | // opccBuf[7] = seqNum & 0xFF; 1084 | // seqNum += 1; 1085 | 1086 | opccBuf[11] = (C0_addr << 1) | hl2_tx_on; // C0 1087 | opccBuf[12] = (hermes_rx_freq >> 24) & 0xFF; // C1 1088 | opccBuf[13] = (hermes_rx_freq >> 16) & 0xFF; // C2 1089 | opccBuf[14] = (hermes_rx_freq >> 8) & 0xFF; // C3 1090 | opccBuf[15] = (hermes_rx_freq ) & 0xFF; // C4 1091 | 1092 | opccBuf[523 ] = ((C0_addr + 1) << 1) | hl2_tx_on; // C0 1093 | opccBuf[523+1] = (hermes_rx_freq >> 24) & 0xFF; // C1 1094 | opccBuf[523+2] = (hermes_rx_freq >> 16) & 0xFF; // C2 1095 | opccBuf[523+3] = (hermes_rx_freq >> 8) & 0xFF; // C3 1096 | opccBuf[523+4] = (hermes_rx_freq ) & 0xFF; // C4 1097 | } 1098 | if (C0_addr == 4) { 1099 | opccBuf[ 11] = (C0_addr << 1) | hl2_tx_on; // C0 == 4 1100 | opccBuf[523] = ((C0_addr + 1) << 1) | hl2_tx_on; // 5 1101 | } 1102 | if (C0_addr == 6) { 1103 | // special commands requiring an ack 1104 | opccBuf[ 11] = (C0_addr << 1) | hl2_tx_on; // 6 1105 | opccBuf[523] = ((C0_addr + 1) << 1) | hl2_tx_on; // 7 1106 | // 1107 | checkForSpecialCommands(seqNum); // sets hwCmdState 1108 | if ((hwCmdState == 3) || (hwCmdState == 7)) { 1109 | opccBuf[523] = (hwCmd[0] << 1) | hl2_tx_on | 0x80; // ? 1110 | for (i=1;i<=4;i++) { 1111 | opccBuf[523+i] = hwCmd[i]; // 4 fpga command bytes 1112 | } 1113 | hwCmdState = hwCmdState & 0x0e; // clear LSB (2,6) 1114 | hwCmdSeqNum = seqNum; 1115 | } else { 1116 | // opccBuff 524..527 are already zero'd 1117 | } 1118 | } 1119 | if (C0_addr == 8) { 1120 | // filter selection 1121 | opccBuf[ 11] = (C0_addr << 1) | hl2_tx_on; // 8 1122 | opccBuf[523] = ((C0_addr + 1) << 1) | hl2_tx_on; // 9 1123 | 1124 | // 0x09 [31:24] Hermes TX Drive Level (only [31:28] used) 1125 | opccBuf[524] = ( (hermes_tx_drive_level << 4) 1126 | + hermes_tx_drive_level ) ; // 0..15 1127 | if (hl2_tx_on) { 1128 | hermes_enable_power_amp = 1; 1129 | hermes_Q5_switch_ext_ptt_lp = 1; 1130 | opccBuf[525] = ( (hermes_enable_power_amp << 3) 1131 | | (hermes_Q5_switch_ext_ptt_lp << 2) ); 1132 | opccBuf[526] = 0; // C3 1133 | opccBuf[527] = 0; // C4 1134 | } else { 1135 | opccBuf[524] = 0; 1136 | opccBuf[525] = 0; 1137 | opccBuf[526] = 0; 1138 | opccBuf[527] = 0; // something called "Alex" ??? 1139 | } 1140 | } 1141 | if (C0_addr == 10) { 1142 | // C0_addr is 10, 11 (0x0a,0x0b) 1143 | opccBuf[ 11] = (C0_addr << 1) | hl2_tx_on; // 10 1144 | opccBuf[ 15] = ((hermes_lna_gain + 12) & 0x3F) | 0x40; 1145 | opccBuf[523] = ((C0_addr + 1) << 1) | hl2_tx_on; // 11 1146 | } 1147 | C0_addr += 2; // receiver 1 frequency 1148 | if (C0_addr > 10) { C0_addr = 0; } 1149 | 1150 | // get IQ transmit samples, if any, or zero fill 1151 | 1152 | tx_block_setup(seqNum); 1153 | 1154 | int jj; 1155 | for (jj = 0; jj < 1024; jj+=512) { 1156 | for (i = 0; i < 63; i++) { 1157 | // update audio & tx IQ sent to hl2 1158 | int txI = 0; 1159 | int txQ = 0; 1160 | // #ifdef TX_OK 1161 | get_tx_sample(&txI, &txQ); 1162 | // #endif 1163 | int k = 16 + jj + 8 * i; 1164 | 1165 | opccBuf[k ] = 0; // Left Audio 1166 | opccBuf[k+1] = 0; // lsb 1167 | opccBuf[k+2] = 0; // Right Audio 1168 | opccBuf[k+3] = 0; // lsb 1169 | 1170 | // normal IQ order 1171 | opccBuf[k+6] = (txI >> 8) & 0xff; // bigEndian I yyy 1172 | opccBuf[k+7] = (txI ) & 0xff; // lsb 1173 | opccBuf[k+4] = (txQ >> 8) & 0xff; // Q 1174 | opccBuf[k+5] = (txQ ) & 0xff; // lsb 1175 | } 1176 | } 1177 | seqNum += 1; 1178 | } 1179 | 1180 | struct sockaddr_in hl2_sockAddr; 1181 | 1182 | int udp_send_count = 0; 1183 | 1184 | int hl2_udp_rcv_loop(int fd, int mode) 1185 | { 1186 | unsigned char hl2RcvBuf[1500]; 1187 | struct sockaddr_in from_Addr; 1188 | socklen_t addrLen2 = sizeof(struct sockaddr_in); 1189 | int k = 0; 1190 | ssize_t b = 0; 1191 | int n2 = 1500; 1192 | 1193 | bzero(hl2RcvBuf, 1500); 1194 | from_Addr = hl2_sockAddr; 1195 | while (udp_running > 0) { 1196 | // fd = hl2_fd 1197 | b = recvfrom(fd, &hl2RcvBuf[0], n2, 0, 1198 | (struct sockaddr *)&from_Addr, &addrLen2); 1199 | if (b > 0) { // got UDP data from HL2 1200 | ssize_t bb; 1201 | 1202 | // unpack and write IQ to ring buffer 1203 | handleRcvData(hl2RcvBuf, b); // stuff IQ into FIFO 1204 | // then if enough IQ samples send from FIFO via rtl_tcp 1205 | tcp_send_poll(); // remove from FIFO 1206 | if (sendErrorFlag < 0) { 1207 | fprintf(stdout, "tcp send error \n"); 1208 | udp_running = -3; 1209 | } 1210 | 1211 | if (tx_gear_counter % tx_gear_ratio == 0) { 1212 | configSendUDP_packet(udpBuffer); // inc send sequence # 1213 | int msgLen = 1032; // send UDP pkt to HL2 1214 | bb = sendto( fd, &udpBuffer[0], msgLen, 0, 1215 | (struct sockaddr *)&hl2_sockAddr, addrLen); 1216 | if (bb > 0) { 1217 | udp_send_count += 1; 1218 | if ((tcp_tx_cmd & 0x00ff) != 0) { 1219 | tcp_tx_cmd -= 1; 1220 | // printf("tcp_tx_cmd = %d ", tcp_tx_cmd); 1221 | } 1222 | } else { // quit loop on error 1223 | fprintf(stdout, "udp to hl2 send error \n"); 1224 | udp_running = -2; 1225 | return(bb); 1226 | } 1227 | } 1228 | tx_gear_counter += 1; 1229 | } else { 1230 | fprintf(stdout, "udp from hl2 rcv error \n"); 1231 | udp_running = -1; 1232 | return(b); 1233 | } 1234 | k += 1; 1235 | } // loop forever 1236 | return(0); 1237 | } 1238 | 1239 | void udp_rcv_n_bytes(int fd, int num) // for testing 1240 | { 1241 | int k = 0; 1242 | ssize_t b = 0; 1243 | char hl2RcvBuf[1500]; 1244 | bzero(hl2RcvBuf, 1500); 1245 | int n2 = 1500; 1246 | while (k < num) { 1247 | b = recvfrom(fd, &hl2RcvBuf[0], n2, 0, NULL, NULL); 1248 | if (b > 0) { 1249 | configSendUDP_packet(udpBuffer); // inc sequence 1250 | int msgLen = 1032; 1251 | b = sendto( fd, &udpBuffer[0], msgLen, 0, 1252 | (struct sockaddr *)&hl2_sockAddr, addrLen); 1253 | if (b > 0) { udp_send_count += 1; } 1254 | } else { 1255 | return; 1256 | } 1257 | k += 1; 1258 | } 1259 | if (fd != 0) { 1260 | b = recvfrom(fd, &hl2RcvBuf[0], n2, 0, NULL, NULL); 1261 | if (b > 0) { 1262 | dump_iq_buf((unsigned char *)hl2RcvBuf); 1263 | } 1264 | } 1265 | printf("udp packets sent = %d \n", udp_send_count ); 1266 | } // udp_rcv_n_bytes() is a print dump for testing 1267 | 1268 | unsigned int getDecimalValueOfIPV4_String(const char* ipAddress); 1269 | 1270 | // int hl2_running = 0; 1271 | int hl2_fd = -1; 1272 | 1273 | int hl2_udp_setup() 1274 | { 1275 | // uint32_t hl2_ipAddr = 0x7f000001; // 127.0.0.1 == localhost 1276 | // int port = HERMES_PORT; 1277 | int fd = -1; 1278 | 1279 | tmp_temperature = 0.0; 1280 | tmp_fwd_power = 0.0; 1281 | tmp_rev_power = 0.0; 1282 | tmp_pa_current = 0.0; 1283 | tmp_temp_count = 0; 1284 | tmp_revp_count = 0; 1285 | bzero(udpBuffer, 1032); 1286 | 1287 | if (1) { 1288 | char *s = hl2_ip_string; 1289 | if (s) { 1290 | hl2_ipAddr = getDecimalValueOfIPV4_String(s); 1291 | printf("hl2 ip addr: %s = 0x%lx\n", s, (long int)hl2_ipAddr); 1292 | } else { 1293 | return(-1); 1294 | } 1295 | } 1296 | 1297 | // if (hl2_fd != -1) { 1298 | if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 1299 | perror("socket failed"); 1300 | return(-1); 1301 | } 1302 | hl2_fd = fd; 1303 | // } 1304 | 1305 | memset( &hl2_sockAddr, 0, sizeof(hl2_sockAddr) ); 1306 | hl2_sockAddr.sin_family = AF_INET; 1307 | hl2_sockAddr.sin_port = htons( HERMES_PORT ); 1308 | // hl2_sockAddr.sin_addr.s_addr = htonl( 0x7f000001 ); // 127.0.0.1 1309 | hl2_sockAddr.sin_addr.s_addr = htonl( hl2_ipAddr ); 1310 | 1311 | return(0); 1312 | } 1313 | 1314 | void hl2_send_start_cmds(int n) 1315 | { 1316 | char myMsg[1500]; 1317 | // int n = MAX_NUMBER_OF_START_COMMANDS; 1318 | 1319 | for ( int i = 0; i < n; i++ ) { 1320 | 1321 | int flag = 0x01; // send start command to HL2 1322 | configStartCmd(myMsg, flag); // 64 bytes 1323 | int msgLen = 64; 1324 | 1325 | int addrLen = sizeof(hl2_sockAddr); 1326 | if (sendto( hl2_fd, &myMsg[0], msgLen, 0, 1327 | (struct sockaddr *)&hl2_sockAddr, addrLen) < 0 ) { 1328 | perror( "sendto failed" ); 1329 | break; 1330 | } 1331 | if (i == 0) { 1332 | // printf( "Cmd %d sent, %d bytes of UDP\n", flag, msgLen); 1333 | } 1334 | if (ring_data_available() > 0) { 1335 | int nt = i + 1; 1336 | printf("hl2 start cmd sent %d times\n", nt); 1337 | printf("hl2 udp IQ data received \n"); 1338 | break; // yyy yyy 1339 | } 1340 | 1341 | usleep(START_LOOP_DELAY); 1342 | #ifdef RESPONSE_WAIT 1343 | char ackvar[1500]; 1344 | bzero(ackvar, 1500); 1345 | int n2 = 1500; 1346 | int b = -1; 1347 | if ((b = recvfrom(hl2_fd, &ackvar[0], n2, 0, NULL, NULL)) < 0 ) { 1348 | printf("hl2 receive error: errno %d\n", errno); 1349 | // exit(1); 1350 | } else { 1351 | if (b > 0) { 1352 | printf("received %d bytes of data >%s< \n", b, "x"); 1353 | hl2_running = 1; 1354 | break; 1355 | } 1356 | } 1357 | #endif 1358 | } 1359 | 1360 | /* 1361 | int b = 0; 1362 | int msgLen = 1032; 1363 | C0_addr = 0; // for rx 1 & transmitter 1 frequency 1364 | 1365 | configSendUDP_packet(udpBuffer); 1366 | b = sendto( hl2_fd, &udpBuffer[0], msgLen, 0, 1367 | (struct sockaddr *)&hl2_sockAddr, addrLen); 1368 | printf("sent %d bytes of f0 cmd \n", b); 1369 | C0_addr = 2; 1370 | configSendUDP_packet(udpBuffer); 1371 | b = sendto( hl2_fd, &udpBuffer[0], msgLen, 0, 1372 | (struct sockaddr *)&hl2_sockAddr, addrLen); 1373 | // printf("sent %d bytes of f0 cmd \n", b); 1374 | */ 1375 | } 1376 | 1377 | 1378 | void hl2_udp_rcv(int loopFlag) 1379 | { 1380 | if (loopFlag) { 1381 | udp_running = 1; 1382 | int mode = 1; 1383 | hl2_udp_rcv_loop(hl2_fd, mode); // loop until error 1384 | } else { 1385 | int count = 2048; 1386 | udp_rcv_n_bytes(hl2_fd, count); // unused 1387 | } 1388 | } 1389 | 1390 | int hl2_udp_rcv_end() 1391 | { 1392 | char myMsg[1500]; 1393 | int msgLen = 64; 1394 | int flag = 0x00; // send stop UDP to HL2 twice 1395 | 1396 | print_hl2_stats() ; 1397 | 1398 | configStartCmd(myMsg, flag); 1399 | 1400 | msgLen = 64; 1401 | sendto( hl2_fd, &myMsg[0], msgLen, 0, 1402 | (struct sockaddr *)&hl2_sockAddr, addrLen); 1403 | usleep(50000); 1404 | sendto( hl2_fd, &myMsg[0], msgLen, 0, 1405 | (struct sockaddr *)&hl2_sockAddr, addrLen); 1406 | if (0) { 1407 | // printf( "Cmd %d sent, %d bytes of UDP\n", flag, msgLen); 1408 | } 1409 | usleep(50000); 1410 | 1411 | hl2_running = 0; 1412 | 1413 | // cleanup 1414 | close( hl2_fd ); 1415 | seqNum = 0; 1416 | tx_gear_counter = 0; 1417 | fprintf(stdout, "hl2 UDP stream stopped \n\n"); 1418 | hl2_fd = -1; 1419 | return 0; 1420 | } 1421 | 1422 | void hl2_stop() 1423 | { 1424 | if (hl2_fd >= 0) { 1425 | char myMsg[1500] ; 1426 | int msgLen = 64; 1427 | int flag = 0x00; // stop 1428 | configStartCmd(myMsg, flag); 1429 | sendto( hl2_fd, &myMsg[0], msgLen, 0, 1430 | (struct sockaddr *)&hl2_sockAddr, addrLen); 1431 | usleep(50000); 1432 | sendto( hl2_fd, &myMsg[0], msgLen, 0, 1433 | (struct sockaddr *)&hl2_sockAddr, addrLen); 1434 | hl2_running = 0; 1435 | close(hl2_fd); 1436 | hl2_fd = -1; 1437 | 1438 | print_hl2_stats() ; 1439 | seqNum = 0; 1440 | fprintf(stdout, "hl2 UDP stream stopped \n"); 1441 | } 1442 | } 1443 | 1444 | // 1445 | // 1446 | 1447 | // hermes_discovery.c 1448 | // 2020-02-04 2019-12-23 rhn 2017-jun-xx 1449 | // 1450 | #define D_VERSION ("0.2.114") 1451 | 1452 | #define DISCOVER 1453 | #define USE_FD 1454 | #define ECHO_WAIT (1) 1455 | 1456 | /* 1457 | #include 1458 | #include 1459 | #include 1460 | #include 1461 | #include 1462 | #include 1463 | #include 1464 | #include 1465 | #include 1466 | 1467 | #include 1468 | #include 1469 | #include 1470 | #include 1471 | #include 1472 | #include 1473 | #include 1474 | #include 1475 | */ 1476 | 1477 | #define HERMES_PORT (1024) 1478 | 1479 | #define ADR_SIZE (32) 1480 | #define INVALID_SOCKET (-1) 1481 | 1482 | char ip_string[ADR_SIZE]; 1483 | unsigned char sendbuf[1500]; 1484 | int rx_discover_socket = -1; 1485 | int sockfd = -1; 1486 | int rx_udp_socket = INVALID_SOCKET; // -1 1487 | // int seqNum = 0; 1488 | 1489 | int total = 0; 1490 | 1491 | #ifndef USE_FD 1492 | struct sockaddr_in hermes_Addr; 1493 | #endif 1494 | struct sockaddr_in recv_Addr; 1495 | // socklen_t addrLen = sizeof(recv_Addr); 1496 | 1497 | void do_discovery(void); 1498 | 1499 | // int main(int argc, char **argv) 1500 | int discover_main() 1501 | { 1502 | printf("Hermes Lite 2 UDP IP Address Discovery %s\n", VERSION); 1503 | 1504 | #ifdef DISCOVER 1505 | do_discovery(); 1506 | #endif 1507 | 1508 | printf("Done.\n"); 1509 | return(0); 1510 | } 1511 | 1512 | // 1513 | 1514 | void broadcast_discover(int rx_discover_socket); 1515 | 1516 | void do_discovery() 1517 | { 1518 | // short int port = HERMES_PORT; 1519 | int i; 1520 | ssize_t b; 1521 | int fl; 1522 | unsigned char data[1500]; 1523 | 1524 | rx_discover_socket = socket(PF_INET, SOCK_DGRAM, 0); 1525 | setsockopt(rx_discover_socket, SOL_SOCKET, SO_BROADCAST, 1526 | (char *)&i, sizeof(i)); 1527 | fl = fcntl(rx_discover_socket, F_GETFL); 1528 | fcntl(rx_discover_socket, F_SETFL, fl | O_NONBLOCK); 1529 | 1530 | broadcast_discover(rx_discover_socket); 1531 | 1532 | usleep(50000); 1533 | usleep(50000); 1534 | b = recvfrom(rx_discover_socket, (char *)data, 1500, 0, 1535 | (struct sockaddr *)&recv_Addr, &addrLen); 1536 | if (b > 0) { 1537 | uint32_t ipAddr; 1538 | strncpy(ip_string, inet_ntoa(recv_Addr.sin_addr), ADR_SIZE); 1539 | printf("discover ip = %s : ", ip_string); 1540 | ipAddr = *(uint32_t *)&recv_Addr.sin_addr; 1541 | printf("0x%08X \n", ntohl(ipAddr)); 1542 | int p = ntohs(recv_Addr.sin_port); 1543 | printf("discover port = %d \n", p); 1544 | } 1545 | if (b >= 60) { 1546 | int j; 1547 | printf("Received %ld bytes from UDP broadcast discover \n", b); 1548 | for (j=0;j<3;j++) { 1549 | printf("0x%02x ", data[j]); 1550 | } 1551 | printf("\n"); 1552 | printf("MAC Address: "); 1553 | for (j=3;j<9;j++) { 1554 | printf("%02x", data[j]); 1555 | if (j != 8) { printf("."); } 1556 | } 1557 | printf("\n"); 1558 | printf("Protocol: %d\n", data[10]); 1559 | printf("Gateware Version: %d\n", data[ 9]); 1560 | 1561 | /* 1562 | for (j=0;j<64;j++) { 1563 | printf("%02x ", data[j]); 1564 | if (j % 8 == 7) { printf("\n"); } 1565 | } 1566 | printf("\n"); 1567 | */ 1568 | } else { 1569 | exit(-1); 1570 | } 1571 | // is set_ip(rx_discover_socket) needed ??? 1572 | } 1573 | 1574 | void print_hl2_stats() 1575 | { 1576 | float tc = ((3.26 * hermes_temperature / 4096.0) - 0.5) / 0.01; 1577 | float tf = (tc * 9.0 / 5.0) + 32.0; 1578 | printf("temp = %5.1f C = %5.1f F\n", tc, tf); 1579 | printf("Rx rms db = %lf \n", samp_db); 1580 | // printf("hermes_fwd_power: %f \n", hermes_fwd_power ); 1581 | // printf("hermes_rev_power: %f \n", hermes_rev_power ); 1582 | // printf("hermes_pa_current: %f \n", hermes_pa_current); 1583 | printf("seqNum = %d \n", seqNum); 1584 | } 1585 | 1586 | // 1587 | // broadcast_discoverer 1588 | // 1589 | 1590 | void broadcast_discover(int rx_discover_socket) 1591 | { 1592 | unsigned char data[64]; 1593 | int i, n = 0; 1594 | ssize_t b = 0; 1595 | int port = 1024; 1596 | static struct sockaddr_in bcast_Addr; 1597 | 1598 | struct ifaddrs * ifap, * p; 1599 | 1600 | data[0] = 0xEF; 1601 | data[1] = 0xFE; 1602 | data[2] = 0x02; 1603 | for (i = 3; i < 64; i++) { data[i] = 0; } 1604 | memset(&bcast_Addr, 0, sizeof(bcast_Addr)); 1605 | bcast_Addr.sin_family = AF_INET; 1606 | bcast_Addr.sin_port = htons(port); 1607 | if (getifaddrs(&ifap) == 0) { 1608 | p = ifap; 1609 | while(p) { 1610 | if ((p->ifa_addr) && p->ifa_addr->sa_family == AF_INET) { 1611 | bcast_Addr.sin_addr 1612 | = ((struct sockaddr_in *)(p->ifa_broadaddr))->sin_addr; 1613 | b = sendto(rx_discover_socket, (char *)data, 63, 0, 1614 | (const struct sockaddr *)&bcast_Addr, sizeof(bcast_Addr)); 1615 | } 1616 | // printf("bcast %d %d\n", i, n); 1617 | n++; 1618 | if (n > 255) { break; } 1619 | usleep(50000); 1620 | p = p->ifa_next; 1621 | } 1622 | freeifaddrs(ifap); 1623 | } 1624 | printf("%d UDP broadcasts of %ld bytes\n", n, b); 1625 | } 1626 | 1627 | // Copyright 2017,2020 Ronald H Nicholson Jr. All Rights Reserved. 1628 | // This code may only be redistributed under terms of 1629 | // the Mozilla Public License, Version 2.0. 1630 | // See: https://www.mozilla.org/en-US/MPL/2.0/ 1631 | // Exhibit B - "Incompatible With Secondary Licenses" Notice 1632 | // This Source Code Form is "Incompatible With Secondary Licenses", 1633 | // as defined by the Mozilla Public License, v. 2.0. 1634 | 1635 | // eof 1636 | --------------------------------------------------------------------------------