1 /*
2 * Copyright (C) 2018
3 * Matthias P. Braendli (matthias.braendli@mpb.li)
4 *
5 * Copyright (C) 2017
6 * Albrecht Lohofener (albrechtloh@gmx.de)
7 *
8 * This file is based on SDR-J
9 * Copyright (C) 2010, 2011, 2012, 2013
10 * Jan van Katwijk (J.vanKatwijk@gmail.com)
11 *
12 * This file is part of the welle.io.
13 * Many of the ideas as implemented in welle.io are derived from
14 * other work, made available through the GNU general Public License.
15 * All copyrights of the original authors are recognized.
16 *
17 * welle.io is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * welle.io is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with welle.io; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 *
31 */
32
33 #include <iostream>
34 #include <sys/time.h>
35
36 #include "rtl_tcp.h"
37
38 // For Qt translation if Qt is exisiting
39 #ifdef QT_CORE_LIB
40 #include <QtGlobal>
41 #else
42 #define QT_TRANSLATE_NOOP(x,y) (y)
43 #endif
44
45 // commands are packed in 5 bytes, one "command byte"
46 // and an integer parameter
47 struct command
48 {
49 unsigned char cmd;
50 unsigned int param;
51 }__attribute__((packed));
52
53 enum rtlsdr_tuner {
54 RTLSDR_TUNER_UNKNOWN = 0,
55 RTLSDR_TUNER_E4000,
56 RTLSDR_TUNER_FC0012,
57 RTLSDR_TUNER_FC0013,
58 RTLSDR_TUNER_FC2580,
59 RTLSDR_TUNER_R820T,
60 RTLSDR_TUNER_R828D
61 };
62
63 #define ONE_BYTE 8
64
getMyTime(void)65 static inline int64_t getMyTime(void)
66 {
67 struct timeval tv;
68
69 gettimeofday(&tv, NULL);
70 return ((int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_usec);
71 }
72
CRTL_TCP_Client(RadioControllerInterface & radioController)73 CRTL_TCP_Client::CRTL_TCP_Client(RadioControllerInterface& radioController) :
74 radioController(radioController),
75 sampleBuffer(32 * 32768),
76 sampleNetworkBuffer(256 * 32768),
77 spectrumSampleBuffer(8192)
78 {
79 memset(&dongleInfo, 0, sizeof(dongle_info_t));
80 dongleInfo.tuner_type = RTLSDR_TUNER_UNKNOWN;
81 }
82
~CRTL_TCP_Client(void)83 CRTL_TCP_Client::~CRTL_TCP_Client(void)
84 {
85 std::clog << "RTL_TCP_CLIENT: deleting ..." << std::endl;
86 stop();
87 }
88
setFrequency(int newFrequency)89 void CRTL_TCP_Client::setFrequency(int newFrequency)
90 {
91 frequency = newFrequency;
92 sendVFO(newFrequency);
93 }
94
getFrequency() const95 int CRTL_TCP_Client::getFrequency() const
96 {
97 return frequency;
98 }
99
restart(void)100 bool CRTL_TCP_Client::restart(void)
101 {
102 if (rtlsdrRunning) {
103 return true;
104 }
105
106 rtlsdrRunning = true;
107
108 networkBufferThread = std::thread(&CRTL_TCP_Client::networkBufferCopy, this);
109 networkBufferThread.detach();
110
111 receiveThread = std::thread(&CRTL_TCP_Client::receiveAndReconnect, this);
112 receiveThread.detach();
113
114 // Wait so that the other thread has a chance to establish the connection
115 std::this_thread::sleep_for(std::chrono::milliseconds(500));
116
117 std::unique_lock<std::mutex> lock(mutex);
118 return connected;
119 }
120
is_ok()121 bool CRTL_TCP_Client::is_ok()
122 {
123 return rtlsdrRunning;
124 }
125
stop(void)126 void CRTL_TCP_Client::stop(void)
127 {
128 #ifdef __ANDROID__
129 // Send TCP_ANDROID_EXIT cmd to explicitly cause the driver to turn off itself
130 sendCommand(0x7e, 0);
131 #endif
132
133 std::unique_lock<std::mutex> lock(mutex);
134
135 // Close connection
136 sock.close();
137
138 agcRunning = false;
139 rtlsdrRunning = false;
140
141 lock.unlock();
142
143 if (agcThread.joinable()) {
144 agcThread.join();
145 }
146
147 if (receiveThread.joinable()) {
148 receiveThread.join();
149 }
150
151 if (networkBufferThread.joinable()) {
152 networkBufferThread.join();
153 }
154
155 connected = false;
156 }
157
read_convert_from_buffer(RingBuffer<uint8_t> & buffer,DSPCOMPLEX * v,int32_t size)158 static int32_t read_convert_from_buffer(
159 RingBuffer<uint8_t>& buffer,
160 DSPCOMPLEX *v, int32_t size)
161 {
162 int32_t amount, i;
163 std::vector<uint8_t> tempBuffer(2 * size);
164
165 // Get data from the ring buffer
166 amount = buffer.getDataFromBuffer(tempBuffer.data(), 2 * size);
167 for (i = 0; i < amount / 2; i ++)
168 v[i] = DSPCOMPLEX(((float)tempBuffer[2 * i] - 128.0f) / 128.0f,
169 ((float)tempBuffer[2 * i + 1] - 128.0f) / 128.0f);
170 return amount / 2;
171 }
172
getSamples(DSPCOMPLEX * v,int32_t size)173 int32_t CRTL_TCP_Client::getSamples(DSPCOMPLEX *v, int32_t size)
174 {
175 return read_convert_from_buffer(sampleBuffer, v, size);
176 }
177
getSpectrumSamples(int size)178 std::vector<DSPCOMPLEX> CRTL_TCP_Client::getSpectrumSamples(int size)
179 {
180 std::vector<DSPCOMPLEX> buffer(size);
181 int sizeRead = read_convert_from_buffer(spectrumSampleBuffer, buffer.data(), size);
182 if (sizeRead < size) {
183 buffer.resize(sizeRead);
184 }
185 return buffer;
186 }
187
getSamplesToRead(void)188 int32_t CRTL_TCP_Client::getSamplesToRead(void)
189 {
190 return sampleBuffer.GetRingBufferReadAvailable() / 2;
191 }
192
reset(void)193 void CRTL_TCP_Client::reset(void)
194 {
195 sampleBuffer.FlushRingBuffer();
196 sampleNetworkBuffer.FlushRingBuffer();
197 spectrumSampleBuffer.FlushRingBuffer();
198 firstFilledNetworkBuffer = false;
199 }
200
receiveData(void)201 void CRTL_TCP_Client::receiveData(void)
202 {
203 std::vector<uint8_t> buffer(8192);
204
205 size_t read = 0;
206
207 while (sock.valid() && read < buffer.size()) {
208 const size_t remain = buffer.size() - read;
209
210 ssize_t ret = sock.recv(buffer.data() + read, remain, 0);
211
212 if (ret == 0) {
213 handleDisconnect();
214 }
215 else if (ret == -1) {
216 if (errno == EAGAIN) {
217 continue;
218 }
219 else if (errno == EINTR) {
220 continue;
221 }
222 else if (errno == ECONNRESET || errno == EBADF) {
223 handleDisconnect();
224 }
225 else {
226 std::string errstr = strerror(errno);
227 throw std::runtime_error("recv: " + errstr);
228 }
229 }
230 else {
231 read += ret;
232 }
233
234 if (not rtlsdrRunning) {
235 break;
236 }
237 }
238
239 if (firstData) {
240 firstData = false;
241
242 // Get dongle information
243 ::memcpy(&dongleInfo, buffer.data(), sizeof(dongle_info_t));
244
245 // Convert the byte order
246 dongleInfo.tuner_type = ntohl(dongleInfo.tuner_type);
247 dongleInfo.tuner_gain_count = ntohl(dongleInfo.tuner_gain_count);
248
249 if(dongleInfo.magic[0] == 'R' &&
250 dongleInfo.magic[1] == 'T' &&
251 dongleInfo.magic[2] == 'L' &&
252 dongleInfo.magic[3] == '0') {
253 std::string TunerType;
254 switch(dongleInfo.tuner_type)
255 {
256 case RTLSDR_TUNER_UNKNOWN: TunerType = "Unknown"; break;
257 case RTLSDR_TUNER_E4000: TunerType = "E4000"; break;
258 case RTLSDR_TUNER_FC0012: TunerType = "FC0012"; break;
259 case RTLSDR_TUNER_FC0013: TunerType = "FC0013"; break;
260 case RTLSDR_TUNER_FC2580: TunerType = "FC2580"; break;
261 case RTLSDR_TUNER_R820T: TunerType = "R820T"; break;
262 case RTLSDR_TUNER_R828D: TunerType = "R828D"; break;
263 default: TunerType = "Unknown";
264 }
265 std::clog << "RTL_TCP_CLIENT: Tuner type: " <<
266 dongleInfo.tuner_type << " " << TunerType << std::endl;
267 std::clog << "RTL_TCP_CLIENT: Tuner gain count: " <<
268 dongleInfo.tuner_gain_count << std::endl;
269 }
270 else {
271 std::clog << "RTL_TCP_CLIENT: Didn't find the \"RTL0\" magic key." <<
272 std::endl;
273 }
274 }
275
276 sampleNetworkBuffer.putDataIntoBuffer(buffer.data(), buffer.size());
277
278 // First fill the complete buffer to avoid sound outtages if the stream data rate is not stable e.g. over WIFI
279 if(!firstFilledNetworkBuffer) {
280 float bufferFill = (float) sampleNetworkBuffer.GetRingBufferReadAvailable() / sampleNetworkBuffer.GetBufferSize() * 100;
281
282 // Wait for 50% filled buffer
283 if(bufferFill >= 50)
284 firstFilledNetworkBuffer = true;
285
286 if((getMyTime() - oldTime_us > 100e3) || firstFilledNetworkBuffer) {
287 //std::clog << "RTL_TCP_CLIENT: Fill network buffer " << bufferFill << "%" << std::endl;
288 oldTime_us = getMyTime();
289 }
290 }
291
292
293 // Check if device is overloaded
294 minAmplitude = 255;
295 maxAmplitude = 0;
296
297 for (const auto b : buffer) {
298 if (minAmplitude > b)
299 minAmplitude = b;
300 if (maxAmplitude < b)
301 maxAmplitude = b;
302 }
303 }
304
handleDisconnect()305 void CRTL_TCP_Client::handleDisconnect()
306 {
307 connected = false;
308 firstData = true;
309 radioController.onMessage(message_level_t::Error,
310 QT_TRANSLATE_NOOP("CRadioController", "RTL-TCP connection closed."));
311 sock.close();
312 }
313
sendCommand(uint8_t cmd,int32_t param)314 void CRTL_TCP_Client::sendCommand(uint8_t cmd, int32_t param)
315 {
316 if (!connected || !sock.valid()) {
317 return;
318 }
319
320 std::vector<uint8_t> datagram;
321
322 datagram.resize(5);
323 datagram[0] = cmd; // command to set rate
324 datagram[4] = param & 0xFF; //lsb last
325 datagram[3] = (param >> ONE_BYTE) & 0xFF;
326 datagram[2] = (param >> (2 * ONE_BYTE)) & 0xFF;
327 datagram[1] = (param >> (3 * ONE_BYTE)) & 0xFF;
328 sock.send(datagram.data(), datagram.size(), 0);
329 }
330
sendVFO(int32_t frequency)331 void CRTL_TCP_Client::sendVFO(int32_t frequency)
332 {
333 sendCommand(0x01, frequency);
334 }
335
sendRate(int32_t theRate)336 void CRTL_TCP_Client::sendRate(int32_t theRate)
337 {
338 sendCommand(0x02, theRate);
339 }
340
setGainMode(int32_t gainMode)341 void CRTL_TCP_Client::setGainMode(int32_t gainMode)
342 {
343 sendCommand (0x03, gainMode);
344 }
345
getGain() const346 float CRTL_TCP_Client::getGain() const
347 {
348 return currentGain;
349 }
350
setGain(int32_t gain)351 float CRTL_TCP_Client::setGain(int32_t gain)
352 {
353 currentGainCount = gain;
354 float gainValue = getGainValue(gain);
355
356 sendCommand(0x04, (int)10 * gainValue);
357
358 currentGain = gainValue;
359 return gainValue;
360 }
361
getGainCount()362 int32_t CRTL_TCP_Client::getGainCount()
363 {
364 int32_t MaxGainCount = 0;
365 switch (dongleInfo.tuner_type) {
366 case RTLSDR_TUNER_E4000: MaxGainCount = e4k_gains.size(); break;
367 case RTLSDR_TUNER_FC0012: MaxGainCount = fc0012_gains.size(); break;
368 case RTLSDR_TUNER_FC0013: MaxGainCount = fc0013_gains.size(); break;
369 case RTLSDR_TUNER_FC2580: MaxGainCount = fc2580_gains.size(); break;
370 case RTLSDR_TUNER_R820T: MaxGainCount = r82xx_gains.size(); break;
371 case RTLSDR_TUNER_R828D: MaxGainCount = r82xx_gains.size(); break;
372 default: MaxGainCount = 29; // Most likely it is the R820T tuner
373 }
374
375 return MaxGainCount;
376 }
377
setAgc(bool AGC)378 void CRTL_TCP_Client::setAgc(bool AGC)
379 {
380 isAGC = AGC;
381 }
382
383 //void CRTL_TCP_Client::setHwAgc(bool hwAGC)
384 //{
385 // isHwAGC = hwAGC;
386 // sendCommand(0x08, hwAGC ? 1 : 0);
387 //}
388
getDescription()389 std::string CRTL_TCP_Client::getDescription()
390 {
391 return "rtl_tcp_client (server: " +
392 serverAddress + ":" +
393 std::to_string(serverPort) + ")";
394 }
395
getID()396 CDeviceID CRTL_TCP_Client::getID()
397 {
398 return CDeviceID::RTL_TCP;
399 }
400
setServerAddress(const std::string & serverAddress)401 void CRTL_TCP_Client::setServerAddress(const std::string& serverAddress)
402 {
403 this->serverAddress = serverAddress;
404 }
405
setPort(uint16_t Port)406 void CRTL_TCP_Client::setPort(uint16_t Port)
407 {
408 serverPort = Port;
409 }
410
receiveAndReconnect()411 void CRTL_TCP_Client::receiveAndReconnect()
412 {
413 while (rtlsdrRunning) {
414 std::unique_lock<std::mutex> lock(mutex);
415
416 if (!connected) {
417 std::clog << "RTL_TCP_CLIENT: Try to connect to server " <<
418 serverAddress << ":" << serverPort << std::endl;
419
420 try {
421 connected = sock.connect(serverAddress, serverPort, 2);
422 }
423 catch(const std::runtime_error& e) {
424 std::clog << "RTL_TCP_CLIENT: " << e.what() << std::endl;
425 }
426
427 if (connected) {
428 std::clog << "RTL_TCP_CLIENT: Successful connected to server " <<
429 std::endl;
430
431 // Always use manual gain, the AGC is implemented in software
432 setGainMode(1);
433 setGain(currentGainCount);
434 sendRate(INPUT_RATE);
435 sendVFO(frequency);
436
437 if (!agcRunning) {
438 agcRunning = true;
439 agcThread = std::thread(&CRTL_TCP_Client::agcTimer, this);
440 }
441 firstData = true;
442 reset(); // Clear buffers
443 }
444 else {
445 std::clog << "RTL_TCP_CLIENT: Could not connect to server" <<
446 std::endl;
447
448 agcRunning = false;
449 rtlsdrRunning = false;
450 lock.unlock();
451
452 radioController.onMessage(message_level_t::Error,
453 QT_TRANSLATE_NOOP("CRadioController", "Connection failed to server "),
454 serverAddress + ":" + std::to_string(serverPort));
455 }
456 }
457
458 if (connected) {
459 if (lock.owns_lock()) {
460 lock.unlock();
461 }
462
463 receiveData();
464 }
465 }
466 }
467
468 #define NETWORK_BUFFER_READ_SAMPLES 32768
networkBufferCopy()469 void CRTL_TCP_Client::networkBufferCopy()
470 {
471 std::vector<uint8_t> tempBuffer(NETWORK_BUFFER_READ_SAMPLES * 2);
472
473 while (rtlsdrRunning) {
474 if(!firstFilledNetworkBuffer) {
475 std::this_thread::sleep_for(std::chrono::milliseconds(100));
476 nextStop_us = getMyTime();
477 continue;
478 }
479
480 int32_t samples = NETWORK_BUFFER_READ_SAMPLES;
481
482 // Figure out the max samples to read from network buffer
483 int32_t samplesInBuffer = sampleNetworkBuffer.GetRingBufferReadAvailable() / 2;
484 if(samplesInBuffer < samples)
485 samples = samplesInBuffer;
486
487 if(samples == 0) {
488 std::this_thread::sleep_for(std::chrono::milliseconds(100));
489 nextStop_us = getMyTime();
490 continue;
491 }
492
493 // Read data
494 int32_t amount = sampleNetworkBuffer.getDataFromBuffer(tempBuffer.data(), 2 * samples);
495
496 // Write data to standard buffers
497 sampleBuffer.putDataIntoBuffer(tempBuffer.data(), amount);
498 spectrumSampleBuffer.putDataIntoBuffer(tempBuffer.data(), amount);
499
500 if(getMyTime() - oldTime_us > 500e3) { // 500 ms
501
502 float bufferFill = (float) sampleNetworkBuffer.GetRingBufferReadAvailable() / sampleNetworkBuffer.GetBufferSize() * 100;
503 //std::clog << "RTL_TCP_CLIENT: Network buffer fill level " << bufferFill << "%" << std::endl;
504
505 oldTime_us = getMyTime();
506 }
507
508
509 uint32_t period_us = samples / ((float) INPUT_RATE / 1e6);
510 nextStop_us += period_us;
511 int64_t timeToWait_us = nextStop_us - getMyTime();
512
513 // Send thread to sleep
514 std::this_thread::sleep_for(std::chrono::microseconds(timeToWait_us));
515 }
516 }
517
agcTimer(void)518 void CRTL_TCP_Client::agcTimer(void)
519 {
520 while (agcRunning) {
521 std::this_thread::sleep_for(std::chrono::milliseconds(50));
522
523 if (isAGC && (dongleInfo.tuner_type != RTLSDR_TUNER_UNKNOWN)) {
524 // Check for overloading
525 if (minAmplitude == 0 || maxAmplitude == 255) {
526 // We have to decrease the gain
527 if(currentGainCount > 0) {
528 setGain(currentGainCount - 1);
529 //std::clog << "RTL_TCP_CLIENT: Decrease gain to " << (float)currentGain << std::endl;
530 }
531 }
532 else {
533 if (currentGainCount < (getGainCount() - 1)) {
534 // Calc if a gain increase overloads the device. Calc it
535 // from the gain values
536 const float newGain = getGainValue(currentGainCount + 1);
537 const float deltaGain = newGain - currentGain;
538 const float linGain = pow(10, deltaGain / 20);
539 const int newMaxValue = (float)maxAmplitude * linGain;
540 const int newMinValue = (float)minAmplitude / linGain;
541
542 // We have to increase the gain
543 if (newMinValue >=0 && newMaxValue <= 255) {
544 setGain(currentGainCount + 1);
545 //std::clog << "RTL_TCP_CLIENT: Increase gain to " << currentGain << std::endl;
546 }
547 }
548 }
549 }
550 else { // AGC is off or unknown tuner
551 if (minAmplitude == 0 || maxAmplitude == 255) {
552 std::string text = QT_TRANSLATE_NOOP("CRadioController", "ADC overload."
553 " Maybe you are using a too high gain.");
554 std::clog << "RTL_TCP_CLIENT:" << text << std::endl;
555 radioController.onMessage(message_level_t::Information, text);
556 }
557 }
558 }
559 }
560
getGainValue(uint16_t gainCount)561 float CRTL_TCP_Client::getGainValue(uint16_t gainCount)
562 {
563 float gainValue = 0;
564
565 if (dongleInfo.tuner_type == RTLSDR_TUNER_UNKNOWN)
566 return 0;
567
568 // Get max gain count
569 uint32_t maxGainCount = getGainCount();
570 if (maxGainCount == 0)
571 return 0;
572
573 // Check if gainCount is valid
574 if (gainCount < maxGainCount) {
575 // Get gain
576 switch(dongleInfo.tuner_type) {
577 case RTLSDR_TUNER_E4000: gainValue = e4k_gains[gainCount]; break;
578 case RTLSDR_TUNER_FC0012: gainValue = fc0012_gains[gainCount]; break;
579 case RTLSDR_TUNER_FC0013: gainValue = fc0013_gains[gainCount]; break;
580 case RTLSDR_TUNER_FC2580: gainValue = fc2580_gains[gainCount]; break;
581 case RTLSDR_TUNER_R820T: gainValue = r82xx_gains[gainCount]; break;
582 case RTLSDR_TUNER_R828D: gainValue = r82xx_gains[gainCount]; break;
583 default: gainValue = 0;
584 }
585 }
586 else {
587 gainValue = 999.0; // Max gain
588 }
589
590 return gainValue;
591 }
592
593