Instrument Neutral Distributed Interface INDI 2.2.5
indi_rtlsdr.cpp
Go to the documentation of this file.
1/*
2 indi_rtlsdr - a software defined radio driver for INDI
3 Copyright (C) 2017 Ilia Platone - Jasem Mutlaq
4 Collaborators:
5 - Ilia Platone <[email protected]>
6 - Jasem Mutlaq - INDI library - <http://indilib.org>
7 - Monroe Pattillo - Fox Observatory - South Florida Amateur Astronomers Association <http://www.sfaaa.com/>
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22*/
23
24#include "indi_rtlsdr.h"
25#include <rtl-sdr.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <termios.h>
30#include <indilogger.h>
31#include <memory>
32#include <deque>
33#include <indicom.h>
34
35#define min(a, b) \
36 ({ \
37 __typeof__(a) _a = (a); \
38 __typeof__(b) _b = (b); \
39 _a < _b ? _a : _b; \
40 })
41#define MAX_TRIES 20
42#define SUBFRAME_SIZE (16384)
43#define MIN_FRAME_SIZE (512)
44#define MAX_FRAME_SIZE (SUBFRAME_SIZE * 16)
45#define SPECTRUM_SIZE (256)
46
47static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
48static pthread_mutex_t condMutex = PTHREAD_MUTEX_INITIALIZER;
49
50void RTLSDR::Callback()
51{
52 LOG_INFO("Integration started...");
53 b_read = 0;
54 n_read = 0;
55 setBufferSize(getSampleRate() * IntegrationRequest * getBPS() / 16);
58 buffer = (unsigned char *)realloc(buffer, min(MAX_FRAME_SIZE, getBufferSize()));
60 rtlsdr_reset_buffer(rtl_dev);
61 else
62 tcflush(PortFD, TCIFLUSH);
63 setIntegrationTime(IntegrationRequest);
64 InIntegration = true;
65 real = getReal();
66 imaginary = getImaginary();
67 gettimeofday(&IntStart, nullptr);
68 while (InIntegration)
69 {
70 if (to_read > 0)
71 {
73 rtlsdr_read_sync(rtl_dev, buffer + b_read, min(MAX_FRAME_SIZE, to_read), &n_read);
74 else
76
77 if (n_read > 0) {
78 b_read += n_read;
79 to_read -= n_read;
80 }
81 } else {
82 for(int x = 0; x < getBufferSize() / 2; x++) {
83 real[x] = buffer[x*2];
84 imaginary[x] = buffer[x*2+1];
85 }
86 setReal(real);
87 setImaginary(imaginary);
88 if(!streamPredicate)
89 {
90 InIntegration = false;
92 }
93 else
94 {
95 Streamer->newFrame(getBuffer(), getBufferSize());
97 b_read = 0;
98 n_read = 0;
99 }
100 LOG_INFO("Download complete.");
101 }
102 }
103}
104
105static class Loader
106{
107 std::deque<std::unique_ptr<RTLSDR>> receivers;
108 public:
109 Loader()
110 {
111 size_t numofConnectedReceivers = rtlsdr_get_device_count();
112 if (numofConnectedReceivers == 0)
113 {
114 //Try sending IDMessage as well?
115 IDLog("No USB RTLSDR receivers detected. Power on?");// Trying with TCP..");
116 IDMessage(nullptr, "No USB RTLSDR receivers detected. Power on?");// Trying with TCP..");
117 // receivers.push_back(std::unique_ptr<RTLSDR>(new RTLSDR(-1)));
118 return;
119 }
120
121 for (size_t i = 0; i < numofConnectedReceivers; i++)
122 {
123 receivers.push_back(std::unique_ptr<RTLSDR>(new RTLSDR(i)));
124 }
125
126 }
127} loader;
128
129RTLSDR::RTLSDR(int32_t index)
130{
131 InIntegration = false;
132 if(index < 0)
133 {
135 }
136
137 receiverIndex = index;
138
139 char name[MAXINDIDEVICE];
140 snprintf(name, MAXINDIDEVICE, "%s %s%c", getDefaultName(), index < 0 ? "TCP" : "USB", index < 0 ? '\0' : index + '1');
141 setDeviceName(name);
142
143 // We set the Receiver capabilities
146 buffer = (unsigned char*)malloc(1);
147}
148
150{
152 {
153 int r = rtlsdr_open(&rtl_dev, static_cast<uint32_t>(receiverIndex));
154 if (r < 0)
155 {
156 LOGF_ERROR("Failed to open rtlsdr device index %d.", receiverIndex);
157 return false;
158 }
159 }
160
161 return true;
162}
163/**************************************************************************************
164** Client is asking us to terminate connection to the device
165***************************************************************************************/
167{
168 InIntegration = false;
170 {
171 rtlsdr_close(rtl_dev);
172 }
173 PortFD = -1;
174
175 setBufferSize(1);
176 pthread_mutex_lock(&condMutex);
177 streamPredicate = 1;
178 terminateThread = true;
179 pthread_cond_signal(&cv);
180 pthread_mutex_unlock(&condMutex);
181 LOG_INFO("RTL-SDR Receiver disconnected successfully!");
182 return true;
183}
184
185/**************************************************************************************
186** INDI is asking us for our default device name
187***************************************************************************************/
189{
190 return "RTL-SDR Receiver";
191}
192
193/**************************************************************************************
194** INDI is asking us to init our properties.
195***************************************************************************************/
197{
198 // Must init parent properties first!
200
201 setMinMaxStep("SENSOR_INTEGRATION", "SENSOR_INTEGRATION_VALUE", 0.001, 600, 0.001);
202 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_FREQUENCY", 2.4e+7, 2.0e+9, 1);
203 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_SAMPLERATE", 2.5e+5, 2.0e+6, 2.5e+5);
204 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_GAIN", 0.0, 25.0, 0.1);
205 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_BANDWIDTH", 2.5e+5, 2.0e+6, 2.5e+5);
206 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_BITSPERSAMPLE", 16, 16, 1);
207 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_ANTENNA", 1, 1, 0, true);
209
210 // Add Debug, Simulator, and Configuration controls
212
214 return true;
215}
216
217/********************************************************************************************
218** INDI is asking us to update the properties because there is a change in CONNECTION status
219** This function is called whenever the device is connected or disconnected.
220*********************************************************************************************/
222{
223 // Call parent update properties first
225
226 if (isConnected())
227 {
228 // Initial values
229 setupParams(1000000, 1420000000, 10);
230
231 // Start the timer
233 }
234
235 return true;
236}
237
238/**************************************************************************************
239** Setting up Receiver parameters
240***************************************************************************************/
241void RTLSDR::setupParams(float sr, float freq, float gain)
242{
243 int r = 0;
244
246 {
247 r |= rtlsdr_set_tuner_gain_mode(rtl_dev, 1);
248 r |= rtlsdr_set_tuner_gain(rtl_dev, static_cast<int>(gain * 10));
249 r |= rtlsdr_set_center_freq(rtl_dev, static_cast<uint32_t>(freq));
250 r |= rtlsdr_set_sample_rate(rtl_dev, static_cast<uint32_t>(sr));
251 r |= rtlsdr_set_tuner_bandwidth(rtl_dev, static_cast<uint32_t>(sr));
252 if (r != 0)
253 {
254 LOG_INFO("Issue(s) setting parameters.");
255 }
256
257 setBPS(16);
258 setGain(static_cast<double>(rtlsdr_get_tuner_gain(rtl_dev)) / 10.0);
259 setFrequency(static_cast<double>(rtlsdr_get_center_freq(rtl_dev)));
260 setSampleRate(static_cast<double>(rtlsdr_get_sample_rate(rtl_dev)));
261 setBandwidth(static_cast<double>(rtlsdr_get_sample_rate(rtl_dev)));
262 }
263 else
264 {
265 sendTcpCommand(CMD_SET_FREQ, static_cast<int>(freq));
266 sendTcpCommand(CMD_SET_SAMPLE_RATE, static_cast<int>(sr));
267 sendTcpCommand(CMD_SET_TUNER_GAIN_MODE, 0);
268 sendTcpCommand(CMD_SET_GAIN, static_cast<int>(gain * 10));
269 sendTcpCommand(CMD_SET_FREQ_COR, 0);
270 sendTcpCommand(CMD_SET_AGC_MODE, 0);
271 sendTcpCommand(CMD_SET_TUNER_GAIN_INDEX, 0);
272
273 setBPS(16);
274 setGain(gain);
275 setFrequency(freq);
276 setSampleRate(sr);
277 setBandwidth(sr);
278 }
279}
280
281bool RTLSDR::sendTcpCommand(int cmd, int value)
282{
283 unsigned char tosend[5];
284 tosend[0] = static_cast<unsigned char>(cmd);
285 tosend[1] = value & 0xff;
286 value >>= 8;
287 tosend[2] = value & 0xff;
288 value >>= 8;
289 tosend[3] = value & 0xff;
290 value >>= 8;
291 tosend[4] = value & 0xff;
292 value >>= 8;
293 tcflush(PortFD, TCOFLUSH);
294 int count = 0;
295 while(count < 5)
296 {
297 count = write(PortFD, tosend, 5);
298 if (count < 0) return false;
299 }
300 return true;
301}
302
303bool RTLSDR::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
304{
305 bool r = false;
306 if (dev && !strcmp(dev, getDeviceName()) && !strcmp(name, ReceiverSettingsNP.name))
307 {
308 for(int i = 0; i < n; i++)
309 {
310 if (!strcmp(names[i], "RECEIVER_GAIN"))
311 {
312 setupParams(getSampleRate(), getFrequency(), values[i]);
313 }
314 else if (!strcmp(names[i], "RECEIVER_FREQUENCY"))
315 {
316 setupParams(getSampleRate(), values[i], getGain());
317 }
318 else if (!strcmp(names[i], "RECEIVER_SAMPLERATE"))
319 {
320 setupParams(values[i], getFrequency(), getGain());
321 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_BANDWIDTH", getSampleRate(), getSampleRate(), getSampleRate(), false);
322 }
323 }
324 values[RECEIVER_GAIN] = getGain();
328 values[RECEIVER_BITSPERSAMPLE] = 16;
329 IUUpdateNumber(&ReceiverSettingsNP, values, names, n);
331 }
332 return processNumber(dev, name, values, names, n) & !r;
333}
334
335/**************************************************************************************
336** Client is asking us to start an exposure
337***************************************************************************************/
338bool RTLSDR::StartIntegration(double duration)
339{
340 IntegrationRequest = static_cast<float>(duration);
342
343 // Run threads
344 std::thread(&RTLSDR::Callback, this).detach();
345 return true;
346}
347
348/**************************************************************************************
349** Client is asking us to abort a capture
350***************************************************************************************/
352{
353 if (InIntegration)
354 {
355 InIntegration = false;
356 }
357 return true;
358}
359
360/**************************************************************************************
361** How much longer until exposure is done?
362***************************************************************************************/
363float RTLSDR::CalcTimeLeft()
364{
365 float timesince;
366 float timeleft;
367 struct timeval now;
368 gettimeofday(&now, nullptr);
369
370 timesince = (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) -
371 (double)(IntStart.tv_sec * 1000.0 + IntStart.tv_usec / 1000);
372 timesince = timesince / 1000;
373
374 timeleft = IntegrationRequest - timesince;
375 return timeleft;
376}
377
378/**************************************************************************************
379** Main device loop. We check for capture progress here
380***************************************************************************************/
382{
383 long timeleft;
384
385 if (isConnected() == false)
386 return; // No need to reset timer if we are not connected anymore
387
388 if (InIntegration)
389 {
390 timeleft = static_cast<long>(CalcTimeLeft());
391 if (timeleft < 0.1)
392 {
393 /* We're done capturing */
394 LOG_INFO("Integration done, expecting data...");
395 timeleft = 0.0;
396 }
397
398 // This is an over simplified timing method, check ReceiverSimulator and rtlsdrReceiver for better timing checks
399 setIntegrationLeft(timeleft);
400 }
401
403 return;
404}
405
406/**************************************************************************************
407** Create the spectrum
408***************************************************************************************/
410{
411}
412
413//Streamer API functions
414
416{
417 pthread_mutex_lock(&condMutex);
418 streamPredicate = 1;
419 StartIntegration(1.0 / Streamer->getTargetFPS());
420 pthread_mutex_unlock(&condMutex);
421 pthread_cond_signal(&cv);
422
423 return true;
424}
425
427{
428 pthread_mutex_lock(&condMutex);
429 streamPredicate = 0;
430 pthread_mutex_unlock(&condMutex);
431 pthread_cond_signal(&cv);
432
433 return true;
434}
435
437{
439 {
440 if(PortFD == -1)
441 {
442 LOG_ERROR("Failed to connect to rtl_tcp server.");
443 return false;
444 }
445 }
446
447 streamPredicate = 0;
448 terminateThread = false;
449 LOG_INFO("RTL-SDR Receiver connected successfully!");
450 // Let's set a timer that checks teleReceivers status every POLLMS milliseconds.
451 // JM 2017-07-31 SetTimer already called in updateProperties(). Just call it once
452 //SetTimer(getCurrentPollingPeriod());
453
454 return true;
455}
456
bool isConnected() const
void setDeviceName(const char *dev)
Set the device name.
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void setGain(double gain)
setGain Set gain of Receiver device.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
void setBandwidth(double bandwidth)
setBandwidth Set bandwidth of Receiver device.
void setBPS(int BPS)
setBPS Set the bits per sample.
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual void setMinMaxStep(const char *property, const char *element, double min, double max, double step, bool sendToClient=true) override
setMinMaxStep for a number property element
void setFrequency(double freq)
setFrequency Set the frequency observed.
INumberVectorProperty ReceiverSettingsNP
double getSampleRate()
getSampleRate Get requested sample rate for the sensor in Hz.
double getFrequency()
getFrequency Get requested integration frequency for the sensor in Hz.
double getBandwidth()
getBandwidth Get requested integration bandwidth for the sensor in Hz.
void SetReceiverCapability(uint32_t cap)
SetReceiverCapability Set the Receiver capabilities. Al fields must be initialized.
void setSampleRate(double sr)
setSampleRate Set depth of Receiver device.
double getGain()
getGain Get requested integration gain for the sensor.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
uint8_t * getReal()
getBuffer Get real buffer of the stream of the Sensor device.
void setBufferSize(int nbuf, bool allocMem=true)
setBufferSize Set desired buffer size. The function will allocate memory accordingly....
void setSensorConnection(const uint8_t &value)
setSensorConnection Set Sensor connection mode. Child class should call this in the constructor befor...
void setIntegrationLeft(double duration)
setIntegrationLeft Update Integration time left. Inform the client of the new Integration time left v...
void setImaginary(uint8_t *buffer)
setBuffer Set imaginary frame buffer pointer.
uint8_t * getImaginary()
getBuffer Get imaginary buffer of the stream of the Sensor device.
std::unique_ptr< StreamManager > Streamer
void setIntegrationTime(double duration)
setIntegrationTime Set desired Sensor frame Integration duration for next Integration....
int PortFD
For Serial & TCP connections.
int getBufferSize() const
getContinuumBufferSize Get allocated continuum buffer size to hold the Sensor integrationd stream.
virtual bool IntegrationComplete()
Uploads target Device exposed buffer as FITS to the client. Derived classes should class this functio...
uint8_t * getBuffer()
getBuffer Get continuum buffer of the stream of the Sensor device.
void setReal(uint8_t *buffer)
setBuffer Set real frame buffer pointer.
int getBPS() const
getBPS Get Sensor depth (bits per sample).
void setIntegrationFileExtension(const char *ext)
setIntegrationExtension Set integration extension
int to_read
Definition indi_rtlsdr.h:44
bool InIntegration
Definition indi_rtlsdr.h:46
bool AbortIntegration() override
Abort ongoing Integration.
RTLSDR(int32_t index)
bool StopStreaming() override
StopStreaming Stop live video streaming.
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
bool Disconnect() override
Disconnect from device.
rtlsdr_dev * rtl_dev
Definition indi_rtlsdr.h:43
bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
uint8_t * buffer
Definition indi_rtlsdr.h:47
void setupParams(float sr, float freq, float gain)
bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
const char * getDefaultName() override
bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
bool Handshake() override
perform handshake with device to check communication
int n_read
Definition indi_rtlsdr.h:48
void grabData()
bool StartIntegration(double duration) override
Start integration from the Sensor device.
int b_read
Definition indi_rtlsdr.h:48
bool StartStreaming() override
StartStreaming Start live video streaming.
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
#define min(a, b)
#define MAX_FRAME_SIZE
#define MAXINDIDEVICE
Definition indiapi.h:191
void IDLog(const char *fmt,...)
Definition indicom.c:305
Implementations for common driver routines.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDMessage(const char *dev, const char *fmt,...)
Definition indidriver.c:983
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:321