SLProject  4.3.020
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SENSSimulated.cpp
Go to the documentation of this file.
1 #include "SENSSimulated.h"
2 #include "HighResTimer.h"
3 
4 //-----------------------------------------------------------------------------
5 template<typename T>
6 SENSSimulated<T>::SENSSimulated(const std::string name,
7  StartSimCB startSimCB,
8  SensorSimStoppedCB sensorSimStoppedCB,
9  std::vector<std::pair<SENSTimePt, T>>&& data,
10  const SENSSimClock& clock)
11  : _name(name),
12  _startSimCB(startSimCB),
13  _sensorSimStoppedCB(sensorSimStoppedCB),
14  _data(data),
15  _clock(clock)
16 {
17 }
18 
19 template<typename T>
21 {
22  //inform SENSSimulator about start
23  _startSimCB();
24  //stop the local simulation thread if running
25  stopSim();
26  _errorMsg.clear();
27  //start the simulation thread
28  _thread = std::thread(&SENSSimulated::feedSensor, this);
29 }
30 
31 template<typename T>
33 {
34  std::unique_lock<std::mutex> lock(_mutex);
35  _stop = true;
36  lock.unlock();
37  _condVar.notify_one();
38 
39  if (_thread.joinable())
40  _thread.join();
41 
42  lock.lock();
43  _stop = false;
44 }
45 
46 template<typename T>
47 bool SENSSimulated<T>::getErrorMsg(std::string& msg)
48 {
49  std::lock_guard<std::mutex> lock(_msgMutex);
50  if (_errorMsg.empty())
51  return false;
52  else
53  {
54  msg = _errorMsg;
55  return true;
56  }
57 }
58 
59 template<typename T>
61 {
62  _threadIsRunning = true;
63  //index of current data set to feed
64  int counter = 0;
65 
66  SENSTimePt simTime = _clock.now();
67  //bring counter close to startTimePt (maybe we skip some values to synchronize simulated sensors)
68  while (counter < _data.size() && _data[counter].first < simTime)
69  counter++;
70 
71  if (counter >= _data.size())
72  {
73  //end of simulation
74  _threadIsRunning = false;
75  _sensorSimStoppedCB();
76  return;
77  }
78 
79  while (true)
80  {
81  //estimate current sim time
82  SENSTimePt simTime = _clock.now();
83 
84  //feed latest data
85  int oldCounter = counter;
86  if (counter < _data.size() && _data[counter].first <= simTime)
87  {
88  //SENS_DEBUG("feed sensor index %d with latency: %d us, SimT: %d, ValueT: %d", counter, std::chrono::duration_cast<SENSMicroseconds>(_data[counter].first - simTime).count(), std::chrono::time_point_cast<SENSMicroseconds>(simTime).time_since_epoch().count(), std::chrono::time_point_cast<SENSMicroseconds>(_data[counter].first).time_since_epoch().count());
89  SENS_DEBUG("feed %s sensor index %d with latency: %d us", _name.c_str(), counter, std::chrono::duration_cast<SENSMicroseconds>(_data[counter].first - simTime).count());
90  feedSensorData(counter);
91  if (_stop)
92  break;
93 
94  //update the counter
95  simTime = _clock.now();
96  while (counter < _data.size() && _data[counter].first < simTime)
97  counter++;
98  }
99 
100  if (counter - oldCounter > 1)
101  {
102  //for feeding camera frames from cv::videocapture we need a special treatment in this case
103  //(THIS FUNCTION MAY CHANGE THE COUNTER)
104  onLatencyProblem(counter);
105  //report an error
106  int numberOfSkippedData = counter - oldCounter;
107 
108  std::stringstream ss;
109  ss << "Data feeding is too slow. Skipped " << numberOfSkippedData << " data sets!";
110  SENS_WARN("SENSSimulated feedSensor: %s", ss.str().c_str());
111  {
112  std::lock_guard<std::mutex> lock(_msgMutex);
113  _errorMsg = ss.str();
114  }
115  }
116 
117  //end of simulation
118  if (counter >= _data.size())
119  break;
120 
121  //prepare things that may take some time (e.g. for video reading we can already read the frame and do decoding and maybe it also blocks for some reasons..)
122  prepareSensorData(counter);
123 
124  //WAITING DOES NOT WORK VERY EXACTLY ON WINDOWS (it does wake too late)
125 #ifdef WAIT
126  simTime = _clock.now();
127  //simTime should now be smaller than valueTime because valueTime is in the simulation future
128  SENSMicroseconds waitTimeUs = std::chrono::duration_cast<SENSMicroseconds>(_data[counter].first - simTime);
129  //We reduce the wait time because thread sleep is not very exact (best is not to wait at all)
130  SENSMicroseconds reducedWaitTimeUs((long)(0.01 * (double)waitTimeUs.count()));
131 
132  //HighResTimer t;
133  std::unique_lock<std::mutex> lock(_mutex);
134  _condVar.wait_for(lock, reducedWaitTimeUs, [&]
135  { return _stop == true; });
136  //SENS_DEBUG("wait time %d us", waitTimeUs.count());
137  //SENS_DEBUG("woke after %d us", t.elapsedTimeInMicroSec());
138 #endif
139  if (_stop)
140  break;
141  }
142 
143  _threadIsRunning = false;
144  _sensorSimStoppedCB();
145 }
146 
147 //explicit template instantiation
148 template class SENSSimulated<SENSGps::Location>;
150 template class SENSSimulated<int>;
151 
152 //-----------------------------------------------------------------------------
153 
155 {
156  stop();
157 }
158 
160 {
161  if (!_running)
162  {
163  startSim();
164  _running = true;
165  }
166 
167  return _running;
168 }
169 
171 {
172  if (_running)
173  {
174  stopSim();
175  _running = false;
176  }
177 }
178 
179 //only SENSSimulator can instantiate
181  SensorSimStoppedCB sensorSimStoppedCB,
182  std::vector<std::pair<SENSTimePt, SENSGps::Location>>&& data,
183  const SENSSimClock& clock)
184  : SENSSimulated("gps", startSimCB, sensorSimStoppedCB, std::move(data), clock)
185 {
186  _permissionGranted = true;
187 }
188 
189 void SENSSimulatedGps::feedSensorData(const int counter)
190 {
191  setLocation(_data[counter].second);
192 }
193 
194 //-----------------------------------------------------------------------------
196 {
197  stop();
198 }
199 
201 {
202  if (!_running)
203  {
204  startSim();
205  _running = true;
206  }
207 
208  return _running;
209 }
210 
212 {
213  if (_running)
214  {
215  stopSim();
216  _running = false;
217  }
218 }
219 
221  SensorSimStoppedCB sensorSimStoppedCB,
222  std::vector<std::pair<SENSTimePt, SENSOrientation::Quat>>&& data,
223  const SENSSimClock& clock)
224  : SENSSimulated("orientation", startSimCB, sensorSimStoppedCB, std::move(data), clock)
225 {
226 }
227 
229 {
230  setOrientation(_data[counter].second);
231 }
232 
233 //-----------------------------------------------------------------------------
235 {
236  stop();
237 }
238 
239 const SENSCameraConfig& SENSSimulatedCamera::start(std::string deviceId,
240  const SENSCameraStreamConfig& streamConfig,
241  bool provideIntrinsics)
242 {
243  if (_started)
244  {
245  Utils::warnMsg("SENSWebCamera", "Call to start was ignored. Camera is currently running!", __LINE__, __FILE__);
246  return _config;
247  }
248 
249  if (!_cap.isOpened())
250  {
251  if (!_cap.open(_videoFileName))
252  throw SENSException(SENSType::CAM, "Could not open camera simulator for filename: " + _videoFileName, __LINE__, __FILE__);
253  }
254 
255  //retrieve all camera characteristics
256  if (_captureProperties.size() == 0)
258 
259  //CONFIG SHOULD HAVE BEEN LOADED FROM FILE
260 
261  processStart();
262 
263  //start the sensor simulation
264  startSim();
265 
266  _started = true;
267 
268  return _config;
269 }
270 
272 {
273  if (_started)
274  {
275  stopSim();
276  _started = false;
277  }
278 }
279 
281 {
282  if (!_captureProperties.size())
283  {
286  _captureProperties.push_back(characteristics);
287  }
288 
289  return _captureProperties;
290 }
291 
293  SensorSimStoppedCB sensorSimStoppedCB,
294  std::vector<std::pair<SENSTimePt, int>>&& data,
295  std::string videoFileName,
296  SENSCameraConfig cameraConfig,
297  const SENSSimClock& clock)
298  : SENSSimulated("camera", startSimCB, sensorSimStoppedCB, std::move(data), clock),
299  _videoFileName(videoFileName)
300 {
301  _config = cameraConfig;
302  _permissionGranted = true;
303 }
304 
305 void SENSSimulatedCamera::feedSensorData(const int counter)
306 {
307  HighResTimer t;
308  //prepare if not yet prepared (e.g. when while loop writes multiple lines)
309  if (_data[counter].second != _preparedFrameIndex)
310  {
311  SENS_DEBUG("preparing in feedSensorData");
312  prepareSensorData(counter);
313  }
314 
315  updateFrame(_preparedFrame, cv::Mat(), false, _preparedFrame.size().width, _preparedFrame.size().height);
316 
317  SENS_DEBUG("feedSensorData %lld us", t.elapsedTimeInMicroSec());
318 }
319 
321 {
322  if (counter >= _data.size())
323  return;
324 
325  int frameIndex = _data[counter].second;
326  if (frameIndex == _preparedFrameIndex)
327  return;
328 
329  HighResTimer t;
330 
331  int nextFramePos = _cap.get(cv::CAP_PROP_POS_FRAMES);
332  if (nextFramePos != frameIndex)
333  {
334  SENS_DEBUG("updating frame pos");
335  _cap.set(cv::CAP_PROP_POS_FRAMES, frameIndex);
336  }
337 
338  cv::Mat frame;
339 
340  _cap.read(frame);
341 
342  {
343  _preparedFrameIndex = frameIndex;
344  std::lock_guard<std::mutex> lock(_frameMutex);
345  _preparedFrame = frame;
346  }
347 
348  SENS_DEBUG("prepared index %d: %lld", frameIndex, t.elapsedTimeInMicroSec());
349 }
350 
352 {
353  //Setting the next frame position may take a lot of time.
354  //Thats why we need this special treatment for camera feed.
355 
356  //update the frame pos with current counter and measure needed time
357  SENSTimePt t0 = SENSClock::now();
358  prepareSensorData(counter);
359  SENSTimePt t1 = SENSClock::now();
360  SENSMicroseconds neededTime = std::chrono::duration_cast<SENSMicroseconds>(t1 - t0);
361 
362  //SENS_DEBUG("update CAP_PROP_POS_FRAMES %d: %lld", counter, t.elapsedTimeInMicroSec());
363  counter++;
364  SENSTimePt now = _clock.now();
365  while (counter < _data.size() - 1 && _data[counter].first < now)
366  {
367  //when we enter this loop the frame the neededTime for pos update took longer than the availableTime between too values.
368  //we measure how much longer and update the counter accordingly
369  SENSMicroseconds availableTime = std::chrono::duration_cast<SENSMicroseconds>(_data[counter + 1].first - _data[counter].first);
370  auto factor = neededTime.count() / availableTime.count();
371  factor++;
372  SENS_DEBUG("factor %lld neededTime %lld available time %lld", factor, neededTime.count(), availableTime.count());
373  counter += factor;
374 
375  if (counter < _data.size())
376  {
377  t0 = SENSClock::now();
378  prepareSensorData(counter);
379  t1 = SENSClock::now();
380  neededTime = std::chrono::duration_cast<SENSMicroseconds>(t1 - t0);
381  }
382 
383  now = _clock.now();
384  }
385 }
#define SENS_DEBUG(...)
Definition: SENS.h:60
std::chrono::microseconds SENSMicroseconds
Definition: SENS.h:58
#define SENS_WARN(...)
Definition: SENS.h:64
std::chrono::high_resolution_clock::time_point SENSTimePt
Definition: SENS.h:57
High Resolution Timer class using C++11.
Definition: HighResTimer.h:31
int64_t elapsedTimeInMicroSec()
Definition: HighResTimer.h:39
SENSCameraConfig _config
indicates what is currently running
Definition: SENSCamera.h:218
std::atomic< bool > _permissionGranted
Definition: SENSCamera.h:219
SENSCaptureProps _captureProperties
Definition: SENSCamera.h:216
std::atomic< bool > _started
flags if camera was started
Definition: SENSCamera.h:217
void updateFrame(cv::Mat bgrImg, cv::Mat intrinsics, int width, int height, bool intrinsicsChanged)
Definition: SENSCamera.cpp:220
void processStart()
call from start function to do startup preprocessing
Definition: SENSCamera.cpp:294
void add(int widthPix, int heightPix, float focalLengthPix)
Definition: SENSCamera.h:97
bool _running
Definition: SENSGps.h:46
void setLocation(SENSGps::Location location)
Definition: SENSGps.cpp:11
std::atomic_bool _permissionGranted
Definition: SENSGps.h:47
void setOrientation(Quat orientation)
SENSTimePt now() const
get current simulation time (const function which should be thread save)
Definition: SENSSimClock.h:71
std::mutex _frameMutex
void onLatencyProblem(int &counter) override
when feeding camera frames from cv::videocapture updating the frame pos may take ages....
std::string _videoFileName
const SENSCaptureProps & captureProperties() override
Get SENSCaptureProps which contains necessary information about all available camera devices and thei...
void prepareSensorData(const int counter) override
const SENSCameraConfig & start(std::string deviceId, const SENSCameraStreamConfig &streamConfig, bool provideIntrinsics=true) override
SENSSimulatedCamera(StartSimCB startSimCB, SensorSimStoppedCB sensorSimStoppedCB, std::vector< std::pair< SENSTimePt, int >> &&data, std::string videoFileName, SENSCameraConfig cameraConfig, const SENSSimClock &clock)
void stop() override
Stop a started camera device.
void feedSensorData(const int counter) override
feed new sensor data to sensor
cv::VideoCapture _cap
bool start() override
void feedSensorData(const int counter) override
feed new sensor data to sensor
void stop() override
SENSSimulatedGps(StartSimCB startSimCB, SensorSimStoppedCB sensorSimStoppedCB, std::vector< std::pair< SENSTimePt, SENSGps::Location >> &&data, const SENSSimClock &clock)
SENSSimulated(const std::string name, StartSimCB startSimCB, SensorSimStoppedCB sensorSimStoppedCB, std::vector< std::pair< SENSTimePt, T >> &&data, const SENSSimClock &clock)
void stopSim()
stop the sensor simulation thread
bool getErrorMsg(std::string &msg) override
get error msg (valid if function returns true)
std::function< void(void)> SensorSimStoppedCB
Definition: SENSSimulated.h:46
std::vector< std::pair< SENSTimePt, SENSGps::Location > > _data
Definition: SENSSimulated.h:79
void feedSensor()
thread run routine to feed sensor with data.
void startSim()
start the sensor simulation thread
const SENSSimClock & _clock
Definition: SENSSimulated.h:94
std::function< void(void)> StartSimCB
Definition: SENSSimulated.h:45
void feedSensorData(const int counter) override
feed new sensor data to sensor
SENSSimulatedOrientation(StartSimCB startSimCB, SensorSimStoppedCB sensorSimStoppedCB, std::vector< std::pair< SENSTimePt, SENSOrientation::Quat >> &&data, const SENSSimClock &clock)
void warnMsg(const char *tag, const char *msg, const int line, const char *file)
Platform independent warn message output.
Definition: Utils.cpp:1142
SENSCameraFacing facing
camera facing
Definition: SENSCamera.h:133
SENSCameraStreamConfig streamConfig
currently selected stream config index (use it to look up original capture size)
Definition: SENSCamera.h:131
std::string deviceId
Definition: SENSCamera.h:130