Instrument Neutral Distributed Interface INDI  2.0.2
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 
33 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
34 static pthread_mutex_t condMutex = PTHREAD_MUTEX_INITIALIZER;
35 std::unique_ptr<RadioSim> receiver(new RadioSim());
36 
38 {
39  streamPredicate = 0;
40  terminateThread = false;
41 }
42 
44 {
45 
46 }
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;
73  setBufferSize(1);
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
112  addAuxControls();
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 fucntion is called whenever the device is connected or disconnected.
121 *********************************************************************************************/
123 {
124  if (isConnected())
125  {
126  // Inital values
127  setupParams(1000000, 1420000000, 10000, 10);
128 
129  // Start the timer
131  }
132 
134 }
135 
136 /**************************************************************************************
137 ** Setting up Receiver parameters
138 ***************************************************************************************/
139 void 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 
149 bool 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  }
173  IDSetNumber(&ReceiverSettingsNP, nullptr);
174  }
175  return processNumber(dev, name, values, names, n) & !r;
176 }
177 
178 /**************************************************************************************
179 ** Client is asking us to start an exposure
180 ***************************************************************************************/
181 bool 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 ***************************************************************************************/
219 float 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
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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.
virtual bool initProperties() override
Initilize 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
Definition: indireceiver.h:190
double getSampleRate()
getSampleRate Get requested sample rate for the sensor in Hz.
Definition: indireceiver.h:141
double getFrequency()
getFrequency Get requested integration frequency for the sensor in Hz.
Definition: indireceiver.h:132
double getBandwidth()
getBandwidth Get requested integration bandwidth for the sensor in Hz.
Definition: indireceiver.h:114
void setSampleRate(double sr)
setSampleRate Set depth of Receiver device.
double getGain()
getGain Get requested integration gain for the sensor.
Definition: indireceiver.h:123
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 setBPS(int bps)
setBPP Set depth of Sensor device.
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...
uint8_t * getBuffer()
getBuffer Get raw 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 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. Dervied classes should class this functio...
int getBPS() const
getBPS Get Sensor depth (bits per sample).
void setIntegrationFileExtension(const char *ext)
setIntegrationExtension Set integration exntension
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
Initilize 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
Definition: indibasetypes.h:71
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
#define LOG_INFO(txt)
Definition: indilogger.h:74
std::unique_ptr< RadioSim > receiver(new RadioSim())
char name[MAXINDINAME]
Definition: indiapi.h:323