PX4 Firmware
PX4 Autopilot Software http://px4.io
camera_capture.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2018 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file camera_capture.cpp
36  *
37  * Online and offline geotagging from camera feedback
38  *
39  * @author Mohammed Kabir <kabir@uasys.io>
40  */
41 
42 #include "camera_capture.hpp"
43 
44 #define commandParamToInt(n) static_cast<int>(n >= 0 ? n + 0.5f : n - 0.5f)
45 
46 namespace camera_capture
47 {
49 }
50 
52  ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::lp_default)
53 {
54  memset(&_work_publisher, 0, sizeof(_work_publisher));
55 
56  // Capture Parameters
57  _p_strobe_delay = param_find("CAM_CAP_DELAY");
59 
60  _p_camera_capture_mode = param_find("CAM_CAP_MODE");
62 
63  _p_camera_capture_edge = param_find("CAM_CAP_EDGE");
65 }
66 
68 {
69  /* free any existing reports */
70  if (_trig_buffer != nullptr) {
71  delete _trig_buffer;
72  }
73 
75 }
76 
77 void
78 CameraCapture::capture_callback(uint32_t chan_index, hrt_abstime edge_time, uint32_t edge_state, uint32_t overflow)
79 {
80  _trigger.chan_index = chan_index;
81  _trigger.edge_time = edge_time;
82  _trigger.edge_state = edge_state;
83  _trigger.overflow = overflow;
84 
85  work_queue(HPWORK, &_work_publisher, (worker_t)&CameraCapture::publish_trigger_trampoline, this, 0);
86 }
87 
88 int
89 CameraCapture::gpio_interrupt_routine(int irq, void *context, void *arg)
90 {
91  CameraCapture *dev = static_cast<CameraCapture *>(arg);
92 
93  dev->_trigger.chan_index = 0;
95  dev->_trigger.edge_state = 0;
96  dev->_trigger.overflow = 0;
97 
98  work_queue(HPWORK, &_work_publisher, (worker_t)&CameraCapture::publish_trigger_trampoline, dev, 0);
99 
100  return PX4_OK;
101 }
102 
103 void
105 {
106  CameraCapture *dev = static_cast<CameraCapture *>(arg);
107 
108  dev->publish_trigger();
109 }
110 
111 void
113 {
114  bool publish = false;
115 
116  camera_trigger_s trigger{};
117 
118  // MODES 1 and 2 are not fully tested
119  if (_camera_capture_mode == 0 || _gpio_capture) {
120  trigger.timestamp = _trigger.edge_time - uint64_t(1000 * _strobe_delay);
121  trigger.seq = _capture_seq++;
122  _last_trig_time = trigger.timestamp;
123  publish = true;
124 
125  } else if (_camera_capture_mode == 1) { // Get timestamp of mid-exposure (active high)
126  if (_trigger.edge_state == 1) {
128 
129  } else if (_trigger.edge_state == 0 && _last_trig_begin_time > 0) {
130  trigger.timestamp = _trigger.edge_time - ((_trigger.edge_time - _last_trig_begin_time) / 2);
131  trigger.seq = _capture_seq++;
133  _last_trig_time = trigger.timestamp;
134  publish = true;
135  _capture_seq++;
136  }
137 
138  } else { // Get timestamp of mid-exposure (active low)
139  if (_trigger.edge_state == 0) {
141 
142  } else if (_trigger.edge_state == 1 && _last_trig_begin_time > 0) {
143  trigger.timestamp = _trigger.edge_time - ((_trigger.edge_time - _last_trig_begin_time) / 2);
144  trigger.seq = _capture_seq++;
146  _last_trig_time = trigger.timestamp;
147  publish = true;
148  }
149 
150  }
151 
152  trigger.feedback = true;
154 
155  if (!publish) {
156  return;
157  }
158 
159  _trigger_pub.publish(trigger);
160 }
161 
162 void
163 CameraCapture::capture_trampoline(void *context, uint32_t chan_index, hrt_abstime edge_time, uint32_t edge_state,
164  uint32_t overflow)
165 {
166  camera_capture::g_camera_capture->capture_callback(chan_index, edge_time, edge_state, overflow);
167 }
168 
169 void
171 {
172  // Command handling
173  vehicle_command_s cmd{};
174 
175  if (_command_sub.update(&cmd)) {
176 
177  // TODO : this should eventuallly be a capture control command
178  if (cmd.command == vehicle_command_s::VEHICLE_CMD_DO_TRIGGER_CONTROL) {
179 
180  // Enable/disable signal capture
181  if (commandParamToInt(cmd.param1) == 1) {
182  set_capture_control(true);
183 
184  } else if (commandParamToInt(cmd.param1) == 0) {
185  set_capture_control(false);
186 
187  }
188 
189  // Reset capture sequence
190  if (commandParamToInt(cmd.param2) == 1) {
191  reset_statistics(true);
192 
193  }
194 
195  // Acknowledge the command
196  vehicle_command_ack_s command_ack{};
197 
198  command_ack.timestamp = hrt_absolute_time();
199  command_ack.command = cmd.command;
200  command_ack.result = (uint8_t)vehicle_command_s::VEHICLE_CMD_RESULT_ACCEPTED;
201  command_ack.target_system = cmd.source_system;
202  command_ack.target_component = cmd.source_component;
203 
204  _command_ack_pub.publish(command_ack);
205  }
206  }
207 }
208 
209 void
211 {
212 #if !defined CONFIG_ARCH_BOARD_AV_X_V1
213 
214  int fd = ::open(PX4FMU_DEVICE_PATH, O_RDWR);
215 
216  if (fd < 0) {
217  PX4_ERR("open fail");
218  return;
219  }
220 
222  conf.channel = 5; // FMU chan 6
223  conf.filter = 0;
224 
225  if (_camera_capture_mode == 0) {
227 
228  } else {
229  conf.edge = Both;
230  }
231 
232  conf.callback = NULL;
233  conf.context = NULL;
234 
235  if (enabled) {
236 
238  conf.context = this;
239 
240  unsigned int capture_count = 0;
241 
242  if (::ioctl(fd, INPUT_CAP_GET_COUNT, (unsigned long)&capture_count) != 0) {
243  PX4_INFO("Not in a capture mode");
244 
245  unsigned long mode = PWM_SERVO_MODE_4PWM2CAP;
246 
247  if (::ioctl(fd, PWM_SERVO_SET_MODE, mode) == 0) {
248  PX4_INFO("Mode changed to 4PWM2CAP");
249 
250  } else {
251  PX4_ERR("Mode NOT changed to 4PWM2CAP!");
252  goto err_out;
253  }
254  }
255  }
256 
257  if (::ioctl(fd, INPUT_CAP_SET_CALLBACK, (unsigned long)&conf) == 0) {
258  _capture_enabled = enabled;
259  _gpio_capture = false;
260 
261  } else {
262  PX4_ERR("Unable to set capture callback for chan %u\n", conf.channel);
263  _capture_enabled = false;
264  goto err_out;
265  }
266 
267 #else
268 
269  px4_arch_gpiosetevent(GPIO_TRIG_AVX, true, false, true, &CameraCapture::gpio_interrupt_routine, this);
270  _capture_enabled = enabled;
271  _gpio_capture = true;
272 
273 #endif
274 
275  reset_statistics(false);
276 
277 #if !defined CONFIG_ARCH_BOARD_AV_X_V1
278 err_out:
279  ::close(fd);
280 #endif
281 
282  return;
283 }
284 
285 void
287 {
288  if (reset_seq) {
289  _capture_seq = 0;
290  }
291 
294  _last_trig_time = 0;
295  _capture_overflows = 0;
296 }
297 
298 int
300 {
301  /* allocate basic report buffers */
302  _trig_buffer = new ringbuffer::RingBuffer(2, sizeof(_trig_s));
303 
304  if (_trig_buffer == nullptr) {
305  return PX4_ERROR;
306  }
307 
308  // run every 100 ms (10 Hz)
309  ScheduleOnInterval(100000, 10000);
310 
311  return PX4_OK;
312 }
313 
314 void
316 {
317  ScheduleClear();
318 
319  work_cancel(HPWORK, &_work_publisher);
320 
321  if (camera_capture::g_camera_capture != nullptr) {
323  }
324 }
325 
326 void
328 {
329  PX4_INFO("Capture enabled : %s", _capture_enabled ? "YES" : "NO");
330  PX4_INFO("Frame sequence : %u", _capture_seq);
331  PX4_INFO("Last trigger timestamp : %llu", _last_trig_time);
332 
333  if (_camera_capture_mode != 0) {
334  PX4_INFO("Last exposure time : %0.2f ms", double(_last_exposure_time) / 1000.0);
335  }
336 
337  PX4_INFO("Number of overflows : %u", _capture_overflows);
338 }
339 
340 static int usage()
341 {
342  PX4_INFO("usage: camera_capture {start|stop|on|off|reset|status}\n");
343  return 1;
344 }
345 
346 extern "C" __EXPORT int camera_capture_main(int argc, char *argv[]);
347 
348 int camera_capture_main(int argc, char *argv[])
349 {
350  if (argc < 2) {
351  return usage();
352  }
353 
354  if (!strcmp(argv[1], "start")) {
355 
356  if (camera_capture::g_camera_capture != nullptr) {
357  PX4_WARN("already running");
358  return 0;
359  }
360 
362 
363  if (camera_capture::g_camera_capture == nullptr) {
364  PX4_WARN("alloc failed");
365  return 1;
366  }
367 
369  return 0;
370 
371  } else {
372  return 1;
373  }
374 
375  }
376 
377  if (camera_capture::g_camera_capture == nullptr) {
378  PX4_WARN("not running");
379  return 1;
380 
381  } else if (!strcmp(argv[1], "stop")) {
383 
384  } else if (!strcmp(argv[1], "status")) {
386 
387  } else if (!strcmp(argv[1], "on")) {
389 
390  } else if (!strcmp(argv[1], "off")) {
392 
393  } else if (!strcmp(argv[1], "reset")) {
396 
397  } else {
398  return usage();
399  }
400 
401  return 0;
402 }
#define PX4FMU_DEVICE_PATH
hrt_abstime _last_exposure_time
int start()
Start the task.
static int gpio_interrupt_routine(int irq, void *context, void *arg)
void stop()
Stop the task.
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
uint32_t _capture_seq
void capture_callback(uint32_t chan_index, hrt_abstime edge_time, uint32_t edge_state, uint32_t overflow)
input_capture_edge edge
__EXPORT int camera_capture_main(int argc, char *argv[])
#define commandParamToInt(n)
ringbuffer::RingBuffer * _trig_buffer
#define GPIO_TRIG_AVX
Definition: I2C.hpp:51
bool publish(const T &data)
Publish the struct.
void Run() override
hrt_abstime _last_trig_begin_time
int32_t _camera_capture_edge
uORB::Publication< camera_trigger_s > _trigger_pub
void reset_statistics(bool reset_seq)
static struct work_s _work_publisher
#define INPUT_CAP_SET_CALLBACK
Set the call back on a capture channel - arg is pointer to input_capture_config with channel call bac...
static int usage()
bool publish(const T &data)
Publish the struct.
Definition: Publication.hpp:68
uint32_t _capture_overflows
static void capture_trampoline(void *context, uint32_t chan_index, hrt_abstime edge_time, uint32_t edge_state, uint32_t overflow)
struct CameraCapture::_trig_s _trigger
CameraCapture()
Constructor.
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
int fd
Definition: dataman.cpp:146
#define PWM_SERVO_MODE_4PWM2CAP
CameraCapture * g_camera_capture
__EXPORT param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
#define INPUT_CAP_GET_COUNT
Get the number of capture in *(unsigned *)arg.
param_t _p_camera_capture_edge
input capture values for a channel
#define PWM_SERVO_SET_MODE
int32_t _camera_capture_mode
capture_callback_t callback
hrt_abstime _last_trig_time
Definition: bst.cpp:62
void set_capture_control(bool enabled)
~CameraCapture()
Destructor, also kills task.
uORB::PublicationQueued< vehicle_command_ack_s > _command_ack_pub
static void publish_trigger_trampoline(void *arg)
bool update(void *dst)
Update the struct.
param_t _p_camera_capture_mode
param_t _p_strobe_delay
mode
Definition: vtol_type.h:76
uORB::Subscription _command_sub
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).