diff --git a/configure.ac b/configure.ac index 866ebff41..6014b3911 100644 --- a/configure.ac +++ b/configure.ac @@ -48,7 +48,7 @@ dnl Beware of duplication should a backend directory include both rig and dnl rotor definitions, e.g. "dummy". Optional backends will not be listed dnl here but will be added later, e.g. "winradio". RIG_BACKEND_LIST="rigs/adat rigs/alinco rigs/aor rigs/barrett rigs/codan rigs/dorji rigs/drake rigs/dummy rigs/elad rigs/flexradio rigs/icom rigs/icmarine rigs/jrc rigs/kachina rigs/kenwood rigs/kit rigs/lowe rigs/pcr rigs/prm80 rigs/racal rigs/rft rigs/rs rigs/skanti rigs/tapr rigs/tentec rigs/tuner rigs/uniden rigs/winradio rigs/wj rigs/yaesu rigs/gomspace rigs/mds" -ROT_BACKEND_LIST="rotators/amsat rotators/ars rotators/celestron rotators/cnctrk rotators/grbltrk rotators/easycomm rotators/ether6 rotators/fodtrack rotators/gs232a rotators/heathkit rotators/m2 rotators/meade rotators/rotorez rotators/sartek rotators/spid rotators/ts7400 rotators/prosistel rotators/ioptron rotators/satel rotators/radant" +ROT_BACKEND_LIST="rotators/amsat rotators/ars rotators/celestron rotators/cnctrk rotators/grbltrk rotators/easycomm rotators/ether6 rotators/flir rotators/fodtrack rotators/gs232a rotators/heathkit rotators/m2 rotators/meade rotators/rotorez rotators/sartek rotators/spid rotators/ts7400 rotators/prosistel rotators/ioptron rotators/satel rotators/radant" # Amplifiers are all in the amplifiers directory AMP_BACKEND_LIST="amplifiers/elecraft amplifiers/gemini" @@ -834,6 +834,7 @@ rotators/cnctrk/Makefile rotators/grbltrk/Makefile rotators/easycomm/Makefile rotators/ether6/Makefile +rotators/flir/Makefile rotators/fodtrack/Makefile rotators/gs232a/Makefile rotators/heathkit/Makefile diff --git a/include/hamlib/rotlist.h b/include/hamlib/rotlist.h index 744c716ca..2ede3f893 100644 --- a/include/hamlib/rotlist.h +++ b/include/hamlib/rotlist.h @@ -650,6 +650,21 @@ #define ROT_MODEL_GRBLTRK_SER ROT_MAKE_MODEL(ROT_GRBLTRK, 1) #define ROT_MODEL_GRBLTRK_NET ROT_MAKE_MODEL(ROT_GRBLTRK, 2) +/** + * \brief A macro that returns the model number of the FLIR backend. + * + * \def ROT_MODEL_FLIR + * + * The FLIR backend can be used with FLIR and DirectedPercepition + * rotators using the PTU protocol (e.g. PTU-D48). Currently only + * the serial interaface is supported and no ethernet. + */ +//! @cond Doxygen_Suppress +#define ROT_FLIR 25 +#define ROT_BACKEND_FLIR "flir" +//! @endcond +#define ROT_MODEL_FLIR ROT_MAKE_MODEL(ROT_FLIR, 1) + /** * \brief Convenience type definition for a rotator model. * diff --git a/rotators/flir/Android.mk b/rotators/flir/Android.mk new file mode 100644 index 000000000..3fa35ec03 --- /dev/null +++ b/rotators/flir/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := flir.c +LOCAL_MODULE := flir + +LOCAL_CFLAGS := +LOCAL_C_INCLUDES := android include src +LOCAL_LDLIBS := -lhamlib -Lobj/local/$(TARGET_ARCH_ABI) + +include $(BUILD_STATIC_LIBRARY) diff --git a/rotators/flir/Makefile.am b/rotators/flir/Makefile.am new file mode 100644 index 000000000..f43281d5b --- /dev/null +++ b/rotators/flir/Makefile.am @@ -0,0 +1,6 @@ +FLIRSRC = flir.c flir.h + +noinst_LTLIBRARIES = libhamlib-flir.la +libhamlib_flir_la_SOURCES = $(FLIRSRC) + +EXTRA_DIST = README.md Android.mk \ No newline at end of file diff --git a/rotators/flir/README.md b/rotators/flir/README.md new file mode 100644 index 000000000..11d3b9bc2 --- /dev/null +++ b/rotators/flir/README.md @@ -0,0 +1,75 @@ +# FLIR/DirectedPerception PTU Rotor Module + +This module interfaces FLIR and DirectedPerception +rotor using the PTU protocol via serial. + +This includes: + +* PTU-D48(E) +* PTU-E46 +* PTU-D100(E) +* PTU-D300(E) + +Tested only with PTU-D48 yet and with one rotor per chain only. + +## Usage + +1. Connect the rotor via serial (RS232 or RS485) +2. Power up the rotor +3. The rotor must be calibrated after each power up. This can be accived +either using the rotctl `Reset` command (R) or manually via serial terminal +sending the `R\n` command. +4. To enable the rotor to fully turn +/- 180°, the softlock must be disabled. +This is included in the rotctl `Reset` commnad or manually via serial terminal +seinden the command `LD\n`. **WARNING:** Send this command only after the rotor is +calibrated, or you risk damage running into the hard endstops (at about +/-190°) +5. Start `rotctl` or `rotctld` with the arguments `-m 2501 -r ` + +Have Fun. + +### Hints + +1. Setup the max. velocity, power and acceleration according to your antenna load. +This must be done via serial terminal, as the functions are not implemented yet. +2. Never use the maximum hold power, only use the low or off. If you use max or regular, +the rotor may easily overheat! + +## PTU Protocol + +* [Protocol Version 3.02 (2011)](https://flir.netx.net/file/asset/11556/original/attachment) + +## Current Status + +The current status is **ALPHA**. It is tested with DirectedPercepiton PTU-D48 (Firmware v2.13.4r0(D48-C14/E)) +Linux with `rotctl` and `gpredict`. + +### Implemented so far: + +* init +* cleanup +* open +* close +* set_position +* get_position +* park +* stop +* reset +* move +* info + +### Needs to be implemented: + +#### Parameters + +* velocity +* acceleration +* velocity profile +* user soft-limits +* power commands (move and hold) +* step mode +* reset on startup + +#### Functions + +* usage of chained rotors via RS485 diff --git a/rotators/flir/flir.c b/rotators/flir/flir.c new file mode 100644 index 000000000..7ae3ffc02 --- /dev/null +++ b/rotators/flir/flir.c @@ -0,0 +1,555 @@ +/* + * Hamlib FLIR PTU rotor backend - main file + * Copyright (c) 2022 by Andreas Mueller (DC1MIL) + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include +#include /* String function definitions */ +#include +#include + +#include "hamlib/rotator.h" +#include "register.h" +#include "idx_builtin.h" +#include "serial.h" + +#include "flir.h" + +#define FLIR_FUNC 0 +#define FLIR_LEVEL ROT_LEVEL_SPEED +#define FLIR_PARM 0 + +#define FLIR_STATUS (ROT_STATUS_MOVING | ROT_STATUS_MOVING_AZ | ROT_STATUS_MOVING_LEFT | ROT_STATUS_MOVING_RIGHT | \ + ROT_STATUS_MOVING_EL | ROT_STATUS_MOVING_UP | ROT_STATUS_MOVING_DOWN | \ + ROT_STATUS_LIMIT_UP | ROT_STATUS_LIMIT_DOWN | ROT_STATUS_LIMIT_LEFT | ROT_STATUS_LIMIT_RIGHT) + +struct flir_priv_data +{ + azimuth_t az; + elevation_t el; + + struct timeval tv; /* time last az/el update */ + azimuth_t target_az; + elevation_t target_el; + rot_status_t status; + + setting_t funcs; + value_t levels[RIG_SETTING_MAX]; + value_t parms[RIG_SETTING_MAX]; + + char info[256]; + + struct ext_list *ext_funcs; + struct ext_list *ext_levels; + struct ext_list *ext_parms; + + char *magic_conf; + + float_t resolution_pp; + float_t resolution_tp; +}; + +static int flir_request(ROT *rot, char *request, char *response, + int resp_size) +{ + int return_value = -RIG_EINVAL; + int retry_read = 0; + int read_char = 0; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + rig_flush(&rot->state.rotport); + + if (request) + { + return_value = write_block(&rot->state.rotport, (unsigned char *)request, + strlen(request)); + if (return_value != RIG_OK) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s request not OK\n", __func__); + return return_value; + } + } + //Is a direct response expected? + if (response != NULL) + { + while(retry_read < rot->state.rotport.retry) + { + memset(response, 0, (size_t)resp_size); + read_char = read_string(&rot->state.rotport, (unsigned char *)response, resp_size, + "\r\n", sizeof("\r\n"), 0, 1); + if(read_char > 0) + { + if(response[0] == '*') + { + rig_debug(RIG_DEBUG_VERBOSE, "accepted command %s\n", request); + return RIG_OK; + } + else + { + rig_debug(RIG_DEBUG_VERBOSE, "NOT accepted command %s\n", request); + return -RIG_ERJCTED; + } + + } + retry_read++; + } + response = ""; + rig_debug(RIG_DEBUG_VERBOSE, "timeout for command %s\n", request); + return -RIG_ETIMEOUT; + } + + return return_value; +}; + +static int flir_init(ROT *rot) +{ + struct flir_priv_data *priv; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + rot->state.priv = (struct flir_priv_data *) + calloc(1, sizeof(struct flir_priv_data)); + + if (!rot->state.priv) + { + return -RIG_ENOMEM; + } + + priv = rot->state.priv; + + priv->az = priv->el = 0; + + priv->target_az = priv->target_el = 0; + + priv->magic_conf = strdup("ROTATOR"); + + priv->resolution_pp = 92.5714; + priv->resolution_tp = 46.2857; + + return RIG_OK; +} + +static int flir_cleanup(ROT *rot) +{ + struct flir_priv_data *priv = (struct flir_priv_data *) + rot->state.priv; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + free(priv->ext_funcs); + free(priv->ext_levels); + free(priv->ext_parms); + free(priv->magic_conf); + + if (rot->state.priv) + { + free(rot->state.priv); + } + + rot->state.priv = NULL; + + return RIG_OK; +} + +static int flir_open(ROT *rot) +{ + struct flir_priv_data *priv; + char return_str[MAXBUF]; + float_t resolution_pp, resolution_tp; + int return_value = RIG_OK; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + priv = rot->state.priv; + + // Disable ECHO + return_value = flir_request(rot, "ED\n", NULL, MAXBUF); + + // Disable Verbose Mode + return_value = flir_request(rot, "FT\n", return_str, MAXBUF); + + // Get PAN resolution in arcsecs + if(flir_request(rot, "PR\n", return_str, MAXBUF) == RIG_OK) + { + sscanf(return_str, "* %f", &resolution_pp); + rig_debug(RIG_DEBUG_VERBOSE, "PAN resolution: %f arcsecs per position\n", resolution_pp); + priv->resolution_pp = resolution_pp; + } + else + { + return_value = -RIG_EPROTO; + } + // Get TILT resolution in arcsecs + if(flir_request(rot, "TR\n", return_str, MAXBUF) == RIG_OK) + { + sscanf(return_str, "* %f", &resolution_tp); + rig_debug(RIG_DEBUG_VERBOSE, "TILT resolution: %f arcsecs per position\n", resolution_tp); + priv->resolution_tp = resolution_tp; + } + else + { + return_value = -RIG_EPROTO; + } + + return return_value; +} + +static int flir_close(ROT *rot) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + return RIG_OK; +} + +static int flir_set_conf(ROT *rot, token_t token, const char *val) +{ + return -RIG_ENIMPL; +} + +static int flir_get_conf(ROT *rot, token_t token, char *val) +{ + return -RIG_ENIMPL; +} + +static int flir_set_position(ROT *rot, azimuth_t az, elevation_t el) +{ + int32_t t_pan_positions, t_tilt_positions; + char return_str[MAXBUF]; + char cmd_str[MAXBUF]; + struct flir_priv_data *priv = (struct flir_priv_data *) + rot->state.priv; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called: %.2f %.2f\n", __func__, + az, el); + + priv->target_az = az; + priv->target_el = el; + + t_pan_positions = (az * 3600) / priv->resolution_pp; + t_tilt_positions = - ((90.0 - el) * 3600) / priv->resolution_tp; + + sprintf(cmd_str, "PP%d TP%d\n", t_pan_positions, t_tilt_positions); + + return flir_request(rot, cmd_str, return_str, MAXBUF); +} + +/* + * Get position of rotor + */ +static int flir_get_position(ROT *rot, azimuth_t *az, elevation_t *el) +{ + int return_value = RIG_OK; + char return_str[MAXBUF]; + int32_t pan_positions, tilt_positions; + + struct flir_priv_data *priv = (struct flir_priv_data *) + rot->state.priv; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + + if(flir_request(rot, "PP\n", return_str, MAXBUF) == RIG_OK) + { + rig_debug(RIG_DEBUG_VERBOSE, "PP Return String: %s\n", return_str); + sscanf(return_str, "* %d", &pan_positions); + priv->az = (pan_positions * priv->resolution_pp) / 3600; + *az = priv->az; + } + else + { + rig_debug(RIG_DEBUG_VERBOSE, "PP Wrong Return String: %s\n", return_str); + return_value = -RIG_EPROTO; + } + + if(flir_request(rot, "TP\n", return_str, MAXBUF) == RIG_OK) + { + rig_debug(RIG_DEBUG_VERBOSE, "TP Return String: %s\n", return_str); + sscanf(return_str, "* %d", &tilt_positions); + priv->el = 90.0 + ((tilt_positions * priv->resolution_tp) / 3600); + *el = priv->el; + } + else + { + rig_debug(RIG_DEBUG_VERBOSE, "PP Wrong Return String: %s\n", return_str); + return_value = -RIG_EPROTO; + } + + return return_value; +} + +static int flir_stop(ROT *rot) +{ + int return_value = RIG_OK; + + struct flir_priv_data *priv = (struct flir_priv_data *) + rot->state.priv; + azimuth_t az; + elevation_t el; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + return_value = flir_request(rot, "H\n", NULL, MAXBUF); + // Wait 2s until rotor has stopped (Needs to be refactored) + hl_usleep(2000000); + + return_value = flir_get_position(rot, &az, &el); + + priv->target_az = priv->az = az; + priv->target_el = priv->el = el; + + return return_value; +} + +static int flir_park(ROT *rot) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + /* Assume park Position is 0,90 */ + flir_set_position(rot, 0, 90); + + return RIG_OK; +} + +static int flir_reset(ROT *rot, rot_reset_t reset) +{ + int return_value = RIG_OK; + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + if (reset != 0) + { + return_value = flir_request(rot, "r\n", NULL, 0); + // After Reset: Disable Hard Limits + if(return_value == RIG_OK) + { + return_value = flir_request(rot, "LD\n", NULL, MAXBUF); + } + } + return return_value; +} + +static int flir_move(ROT *rot, int direction, int speed) +{ + struct flir_priv_data *priv = (struct flir_priv_data *) + rot->state.priv; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + rig_debug(RIG_DEBUG_TRACE, "%s: Direction = %d, Speed = %d\n", __func__, + direction, speed); + + switch (direction) + { + case ROT_MOVE_UP: + return flir_set_position(rot, priv->target_az, 90); + + case ROT_MOVE_DOWN: + return flir_set_position(rot, priv->target_az, 0); + + case ROT_MOVE_CCW: + return flir_set_position(rot, -180, priv->target_el); + + case ROT_MOVE_CW: + return flir_set_position(rot, 180, priv->target_el); + + default: + return -RIG_EINVAL; + } + + return RIG_OK; +} + +static const char *flir_get_info(ROT *rot) +{ + char firmware_str[121]; + char info_str[101]; + + struct flir_priv_data *priv = (struct flir_priv_data *) + rot->state.priv; + + sprintf(priv->info, "No Info"); + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + if(flir_request(rot, "V\n", firmware_str, 120) != RIG_OK) + { + return "No Info available"; + } + hl_usleep(500000); + if(flir_request(rot, "O\n", info_str, 100) != RIG_OK) + { + return "No Info available"; + } + sprintf(priv->info, "Firmware: %s\nPower: %s", firmware_str, info_str); + + return priv->info; +} + +static int flir_set_func(ROT *rot, setting_t func, int status) +{ + return -RIG_ENIMPL; +} + +static int flir_get_func(ROT *rot, setting_t func, int *status) +{ + return -RIG_ENIMPL; +} + +static int flir_set_level(ROT *rot, setting_t level, value_t val) +{ + return -RIG_ENIMPL; +} + +static int flir_get_level(ROT *rot, setting_t level, value_t *val) +{ + return -RIG_ENIMPL; +} + +static int flir_set_ext_level(ROT *rot, token_t token, value_t val) +{ + return -RIG_ENIMPL; +} + +static int flir_get_ext_level(ROT *rot, token_t token, value_t *val) +{ + return -RIG_ENIMPL; +} + +static int flir_set_ext_func(ROT *rot, token_t token, int status) +{ + return -RIG_ENIMPL; +} + +static int flir_get_ext_func(ROT *rot, token_t token, int *status) +{ + return -RIG_ENIMPL; +} + +static int flir_set_parm(ROT *rot, setting_t parm, value_t val) +{ + return -RIG_ENIMPL; +} + +static int flir_get_parm(ROT *rot, setting_t parm, value_t *val) +{ + return -RIG_ENIMPL; +} + +static int flir_set_ext_parm(ROT *rot, token_t token, value_t val) +{ + return -RIG_ENIMPL; +} + +static int flir_get_ext_parm(ROT *rot, token_t token, value_t *val) +{ + return -RIG_ENIMPL; +} + +static int flir_get_status(ROT *rot, rot_status_t *status) +{ + struct flir_priv_data *priv = (struct flir_priv_data *) + rot->state.priv; + *status = priv->status; + + return RIG_OK; +} + +/* + * flir rotator capabilities. + */ +struct rot_caps flir_caps = +{ + ROT_MODEL(ROT_MODEL_FLIR), + .model_name = "PTU Serial", + .mfg_name = "FLIR", + .version = "20221126.0", + .copyright = "LGPL", + .status = RIG_STATUS_ALPHA, + .rot_type = ROT_TYPE_AZEL, + .port_type = RIG_PORT_SERIAL, + .serial_rate_min = 9600, + .serial_rate_max = 9600, + .serial_data_bits = 8, + .serial_stop_bits = 1, + .serial_parity = RIG_PARITY_NONE, + .serial_handshake = RIG_HANDSHAKE_NONE, + .write_delay = 0, + .post_write_delay = 300, + .timeout = 400, + .retry = 3, + + .min_az = -180., + .max_az = 180., + .min_el = 0., + .max_el = 90., + + .priv = NULL, /* priv */ + + .has_get_func = FLIR_FUNC, + .has_set_func = FLIR_FUNC, + .has_get_level = FLIR_LEVEL, + .has_set_level = ROT_LEVEL_SET(FLIR_LEVEL), + .has_get_parm = FLIR_PARM, + .has_set_parm = ROT_PARM_SET(FLIR_PARM), + + .level_gran = { [ROT_LVL_SPEED] = { .min = { .i = 1 }, .max = { .i = 4 }, .step = { .i = 1 } } }, + + .has_status = FLIR_STATUS, + + .rot_init = flir_init, + .rot_cleanup = flir_cleanup, + .rot_open = flir_open, + .rot_close = flir_close, + + .set_conf = flir_set_conf, + .get_conf = flir_get_conf, + + .set_position = flir_set_position, + .get_position = flir_get_position, + .park = flir_park, + .stop = flir_stop, + .reset = flir_reset, + .move = flir_move, + + .set_func = flir_set_func, + .get_func = flir_get_func, + .set_level = flir_set_level, + .get_level = flir_get_level, + .set_parm = flir_set_parm, + .get_parm = flir_get_parm, + + .set_ext_func = flir_set_ext_func, + .get_ext_func = flir_get_ext_func, + .set_ext_level = flir_set_ext_level, + .get_ext_level = flir_get_ext_level, + .set_ext_parm = flir_set_ext_parm, + .get_ext_parm = flir_get_ext_parm, + + .get_info = flir_get_info, + .get_status = flir_get_status, +}; + +DECLARE_INITROT_BACKEND(flir) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s: _init called\n", __func__); + + rot_register(&flir_caps); + rot_register(&netrotctl_caps); + + return RIG_OK; +} diff --git a/rotators/flir/flir.h b/rotators/flir/flir.h new file mode 100644 index 000000000..e20c05c79 --- /dev/null +++ b/rotators/flir/flir.h @@ -0,0 +1,46 @@ +/* + * Hamlib FLIR PTU rotor backend - main header + * Copyright (c) 2022 by Andreas Mueller (DC1MIL) + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _ROT_FLIR_H +#define _ROT_FLIR_H 1 + +#include "token.h" + +/* backend conf */ +#define TOK_CFG_ROT_MAGICCONF TOKEN_BACKEND(1) +#define TOK_CFG_ROT_STATIC_DATA TOKEN_BACKEND(2) + +/* ext_level's and ext_parm's tokens */ +#define TOK_EL_ROT_MAGICLEVEL TOKEN_BACKEND(1) +#define TOK_EL_ROT_MAGICFUNC TOKEN_BACKEND(2) +#define TOK_EL_ROT_MAGICOP TOKEN_BACKEND(3) +#define TOK_EP_ROT_MAGICPARM TOKEN_BACKEND(4) +#define TOK_EL_ROT_MAGICCOMBO TOKEN_BACKEND(5) +#define TOK_EL_ROT_MAGICEXTFUNC TOKEN_BACKEND(6) + +#define MAXBUF 64 + + + +extern struct rot_caps flir_caps; +extern struct rot_caps netrotctl_caps; + +#endif /* _ROT_FLIR_H */ diff --git a/src/rot_reg.c b/src/rot_reg.c index 13e152c59..b476dd4c2 100644 --- a/src/rot_reg.c +++ b/src/rot_reg.c @@ -90,6 +90,7 @@ DEFINE_INITROT_BACKEND(indi); DEFINE_INITROT_BACKEND(androidsensor); #endif DEFINE_INITROT_BACKEND(grbltrk); +DEFINE_INITROT_BACKEND(flir); //! @endcond /** @@ -138,6 +139,7 @@ static struct { ROT_ANDROIDSENSOR, ROT_BACKEND_ANDROIDSENSOR, ROT_FUNCNAMA(androidsensor) }, #endif { ROT_GRBLTRK, ROT_BACKEND_GRBLTRK, ROT_FUNCNAMA(grbltrk) }, + { ROT_FLIR, ROT_BACKEND_FLIR, ROT_FUNCNAMA(flir) }, { 0, NULL }, /* end */ };