Instrument Neutral Distributed Interface INDI 2.2.5
receiver_simulator.cpp
Go to the documentation of this file.
1/*
2 indi_RadioSim_detector - a software defined radio driver for INDI
3 Copyright (C) 2017 Ilia Platone
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19
20#include "receiver_simulator.h"
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <indilogger.h>
25#include <memory>
26
27#define SPECTRUM_SIZE (256)
28#define min(a,b) \
29 ({ __typeof__ (a) _a = (a); \
30 __typeof__ (b) _b = (b); \
31 _a < _b ? _a : _b; })
32
33static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
34static pthread_mutex_t condMutex = PTHREAD_MUTEX_INITIALIZER;
35std::unique_ptr<RadioSim> receiver(new RadioSim());
36
38{
39 streamPredicate = 0;
40 terminateThread = false;
41}
42
47
48/**************************************************************************************
49** Client is asking us to establish connection to the device
50***************************************************************************************/
52{
53 LOG_INFO("Simulator Receiver connected successfully!");
54 // Let's set a timer that checks teleReceivers status every POLLMS milliseconds.
55 // JM 2017-07-31 SetTimer already called in updateProperties(). Just call it once
56 //SetTimer(getCurrentPollingPeriod());
57
58 streamPredicate = 0;
59 terminateThread = false;
60 // Run threads
61 std::thread(&RadioSim::streamCaptureHelper, this).detach();
63
64 return true;
65}
66
67/**************************************************************************************
68** Client is asking us to terminate connection to the device
69***************************************************************************************/
71{
72 InIntegration = false;
74 pthread_mutex_lock(&condMutex);
75 streamPredicate = 1;
76 terminateThread = true;
77 pthread_cond_signal(&cv);
78 pthread_mutex_unlock(&condMutex);
79 LOG_INFO("Simulator Receiver disconnected successfully!");
80 return true;
81}
82
83/**************************************************************************************
84** INDI is asking us for our default device name
85***************************************************************************************/
87{
88 return "Receiver Simulator";
89}
90
91/**************************************************************************************
92** INDI is asking us to init our properties.
93***************************************************************************************/
95{
96 // We set the Receiver capabilities
98 SetCapability(cap);
99
100 // Must init parent properties first!
102
103 setMinMaxStep("SENSOR_INTEGRATION", "SENSOR_INTEGRATION_VALUE", 0.001, 86164.092, 0.001, false);
104 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_FREQUENCY", 2.4e+7, 2.0e+9, 1, false);
105 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_SAMPLERATE", 1.0e+6, 2.0e+6, 1, false);
106 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_GAIN", 0.0, 25.0, 0.1, false);
107 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_BANDWIDTH", 0, 0, 0, false);
108 setMinMaxStep("RECEIVER_SETTINGS", "RECEIVER_BITSPERSAMPLE", 16, 16, 0, false);
110
111 // Add Debug, Simulator, and Configuration controls
113
115 return true;
116}
117
118/********************************************************************************************
119** INDI is asking us to update the properties because there is a change in CONNECTION status
120** This function is called whenever the device is connected or disconnected.
121*********************************************************************************************/
123{
124 if (isConnected())
125 {
126 // Initial values
127 setupParams(1000000, 1420000000, 10000, 10);
128
129 // Start the timer
131 }
132
134}
135
136/**************************************************************************************
137** Setting up Receiver parameters
138***************************************************************************************/
139void RadioSim::setupParams(float sr, float freq, float bw, float gain)
140{
141 // Our Receiver is an 8 bit Receiver, 100MHz frequency 1MHz bandwidth.
142 setFrequency(freq);
143 setSampleRate(sr);
144 setBPS(16);
145 setBandwidth(bw);
146 setGain(gain);
147}
148
149bool RadioSim::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
150{
151 bool r = false;
152 if (dev && !strcmp(dev, getDeviceName()) && !strcmp(name, ReceiverSettingsNP.name))
153 {
154 for(int i = 0; i < n; i++)
155 {
156 if (!strcmp(names[i], "RECEIVER_GAIN"))
157 {
158 setupParams(getSampleRate(), getFrequency(), getBandwidth(), values[i]);
159 }
160 else if (!strcmp(names[i], "RECEIVER_BANDWIDTH"))
161 {
162 setupParams(getSampleRate(), getFrequency(), values[i], getGain());
163 }
164 else if (!strcmp(names[i], "RECEIVER_FREQUENCY"))
165 {
166 setupParams(getSampleRate(), values[i], getBandwidth(), getGain());
167 }
168 else if (!strcmp(names[i], "RECEIVER_SAMPLERATE"))
169 {
170 setupParams(values[i], getFrequency(), getBandwidth(), getGain());
171 }
172 }
174 }
175 return processNumber(dev, name, values, names, n) & !r;
176}
177
178/**************************************************************************************
179** Client is asking us to start an exposure
180***************************************************************************************/
181bool RadioSim::StartIntegration(double duration)
182{
183 IntegrationRequest = duration;
185
186 // Since we have only have one Receiver with one chip, we set the exposure duration of the primary Receiver
187 setIntegrationTime(duration);
188 int to_read = getSampleRate() * getIntegrationTime() * abs(getBPS()) / 8;
189
190 setBufferSize(to_read);
191 InIntegration = true;
192
193 gettimeofday(&CapStart, nullptr);
194 if(HasStreaming())
195 {
196 Streamer->setPixelFormat(INDI_MONO, getBPS());
197 Streamer->setSize(getBufferSize() * 8 / abs(getBPS()), 1);
198 }
199
200 // We're done
201 return true;
202}
203
204/**************************************************************************************
205** Client is asking us to abort a capture
206***************************************************************************************/
208{
209 if(InIntegration)
210 {
211 InIntegration = false;
212 }
213 return true;
214}
215
216/**************************************************************************************
217** How much longer until exposure is done?
218***************************************************************************************/
219float RadioSim::CalcTimeLeft()
220{
221 double timesince;
222 double timeleft;
223 struct timeval now;
224 gettimeofday(&now, nullptr);
225
226 timesince = (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) -
227 (double)(CapStart.tv_sec * 1000.0 + CapStart.tv_usec / 1000);
228 timesince = timesince / 1000;
229
230 timeleft = IntegrationRequest - timesince;
231 return timeleft;
232}
233
234/**************************************************************************************
235** Main device loop. We check for capture progress here
236***************************************************************************************/
238{
239 long timeleft;
240
241 if (isConnected() == false)
242 return; // No need to reset timer if we are not connected anymore
243
244 if (InIntegration)
245 {
246 timeleft = CalcTimeLeft();
247 if(timeleft <= 0.0)
248 {
249 /* We're done capturing */
250 LOG_INFO("Integration done, expecting data...");
251 timeleft = 0.0;
252 grabData();
253 }
254
255 // This is an over simplified timing method, check ReceiverSimulator and RadioSimReceiver for better timing checks
256 setIntegrationLeft(timeleft);
257 }
258
260 return;
261}
262
263/**************************************************************************************
264** Create the spectrum
265***************************************************************************************/
267{
268 if(InIntegration)
269 {
270
271 LOG_INFO("Downloading...");
272 InIntegration = false;
273
274 uint8_t* continuum;
275 int size = getBufferSize();
276
277 //Fill the continuum
278 continuum = getBuffer();
279 for(int i = 0; i < size; i++)
280 continuum[i] = rand() % 255;
281
282 LOG_INFO("Download complete.");
284 }
285}
286
287//Streamer API functions
288
290{
291 pthread_mutex_lock(&condMutex);
292 streamPredicate = 1;
293 pthread_mutex_unlock(&condMutex);
294 pthread_cond_signal(&cv);
295
296 return true;
297}
298
300{
301 pthread_mutex_lock(&condMutex);
302 streamPredicate = 0;
303 pthread_mutex_unlock(&condMutex);
304 pthread_cond_signal(&cv);
305
306 return true;
307}
308
310{
311 struct itimerval tframe1, tframe2;
312 double deltas;
313 getitimer(ITIMER_REAL, &tframe1);
314 auto s1 = ((double)tframe2.it_value.tv_sec) + ((double)tframe2.it_value.tv_usec / 1e6);
315 auto s2 = ((double)tframe2.it_value.tv_sec) + ((double)tframe2.it_value.tv_usec / 1e6);
316
317 while (true)
318 {
319 pthread_mutex_lock(&condMutex);
320
321 while (streamPredicate == 0)
322 {
323 pthread_cond_wait(&cv, &condMutex);
324 }
325 StartIntegration(1.0 / Streamer->getTargetFPS());
326
327 if (terminateThread)
328 break;
329
330 // release condMutex
331 pthread_mutex_unlock(&condMutex);
332
333 // Simulate exposure time
334 //usleep(ExposureRequest*1e5);
335 grabData();
336 getitimer(ITIMER_REAL, &tframe1);
337
338 s2 = ((double)tframe2.it_value.tv_sec) + ((double)tframe2.it_value.tv_usec / 1e6);
339 deltas = fabs(s2 - s1);
340
341 if (deltas < IntegrationTime)
342 usleep(fabs(IntegrationTime - deltas) * 1e6);
343
344 int32_t size = getBufferSize();
345 Streamer->newFrame(getBuffer(), size);
346
347 s1 = ((double)tframe1.it_value.tv_sec) + ((double)tframe1.it_value.tv_usec / 1e6);
348
349 getitimer(ITIMER_REAL, &tframe2);
350 }
351
352 pthread_mutex_unlock(&condMutex);
353}
bool isConnected() const
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 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)
void SetCapability(uint32_t cap)
SetCapability Set the Sensor capabilities. Al fields must be initialized.
void setBufferSize(int nbuf, bool allocMem=true)
setBufferSize Set desired buffer size. The function will allocate memory accordingly....
void setIntegrationLeft(double duration)
setIntegrationLeft Update Integration time left. Inform the client of the new Integration time left v...
std::unique_ptr< StreamManager > Streamer
void setIntegrationTime(double duration)
setIntegrationTime Set desired Sensor frame Integration duration for next Integration....
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.
int getBPS() const
getBPS Get Sensor depth (bits per sample).
void setIntegrationFileExtension(const char *ext)
setIntegrationExtension Set integration extension
double getIntegrationTime() const
getIntegrationTime Get requested Integration duration for the Sensor device in seconds.
void streamCaptureHelper()
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
const char * getDefaultName() override
bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
bool Disconnect() override
Disconnect from device.
bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
bool StopStreaming() override
StopStreaming Stop live video streaming.
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
bool StartIntegration(double duration) override
Start integration from the Sensor device.
bool StartStreaming() override
StartStreaming Start live video streaming.
bool AbortIntegration() override
Abort ongoing Integration.
@ INDI_MONO
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
std::unique_ptr< RadioSim > receiver(new RadioSim())
char name[MAXINDINAME]
Definition indiapi.h:321