mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-21 14:33:35 +08:00
Merge branch 'master' of github.com:PX4/Firmware
This commit is contained in:
@@ -10,7 +10,7 @@ then
|
|||||||
#
|
#
|
||||||
set MIXERSD /fs/microsd/etc/mixers/$MIXER.mix
|
set MIXERSD /fs/microsd/etc/mixers/$MIXER.mix
|
||||||
|
|
||||||
#Use the mixer file from the SD-card if it exists
|
# Use the mixer file from the SD-card if it exists
|
||||||
if [ -f $MIXERSD ]
|
if [ -f $MIXERSD ]
|
||||||
then
|
then
|
||||||
set MIXER_FILE $MIXERSD
|
set MIXER_FILE $MIXERSD
|
||||||
@@ -54,7 +54,6 @@ then
|
|||||||
#
|
#
|
||||||
if [ $PWM_RATE != none ]
|
if [ $PWM_RATE != none ]
|
||||||
then
|
then
|
||||||
echo "[init] Set PWM rate: $PWM_RATE"
|
|
||||||
pwm rate -c $PWM_OUTPUTS -r $PWM_RATE
|
pwm rate -c $PWM_OUTPUTS -r $PWM_RATE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -63,17 +62,14 @@ then
|
|||||||
#
|
#
|
||||||
if [ $PWM_DISARMED != none ]
|
if [ $PWM_DISARMED != none ]
|
||||||
then
|
then
|
||||||
echo "[init] Set PWM disarmed: $PWM_DISARMED"
|
|
||||||
pwm disarmed -c $PWM_OUTPUTS -p $PWM_DISARMED
|
pwm disarmed -c $PWM_OUTPUTS -p $PWM_DISARMED
|
||||||
fi
|
fi
|
||||||
if [ $PWM_MIN != none ]
|
if [ $PWM_MIN != none ]
|
||||||
then
|
then
|
||||||
echo "[init] Set PWM min: $PWM_MIN"
|
|
||||||
pwm min -c $PWM_OUTPUTS -p $PWM_MIN
|
pwm min -c $PWM_OUTPUTS -p $PWM_MIN
|
||||||
fi
|
fi
|
||||||
if [ $PWM_MAX != none ]
|
if [ $PWM_MAX != none ]
|
||||||
then
|
then
|
||||||
echo "[init] Set PWM max: $PWM_MAX"
|
|
||||||
pwm max -c $PWM_OUTPUTS -p $PWM_MAX
|
pwm max -c $PWM_OUTPUTS -p $PWM_MAX
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -301,7 +301,6 @@ then
|
|||||||
|
|
||||||
if [ $OUTPUT_MODE == io -o $OUTPUT_MODE == uavcan_esc ]
|
if [ $OUTPUT_MODE == io -o $OUTPUT_MODE == uavcan_esc ]
|
||||||
then
|
then
|
||||||
echo "[init] Use PX4IO PWM as primary output"
|
|
||||||
if px4io start
|
if px4io start
|
||||||
then
|
then
|
||||||
echo "[init] PX4IO started"
|
echo "[init] PX4IO started"
|
||||||
@@ -314,7 +313,6 @@ then
|
|||||||
|
|
||||||
if [ $OUTPUT_MODE == fmu -o $OUTPUT_MODE == ardrone ]
|
if [ $OUTPUT_MODE == fmu -o $OUTPUT_MODE == ardrone ]
|
||||||
then
|
then
|
||||||
echo "[init] Use FMU as primary output"
|
|
||||||
if fmu mode_$FMU_MODE
|
if fmu mode_$FMU_MODE
|
||||||
then
|
then
|
||||||
echo "[init] FMU mode_$FMU_MODE started"
|
echo "[init] FMU mode_$FMU_MODE started"
|
||||||
@@ -338,7 +336,6 @@ then
|
|||||||
|
|
||||||
if [ $OUTPUT_MODE == mkblctrl ]
|
if [ $OUTPUT_MODE == mkblctrl ]
|
||||||
then
|
then
|
||||||
echo "[init] Use MKBLCTRL as primary output"
|
|
||||||
set MKBLCTRL_ARG ""
|
set MKBLCTRL_ARG ""
|
||||||
if [ $MKBLCTRL_MODE == x ]
|
if [ $MKBLCTRL_MODE == x ]
|
||||||
then
|
then
|
||||||
@@ -361,7 +358,6 @@ then
|
|||||||
|
|
||||||
if [ $OUTPUT_MODE == hil ]
|
if [ $OUTPUT_MODE == hil ]
|
||||||
then
|
then
|
||||||
echo "[init] Use HIL as primary output"
|
|
||||||
if hil mode_port2_pwm8
|
if hil mode_port2_pwm8
|
||||||
then
|
then
|
||||||
echo "[init] HIL output started"
|
echo "[init] HIL output started"
|
||||||
@@ -380,7 +376,6 @@ then
|
|||||||
then
|
then
|
||||||
if px4io start
|
if px4io start
|
||||||
then
|
then
|
||||||
echo "[init] PX4IO started"
|
|
||||||
sh /etc/init.d/rc.io
|
sh /etc/init.d/rc.io
|
||||||
else
|
else
|
||||||
echo "[init] ERROR: PX4IO start failed"
|
echo "[init] ERROR: PX4IO start failed"
|
||||||
@@ -444,7 +439,6 @@ then
|
|||||||
|
|
||||||
if [ $GPS == yes ]
|
if [ $GPS == yes ]
|
||||||
then
|
then
|
||||||
echo "[init] Start GPS"
|
|
||||||
if [ $GPS_FAKE == yes ]
|
if [ $GPS_FAKE == yes ]
|
||||||
then
|
then
|
||||||
echo "[init] Faking GPS"
|
echo "[init] Faking GPS"
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ parser.add_argument("--version", action="store", help="set a version string")
|
|||||||
parser.add_argument("--summary", action="store", help="set a brief description")
|
parser.add_argument("--summary", action="store", help="set a brief description")
|
||||||
parser.add_argument("--description", action="store", help="set a longer description")
|
parser.add_argument("--description", action="store", help="set a longer description")
|
||||||
parser.add_argument("--git_identity", action="store", help="the working directory to check for git identity")
|
parser.add_argument("--git_identity", action="store", help="the working directory to check for git identity")
|
||||||
|
parser.add_argument("--parameter_xml", action="store", help="the parameters.xml file")
|
||||||
parser.add_argument("--image", action="store", help="the firmware image")
|
parser.add_argument("--image", action="store", help="the firmware image")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -101,6 +102,10 @@ if args.git_identity != None:
|
|||||||
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
|
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
|
||||||
desc['git_identity'] = str(p.read().strip())
|
desc['git_identity'] = str(p.read().strip())
|
||||||
p.close()
|
p.close()
|
||||||
|
if args.parameter_xml != None:
|
||||||
|
f = open(args.parameter_xml, "rb")
|
||||||
|
bytes = f.read()
|
||||||
|
desc['parameter_xml'] = base64.b64encode(zlib.compress(bytes,9)).decode('utf-8')
|
||||||
if args.image != None:
|
if args.image != None:
|
||||||
f = open(args.image, "rb")
|
f = open(args.image, "rb")
|
||||||
bytes = f.read()
|
bytes = f.read()
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ MODULES += drivers/l3gd20
|
|||||||
MODULES += drivers/mpu6000
|
MODULES += drivers/mpu6000
|
||||||
MODULES += drivers/hmc5883
|
MODULES += drivers/hmc5883
|
||||||
MODULES += drivers/ms5611
|
MODULES += drivers/ms5611
|
||||||
|
#MODULES += drivers/ll40ls
|
||||||
|
MODULES += drivers/trone
|
||||||
#MODULES += drivers/mb12xx
|
#MODULES += drivers/mb12xx
|
||||||
MODULES += drivers/gps
|
MODULES += drivers/gps
|
||||||
MODULES += drivers/hil
|
MODULES += drivers/hil
|
||||||
@@ -132,6 +134,9 @@ MODULES += lib/launchdetection
|
|||||||
# Hardware test
|
# Hardware test
|
||||||
#MODULES += examples/hwtest
|
#MODULES += examples/hwtest
|
||||||
|
|
||||||
|
# Generate parameter XML file
|
||||||
|
GEN_PARAM_XML = 1
|
||||||
|
|
||||||
#
|
#
|
||||||
# Transitional support - add commands from the NuttX export archive.
|
# Transitional support - add commands from the NuttX export archive.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -27,13 +27,14 @@ MODULES += drivers/l3gd20
|
|||||||
MODULES += drivers/hmc5883
|
MODULES += drivers/hmc5883
|
||||||
MODULES += drivers/ms5611
|
MODULES += drivers/ms5611
|
||||||
MODULES += drivers/mb12xx
|
MODULES += drivers/mb12xx
|
||||||
MODULES += drivers/sf0x
|
# MODULES += drivers/sf0x
|
||||||
MODULES += drivers/ll40ls
|
MODULES += drivers/ll40ls
|
||||||
|
# MODULES += drivers/trone
|
||||||
MODULES += drivers/gps
|
MODULES += drivers/gps
|
||||||
MODULES += drivers/hil
|
MODULES += drivers/hil
|
||||||
MODULES += drivers/hott/hott_telemetry
|
MODULES += drivers/hott/hott_telemetry
|
||||||
MODULES += drivers/hott/hott_sensors
|
MODULES += drivers/hott/hott_sensors
|
||||||
MODULES += drivers/blinkm
|
# MODULES += drivers/blinkm
|
||||||
MODULES += drivers/airspeed
|
MODULES += drivers/airspeed
|
||||||
MODULES += drivers/ets_airspeed
|
MODULES += drivers/ets_airspeed
|
||||||
MODULES += drivers/meas_airspeed
|
MODULES += drivers/meas_airspeed
|
||||||
@@ -141,6 +142,9 @@ MODULES += modules/bottle_drop
|
|||||||
# Hardware test
|
# Hardware test
|
||||||
#MODULES += examples/hwtest
|
#MODULES += examples/hwtest
|
||||||
|
|
||||||
|
# Generate parameter XML file
|
||||||
|
GEN_PARAM_XML = 1
|
||||||
|
|
||||||
#
|
#
|
||||||
# Transitional support - add commands from the NuttX export archive.
|
# Transitional support - add commands from the NuttX export archive.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -467,6 +467,7 @@ endif
|
|||||||
PRODUCT_BUNDLE = $(WORK_DIR)firmware.px4
|
PRODUCT_BUNDLE = $(WORK_DIR)firmware.px4
|
||||||
PRODUCT_BIN = $(WORK_DIR)firmware.bin
|
PRODUCT_BIN = $(WORK_DIR)firmware.bin
|
||||||
PRODUCT_ELF = $(WORK_DIR)firmware.elf
|
PRODUCT_ELF = $(WORK_DIR)firmware.elf
|
||||||
|
PRODUCT_PARAMXML = $(WORK_DIR)/parameters.xml
|
||||||
|
|
||||||
.PHONY: firmware
|
.PHONY: firmware
|
||||||
firmware: $(PRODUCT_BUNDLE)
|
firmware: $(PRODUCT_BUNDLE)
|
||||||
@@ -497,9 +498,17 @@ $(filter %.S.o,$(OBJS)): $(WORK_DIR)%.S.o: %.S $(GLOBAL_DEPS)
|
|||||||
|
|
||||||
$(PRODUCT_BUNDLE): $(PRODUCT_BIN)
|
$(PRODUCT_BUNDLE): $(PRODUCT_BIN)
|
||||||
@$(ECHO) %% Generating $@
|
@$(ECHO) %% Generating $@
|
||||||
|
ifdef GEN_PARAM_XML
|
||||||
|
python $(PX4_BASE)/Tools/px_process_params.py --src-path $(PX4_BASE)/src --xml
|
||||||
|
$(Q) $(MKFW) --prototype $(IMAGE_DIR)/$(BOARD).prototype \
|
||||||
|
--git_identity $(PX4_BASE) \
|
||||||
|
--parameter_xml $(PRODUCT_PARAMXML) \
|
||||||
|
--image $< > $@
|
||||||
|
else
|
||||||
$(Q) $(MKFW) --prototype $(IMAGE_DIR)/$(BOARD).prototype \
|
$(Q) $(MKFW) --prototype $(IMAGE_DIR)/$(BOARD).prototype \
|
||||||
--git_identity $(PX4_BASE) \
|
--git_identity $(PX4_BASE) \
|
||||||
--image $< > $@
|
--image $< > $@
|
||||||
|
endif
|
||||||
|
|
||||||
$(PRODUCT_BIN): $(PRODUCT_ELF)
|
$(PRODUCT_BIN): $(PRODUCT_ELF)
|
||||||
$(call SYM_TO_BIN,$<,$@)
|
$(call SYM_TO_BIN,$<,$@)
|
||||||
|
|||||||
@@ -159,13 +159,15 @@ out:
|
|||||||
int
|
int
|
||||||
Airspeed::probe()
|
Airspeed::probe()
|
||||||
{
|
{
|
||||||
/* on initial power up the device needs more than one retry
|
/* on initial power up the device may need more than one retry
|
||||||
for detection. Once it is running then retries aren't
|
for detection. Once it is running the number of retries can
|
||||||
needed
|
be reduced
|
||||||
*/
|
*/
|
||||||
_retries = 4;
|
_retries = 4;
|
||||||
int ret = measure();
|
int ret = measure();
|
||||||
_retries = 0;
|
|
||||||
|
// drop back to 2 retries once initialised
|
||||||
|
_retries = 2;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -274,7 +274,6 @@ GPS::task_main_trampoline(void *arg)
|
|||||||
void
|
void
|
||||||
GPS::task_main()
|
GPS::task_main()
|
||||||
{
|
{
|
||||||
log("starting");
|
|
||||||
|
|
||||||
/* open the serial port */
|
/* open the serial port */
|
||||||
_serial_fd = ::open(_port, O_RDWR);
|
_serial_fd = ::open(_port, O_RDWR);
|
||||||
|
|||||||
@@ -1349,7 +1349,7 @@ HMC5883 *g_dev_ext = nullptr;
|
|||||||
void start(int bus, enum Rotation rotation);
|
void start(int bus, enum Rotation rotation);
|
||||||
void test(int bus);
|
void test(int bus);
|
||||||
void reset(int bus);
|
void reset(int bus);
|
||||||
void info(int bus);
|
int info(int bus);
|
||||||
int calibrate(int bus);
|
int calibrate(int bus);
|
||||||
void usage();
|
void usage();
|
||||||
|
|
||||||
@@ -1595,17 +1595,23 @@ reset(int bus)
|
|||||||
/**
|
/**
|
||||||
* Print a little info about the driver.
|
* Print a little info about the driver.
|
||||||
*/
|
*/
|
||||||
void
|
int
|
||||||
info(int bus)
|
info(int bus)
|
||||||
{
|
{
|
||||||
HMC5883 *g_dev = (bus == PX4_I2C_BUS_ONBOARD?g_dev_int:g_dev_ext);
|
int ret = 1;
|
||||||
if (g_dev == nullptr)
|
|
||||||
errx(1, "driver not running");
|
|
||||||
|
|
||||||
printf("state @ %p\n", g_dev);
|
HMC5883 *g_dev = (bus == PX4_I2C_BUS_ONBOARD ? g_dev_int : g_dev_ext);
|
||||||
g_dev->print_info();
|
if (g_dev == nullptr) {
|
||||||
|
warnx("not running on bus %d", bus);
|
||||||
|
} else {
|
||||||
|
|
||||||
exit(0);
|
warnx("running on bus: %d (%s)\n", bus, ((PX4_I2C_BUS_ONBOARD) ? "onboard" : "offboard"));
|
||||||
|
|
||||||
|
g_dev->print_info();
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1685,8 +1691,21 @@ hmc5883_main(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* Print driver information.
|
* Print driver information.
|
||||||
*/
|
*/
|
||||||
if (!strcmp(verb, "info") || !strcmp(verb, "status"))
|
if (!strcmp(verb, "info") || !strcmp(verb, "status")) {
|
||||||
hmc5883::info(bus);
|
if (bus == -1) {
|
||||||
|
int ret = 0;
|
||||||
|
if (hmc5883::info(PX4_I2C_BUS_ONBOARD)) {
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hmc5883::info(PX4_I2C_BUS_EXPANSION)) {
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
exit(ret);
|
||||||
|
} else {
|
||||||
|
exit(hmc5883::info(bus));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Autocalibrate the scaling
|
* Autocalibrate the scaling
|
||||||
|
|||||||
+200
-143
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
*
|
*
|
||||||
* Copyright (c) 2013 PX4 Development Team. All rights reserved.
|
* Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@@ -73,15 +73,13 @@
|
|||||||
#include <board_config.h>
|
#include <board_config.h>
|
||||||
|
|
||||||
/* Configuration Constants */
|
/* Configuration Constants */
|
||||||
#define PX4FLOW_BUS PX4_I2C_BUS_EXPANSION
|
|
||||||
#define I2C_FLOW_ADDRESS 0x42 //* 7-bit address. 8-bit address is 0x84
|
#define I2C_FLOW_ADDRESS 0x42 //* 7-bit address. 8-bit address is 0x84
|
||||||
//range 0x42 - 0x49
|
//range 0x42 - 0x49
|
||||||
|
|
||||||
/* PX4FLOW Registers addresses */
|
/* PX4FLOW Registers addresses */
|
||||||
#define PX4FLOW_REG 0x00 /* Measure Register */
|
#define PX4FLOW_REG 0x16 /* Measure Register 22*/
|
||||||
|
|
||||||
#define PX4FLOW_CONVERSION_INTERVAL 8000 /* 8ms 125Hz */
|
|
||||||
|
|
||||||
|
#define PX4FLOW_CONVERSION_INTERVAL 20000 //in microseconds! 20000 = 50 Hz 100000 = 10Hz
|
||||||
/* oddly, ERROR is not defined for c++ */
|
/* oddly, ERROR is not defined for c++ */
|
||||||
#ifdef ERROR
|
#ifdef ERROR
|
||||||
# undef ERROR
|
# undef ERROR
|
||||||
@@ -92,28 +90,42 @@ static const int ERROR = -1;
|
|||||||
# error This requires CONFIG_SCHED_WORKQUEUE.
|
# error This requires CONFIG_SCHED_WORKQUEUE.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//struct i2c_frame
|
struct i2c_frame {
|
||||||
//{
|
uint16_t frame_count;
|
||||||
// uint16_t frame_count;
|
int16_t pixel_flow_x_sum;
|
||||||
// int16_t pixel_flow_x_sum;
|
int16_t pixel_flow_y_sum;
|
||||||
// int16_t pixel_flow_y_sum;
|
int16_t flow_comp_m_x;
|
||||||
// int16_t flow_comp_m_x;
|
int16_t flow_comp_m_y;
|
||||||
// int16_t flow_comp_m_y;
|
int16_t qual;
|
||||||
// int16_t qual;
|
int16_t gyro_x_rate;
|
||||||
// int16_t gyro_x_rate;
|
int16_t gyro_y_rate;
|
||||||
// int16_t gyro_y_rate;
|
int16_t gyro_z_rate;
|
||||||
// int16_t gyro_z_rate;
|
uint8_t gyro_range;
|
||||||
// uint8_t gyro_range;
|
uint8_t sonar_timestamp;
|
||||||
// uint8_t sonar_timestamp;
|
int16_t ground_distance;
|
||||||
// int16_t ground_distance;
|
};
|
||||||
//};
|
struct i2c_frame f;
|
||||||
//
|
|
||||||
//struct i2c_frame f;
|
|
||||||
|
|
||||||
class PX4FLOW : public device::I2C
|
typedef struct i2c_integral_frame {
|
||||||
|
uint16_t frame_count_since_last_readout;
|
||||||
|
int16_t pixel_flow_x_integral;
|
||||||
|
int16_t pixel_flow_y_integral;
|
||||||
|
int16_t gyro_x_rate_integral;
|
||||||
|
int16_t gyro_y_rate_integral;
|
||||||
|
int16_t gyro_z_rate_integral;
|
||||||
|
uint32_t integration_timespan;
|
||||||
|
uint32_t time_since_last_sonar_update;
|
||||||
|
uint16_t ground_distance;
|
||||||
|
int16_t gyro_temperature;
|
||||||
|
uint8_t qual;
|
||||||
|
} __attribute__((packed));
|
||||||
|
struct i2c_integral_frame f_integral;
|
||||||
|
|
||||||
|
|
||||||
|
class PX4FLOW: public device::I2C
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PX4FLOW(int bus = PX4FLOW_BUS, int address = I2C_FLOW_ADDRESS);
|
PX4FLOW(int bus, int address = I2C_FLOW_ADDRESS);
|
||||||
virtual ~PX4FLOW();
|
virtual ~PX4FLOW();
|
||||||
|
|
||||||
virtual int init();
|
virtual int init();
|
||||||
@@ -122,8 +134,8 @@ public:
|
|||||||
virtual int ioctl(struct file *filp, int cmd, unsigned long arg);
|
virtual int ioctl(struct file *filp, int cmd, unsigned long arg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Diagnostics - print some basic information about the driver.
|
* Diagnostics - print some basic information about the driver.
|
||||||
*/
|
*/
|
||||||
void print_info();
|
void print_info();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -144,42 +156,41 @@ private:
|
|||||||
perf_counter_t _buffer_overflows;
|
perf_counter_t _buffer_overflows;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether the device supported by the driver is present at a
|
* Test whether the device supported by the driver is present at a
|
||||||
* specific address.
|
* specific address.
|
||||||
*
|
*
|
||||||
* @param address The I2C bus address to probe.
|
* @param address The I2C bus address to probe.
|
||||||
* @return True if the device is present.
|
* @return True if the device is present.
|
||||||
*/
|
*/
|
||||||
int probe_address(uint8_t address);
|
int probe_address(uint8_t address);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise the automatic measurement state machine and start it.
|
* Initialise the automatic measurement state machine and start it.
|
||||||
*
|
*
|
||||||
* @note This function is called at open and error time. It might make sense
|
* @note This function is called at open and error time. It might make sense
|
||||||
* to make it more aggressive about resetting the bus in case of errors.
|
* to make it more aggressive about resetting the bus in case of errors.
|
||||||
*/
|
*/
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the automatic measurement state machine.
|
* Stop the automatic measurement state machine.
|
||||||
*/
|
*/
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a poll cycle; collect from the previous measurement
|
* Perform a poll cycle; collect from the previous measurement
|
||||||
* and start a new one.
|
* and start a new one.
|
||||||
*/
|
*/
|
||||||
void cycle();
|
void cycle();
|
||||||
int measure();
|
int measure();
|
||||||
int collect();
|
int collect();
|
||||||
/**
|
/**
|
||||||
* Static trampoline from the workq context; because we don't have a
|
* Static trampoline from the workq context; because we don't have a
|
||||||
* generic workq wrapper yet.
|
* generic workq wrapper yet.
|
||||||
*
|
*
|
||||||
* @param arg Instance pointer for the driver that is polling.
|
* @param arg Instance pointer for the driver that is polling.
|
||||||
*/
|
*/
|
||||||
static void cycle_trampoline(void *arg);
|
static void cycle_trampoline(void *arg);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -189,7 +200,7 @@ private:
|
|||||||
extern "C" __EXPORT int px4flow_main(int argc, char *argv[]);
|
extern "C" __EXPORT int px4flow_main(int argc, char *argv[]);
|
||||||
|
|
||||||
PX4FLOW::PX4FLOW(int bus, int address) :
|
PX4FLOW::PX4FLOW(int bus, int address) :
|
||||||
I2C("PX4FLOW", PX4FLOW_DEVICE_PATH, bus, address, 400000),//400khz
|
I2C("PX4FLOW", PX4FLOW_DEVICE_PATH, bus, address, 400000), //400khz
|
||||||
_reports(nullptr),
|
_reports(nullptr),
|
||||||
_sensor_ok(false),
|
_sensor_ok(false),
|
||||||
_measure_ticks(0),
|
_measure_ticks(0),
|
||||||
@@ -228,21 +239,12 @@ PX4FLOW::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* allocate basic report buffers */
|
/* allocate basic report buffers */
|
||||||
_reports = new RingBuffer(2, sizeof(struct optical_flow_s));
|
_reports = new RingBuffer(2, sizeof(optical_flow_s));
|
||||||
|
|
||||||
if (_reports == nullptr) {
|
if (_reports == nullptr) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get a publish handle on the px4flow topic */
|
|
||||||
struct optical_flow_s zero_report;
|
|
||||||
memset(&zero_report, 0, sizeof(zero_report));
|
|
||||||
_px4flow_topic = orb_advertise(ORB_ID(optical_flow), &zero_report);
|
|
||||||
|
|
||||||
if (_px4flow_topic < 0) {
|
|
||||||
warnx("failed to create px4flow object. Did you start uOrb?");
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = OK;
|
ret = OK;
|
||||||
/* sensor is ok, but we don't really know if it is within range */
|
/* sensor is ok, but we don't really know if it is within range */
|
||||||
_sensor_ok = true;
|
_sensor_ok = true;
|
||||||
@@ -410,9 +412,6 @@ PX4FLOW::read(struct file *filp, char *buffer, size_t buflen)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait for it to complete */
|
|
||||||
usleep(PX4FLOW_CONVERSION_INTERVAL);
|
|
||||||
|
|
||||||
/* run the collection phase */
|
/* run the collection phase */
|
||||||
if (OK != collect()) {
|
if (OK != collect()) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
@@ -442,6 +441,7 @@ PX4FLOW::measure()
|
|||||||
|
|
||||||
if (OK != ret) {
|
if (OK != ret) {
|
||||||
perf_count(_comms_errors);
|
perf_count(_comms_errors);
|
||||||
|
debug("i2c::transfer returned %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,14 +453,20 @@ PX4FLOW::measure()
|
|||||||
int
|
int
|
||||||
PX4FLOW::collect()
|
PX4FLOW::collect()
|
||||||
{
|
{
|
||||||
int ret = -EIO;
|
int ret = -EIO;
|
||||||
|
|
||||||
/* read from the sensor */
|
/* read from the sensor */
|
||||||
uint8_t val[22] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
uint8_t val[47] = { 0 };
|
||||||
|
|
||||||
perf_begin(_sample_perf);
|
perf_begin(_sample_perf);
|
||||||
|
|
||||||
ret = transfer(nullptr, 0, &val[0], 22);
|
if (PX4FLOW_REG == 0x00) {
|
||||||
|
ret = transfer(nullptr, 0, &val[0], 47); // read 47 bytes (22+25 : frame1 + frame2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PX4FLOW_REG == 0x16) {
|
||||||
|
ret = transfer(nullptr, 0, &val[0], 25); // read 25 bytes (only frame2)
|
||||||
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
debug("error reading from sensor: %d", ret);
|
debug("error reading from sensor: %d", ret);
|
||||||
@@ -469,36 +475,74 @@ PX4FLOW::collect()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// f.frame_count = val[1] << 8 | val[0];
|
if (PX4FLOW_REG == 0) {
|
||||||
// f.pixel_flow_x_sum= val[3] << 8 | val[2];
|
f.frame_count = val[1] << 8 | val[0];
|
||||||
// f.pixel_flow_y_sum= val[5] << 8 | val[4];
|
f.pixel_flow_x_sum = val[3] << 8 | val[2];
|
||||||
// f.flow_comp_m_x= val[7] << 8 | val[6];
|
f.pixel_flow_y_sum = val[5] << 8 | val[4];
|
||||||
// f.flow_comp_m_y= val[9] << 8 | val[8];
|
f.flow_comp_m_x = val[7] << 8 | val[6];
|
||||||
// f.qual= val[11] << 8 | val[10];
|
f.flow_comp_m_y = val[9] << 8 | val[8];
|
||||||
// f.gyro_x_rate= val[13] << 8 | val[12];
|
f.qual = val[11] << 8 | val[10];
|
||||||
// f.gyro_y_rate= val[15] << 8 | val[14];
|
f.gyro_x_rate = val[13] << 8 | val[12];
|
||||||
// f.gyro_z_rate= val[17] << 8 | val[16];
|
f.gyro_y_rate = val[15] << 8 | val[14];
|
||||||
// f.gyro_range= val[18];
|
f.gyro_z_rate = val[17] << 8 | val[16];
|
||||||
// f.sonar_timestamp= val[19];
|
f.gyro_range = val[18];
|
||||||
// f.ground_distance= val[21] << 8 | val[20];
|
f.sonar_timestamp = val[19];
|
||||||
|
f.ground_distance = val[21] << 8 | val[20];
|
||||||
|
|
||||||
|
f_integral.frame_count_since_last_readout = val[23] << 8 | val[22];
|
||||||
|
f_integral.pixel_flow_x_integral = val[25] << 8 | val[24];
|
||||||
|
f_integral.pixel_flow_y_integral = val[27] << 8 | val[26];
|
||||||
|
f_integral.gyro_x_rate_integral = val[29] << 8 | val[28];
|
||||||
|
f_integral.gyro_y_rate_integral = val[31] << 8 | val[30];
|
||||||
|
f_integral.gyro_z_rate_integral = val[33] << 8 | val[32];
|
||||||
|
f_integral.integration_timespan = val[37] << 24 | val[36] << 16
|
||||||
|
| val[35] << 8 | val[34];
|
||||||
|
f_integral.time_since_last_sonar_update = val[41] << 24 | val[40] << 16
|
||||||
|
| val[39] << 8 | val[38];
|
||||||
|
f_integral.ground_distance = val[43] << 8 | val[42];
|
||||||
|
f_integral.gyro_temperature = val[45] << 8 | val[44];
|
||||||
|
f_integral.qual = val[46];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PX4FLOW_REG == 0x16) {
|
||||||
|
f_integral.frame_count_since_last_readout = val[1] << 8 | val[0];
|
||||||
|
f_integral.pixel_flow_x_integral = val[3] << 8 | val[2];
|
||||||
|
f_integral.pixel_flow_y_integral = val[5] << 8 | val[4];
|
||||||
|
f_integral.gyro_x_rate_integral = val[7] << 8 | val[6];
|
||||||
|
f_integral.gyro_y_rate_integral = val[9] << 8 | val[8];
|
||||||
|
f_integral.gyro_z_rate_integral = val[11] << 8 | val[10];
|
||||||
|
f_integral.integration_timespan = val[15] << 24 | val[14] << 16 | val[13] << 8 | val[12];
|
||||||
|
f_integral.time_since_last_sonar_update = val[19] << 24 | val[18] << 16 | val[17] << 8 | val[16];
|
||||||
|
f_integral.ground_distance = val[21] << 8 | val[20];
|
||||||
|
f_integral.gyro_temperature = val[23] << 8 | val[22];
|
||||||
|
f_integral.qual = val[24];
|
||||||
|
}
|
||||||
|
|
||||||
int16_t flowcx = val[7] << 8 | val[6];
|
|
||||||
int16_t flowcy = val[9] << 8 | val[8];
|
|
||||||
int16_t gdist = val[21] << 8 | val[20];
|
|
||||||
|
|
||||||
struct optical_flow_s report;
|
struct optical_flow_s report;
|
||||||
report.flow_comp_x_m = float(flowcx) / 1000.0f;
|
|
||||||
report.flow_comp_y_m = float(flowcy) / 1000.0f;
|
|
||||||
report.flow_raw_x = val[3] << 8 | val[2];
|
|
||||||
report.flow_raw_y = val[5] << 8 | val[4];
|
|
||||||
report.ground_distance_m = float(gdist) / 1000.0f;
|
|
||||||
report.quality = val[10];
|
|
||||||
report.sensor_id = 0;
|
|
||||||
report.timestamp = hrt_absolute_time();
|
report.timestamp = hrt_absolute_time();
|
||||||
|
report.pixel_flow_x_integral = static_cast<float>(f_integral.pixel_flow_x_integral) / 10000.0f;//convert to radians
|
||||||
|
report.pixel_flow_y_integral = static_cast<float>(f_integral.pixel_flow_y_integral) / 10000.0f;//convert to radians
|
||||||
|
report.frame_count_since_last_readout = f_integral.frame_count_since_last_readout;
|
||||||
|
report.ground_distance_m = static_cast<float>(f_integral.ground_distance) / 1000.0f;//convert to meters
|
||||||
|
report.quality = f_integral.qual; //0:bad ; 255 max quality
|
||||||
|
report.gyro_x_rate_integral = static_cast<float>(f_integral.gyro_x_rate_integral) / 10000.0f; //convert to radians
|
||||||
|
report.gyro_y_rate_integral = static_cast<float>(f_integral.gyro_y_rate_integral) / 10000.0f; //convert to radians
|
||||||
|
report.gyro_z_rate_integral = static_cast<float>(f_integral.gyro_z_rate_integral) / 10000.0f; //convert to radians
|
||||||
|
report.integration_timespan = f_integral.integration_timespan; //microseconds
|
||||||
|
report.time_since_last_sonar_update = f_integral.time_since_last_sonar_update;//microseconds
|
||||||
|
report.gyro_temperature = f_integral.gyro_temperature;//Temperature * 100 in centi-degrees Celsius
|
||||||
|
|
||||||
|
report.sensor_id = 0;
|
||||||
|
|
||||||
/* publish it */
|
if (_px4flow_topic < 0) {
|
||||||
orb_publish(ORB_ID(optical_flow), _px4flow_topic, &report);
|
_px4flow_topic = orb_advertise(ORB_ID(optical_flow), &report);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* publish it */
|
||||||
|
orb_publish(ORB_ID(optical_flow), _px4flow_topic, &report);
|
||||||
|
}
|
||||||
|
|
||||||
/* post a report to the ring */
|
/* post a report to the ring */
|
||||||
if (_reports->force(&report)) {
|
if (_reports->force(&report)) {
|
||||||
@@ -558,50 +602,21 @@ PX4FLOW::cycle_trampoline(void *arg)
|
|||||||
void
|
void
|
||||||
PX4FLOW::cycle()
|
PX4FLOW::cycle()
|
||||||
{
|
{
|
||||||
/* collection phase? */
|
|
||||||
if (_collect_phase) {
|
|
||||||
|
|
||||||
/* perform collection */
|
|
||||||
if (OK != collect()) {
|
|
||||||
debug("collection error");
|
|
||||||
/* restart the measurement state machine */
|
|
||||||
start();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* next phase is measurement */
|
|
||||||
_collect_phase = false;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Is there a collect->measure gap?
|
|
||||||
*/
|
|
||||||
if (_measure_ticks > USEC2TICK(PX4FLOW_CONVERSION_INTERVAL)) {
|
|
||||||
|
|
||||||
/* schedule a fresh cycle call when we are ready to measure again */
|
|
||||||
work_queue(HPWORK,
|
|
||||||
&_work,
|
|
||||||
(worker_t)&PX4FLOW::cycle_trampoline,
|
|
||||||
this,
|
|
||||||
_measure_ticks - USEC2TICK(PX4FLOW_CONVERSION_INTERVAL));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* measurement phase */
|
|
||||||
if (OK != measure()) {
|
if (OK != measure()) {
|
||||||
debug("measure error");
|
debug("measure error");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* next phase is collection */
|
/* perform collection */
|
||||||
_collect_phase = true;
|
if (OK != collect()) {
|
||||||
|
debug("collection error");
|
||||||
|
/* restart the measurement state machine */
|
||||||
|
start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
work_queue(HPWORK, &_work, (worker_t)&PX4FLOW::cycle_trampoline, this,
|
||||||
|
_measure_ticks);
|
||||||
|
|
||||||
/* schedule a fresh cycle call when the measurement is done */
|
|
||||||
work_queue(HPWORK,
|
|
||||||
&_work,
|
|
||||||
(worker_t)&PX4FLOW::cycle_trampoline,
|
|
||||||
this,
|
|
||||||
USEC2TICK(PX4FLOW_CONVERSION_INTERVAL));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -647,14 +662,41 @@ start()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* create the driver */
|
/* create the driver */
|
||||||
g_dev = new PX4FLOW(PX4FLOW_BUS);
|
g_dev = new PX4FLOW(PX4_I2C_BUS_EXPANSION);
|
||||||
|
|
||||||
if (g_dev == nullptr) {
|
if (g_dev == nullptr) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OK != g_dev->init()) {
|
if (OK != g_dev->init()) {
|
||||||
goto fail;
|
|
||||||
|
#ifdef PX4_I2C_BUS_ESC
|
||||||
|
delete g_dev;
|
||||||
|
/* try 2nd bus */
|
||||||
|
g_dev = new PX4FLOW(PX4_I2C_BUS_ESC);
|
||||||
|
|
||||||
|
if (g_dev == nullptr) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OK != g_dev->init()) {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
delete g_dev;
|
||||||
|
/* try 3rd bus */
|
||||||
|
g_dev = new PX4FLOW(PX4_I2C_BUS_ONBOARD);
|
||||||
|
|
||||||
|
if (g_dev == nullptr) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OK != g_dev->init()) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PX4_I2C_BUS_ESC
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set the poll rate to default, starts automatic data collection */
|
/* set the poll rate to default, starts automatic data collection */
|
||||||
@@ -683,7 +725,8 @@ fail:
|
|||||||
/**
|
/**
|
||||||
* Stop the driver
|
* Stop the driver
|
||||||
*/
|
*/
|
||||||
void stop()
|
void
|
||||||
|
stop()
|
||||||
{
|
{
|
||||||
if (g_dev != nullptr) {
|
if (g_dev != nullptr) {
|
||||||
delete g_dev;
|
delete g_dev;
|
||||||
@@ -714,6 +757,7 @@ test()
|
|||||||
err(1, "%s open failed (try 'px4flow start' if the driver is not running", PX4FLOW_DEVICE_PATH);
|
err(1, "%s open failed (try 'px4flow start' if the driver is not running", PX4FLOW_DEVICE_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* do a simple demand read */
|
/* do a simple demand read */
|
||||||
sz = read(fd, &report, sizeof(report));
|
sz = read(fd, &report, sizeof(report));
|
||||||
|
|
||||||
@@ -723,18 +767,18 @@ test()
|
|||||||
}
|
}
|
||||||
|
|
||||||
warnx("single read");
|
warnx("single read");
|
||||||
warnx("flowx: %0.2f m/s", (double)report.flow_comp_x_m);
|
warnx("pixel_flow_x_integral: %i", f_integral.pixel_flow_x_integral);
|
||||||
warnx("flowy: %0.2f m/s", (double)report.flow_comp_y_m);
|
warnx("pixel_flow_y_integral: %i", f_integral.pixel_flow_y_integral);
|
||||||
warnx("time: %lld", report.timestamp);
|
warnx("framecount_integral: %u",
|
||||||
|
f_integral.frame_count_since_last_readout);
|
||||||
|
|
||||||
|
/* start the sensor polling at 10Hz */
|
||||||
/* start the sensor polling at 2Hz */
|
if (OK != ioctl(fd, SENSORIOCSPOLLRATE, 10)) {
|
||||||
if (OK != ioctl(fd, SENSORIOCSPOLLRATE, 2)) {
|
errx(1, "failed to set 10Hz poll rate");
|
||||||
errx(1, "failed to set 2Hz poll rate");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read the sensor 5x and report each value */
|
/* read the sensor 5x and report each value */
|
||||||
for (unsigned i = 0; i < 5; i++) {
|
for (unsigned i = 0; i < 10; i++) {
|
||||||
struct pollfd fds;
|
struct pollfd fds;
|
||||||
|
|
||||||
/* wait for data to be ready */
|
/* wait for data to be ready */
|
||||||
@@ -754,9 +798,22 @@ test()
|
|||||||
}
|
}
|
||||||
|
|
||||||
warnx("periodic read %u", i);
|
warnx("periodic read %u", i);
|
||||||
warnx("flowx: %0.2f m/s", (double)report.flow_comp_x_m);
|
|
||||||
warnx("flowy: %0.2f m/s", (double)report.flow_comp_y_m);
|
warnx("framecount_total: %u", f.frame_count);
|
||||||
warnx("time: %lld", report.timestamp);
|
warnx("framecount_integral: %u",
|
||||||
|
f_integral.frame_count_since_last_readout);
|
||||||
|
warnx("pixel_flow_x_integral: %i", f_integral.pixel_flow_x_integral);
|
||||||
|
warnx("pixel_flow_y_integral: %i", f_integral.pixel_flow_y_integral);
|
||||||
|
warnx("gyro_x_rate_integral: %i", f_integral.gyro_x_rate_integral);
|
||||||
|
warnx("gyro_y_rate_integral: %i", f_integral.gyro_y_rate_integral);
|
||||||
|
warnx("gyro_z_rate_integral: %i", f_integral.gyro_z_rate_integral);
|
||||||
|
warnx("integration_timespan [us]: %u", f_integral.integration_timespan);
|
||||||
|
warnx("ground_distance: %0.2f m",
|
||||||
|
(double) f_integral.ground_distance / 1000);
|
||||||
|
warnx("time since last sonar update [us]: %i",
|
||||||
|
f_integral.time_since_last_sonar_update);
|
||||||
|
warnx("quality integration average : %i", f_integral.qual);
|
||||||
|
warnx("quality : %i", f.qual);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-45
@@ -817,6 +817,11 @@ PX4IO::init()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* set safety to off if circuit breaker enabled */
|
||||||
|
if (circuit_breaker_enabled("CBRK_IO_SAFETY", CBRK_IO_SAFETY_KEY)) {
|
||||||
|
(void)io_reg_set(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_FORCE_SAFETY_OFF, PX4IO_FORCE_SAFETY_MAGIC);
|
||||||
|
}
|
||||||
|
|
||||||
/* try to claim the generic PWM output device node as well - it's OK if we fail at this */
|
/* try to claim the generic PWM output device node as well - it's OK if we fail at this */
|
||||||
ret = register_driver(PWM_OUTPUT_DEVICE_PATH, &fops, 0666, (void *)this);
|
ret = register_driver(PWM_OUTPUT_DEVICE_PATH, &fops, 0666, (void *)this);
|
||||||
|
|
||||||
@@ -1155,52 +1160,54 @@ PX4IO::io_set_arming_state()
|
|||||||
actuator_armed_s armed; ///< system armed state
|
actuator_armed_s armed; ///< system armed state
|
||||||
vehicle_control_mode_s control_mode; ///< vehicle_control_mode
|
vehicle_control_mode_s control_mode; ///< vehicle_control_mode
|
||||||
|
|
||||||
orb_copy(ORB_ID(actuator_armed), _t_actuator_armed, &armed);
|
int have_armed = orb_copy(ORB_ID(actuator_armed), _t_actuator_armed, &armed);
|
||||||
orb_copy(ORB_ID(vehicle_control_mode), _t_vehicle_control_mode, &control_mode);
|
int have_control_mode = orb_copy(ORB_ID(vehicle_control_mode), _t_vehicle_control_mode, &control_mode);
|
||||||
|
|
||||||
uint16_t set = 0;
|
uint16_t set = 0;
|
||||||
uint16_t clear = 0;
|
uint16_t clear = 0;
|
||||||
|
|
||||||
if (armed.armed) {
|
if (have_armed == OK) {
|
||||||
set |= PX4IO_P_SETUP_ARMING_FMU_ARMED;
|
if (armed.armed) {
|
||||||
|
set |= PX4IO_P_SETUP_ARMING_FMU_ARMED;
|
||||||
|
} else {
|
||||||
|
clear |= PX4IO_P_SETUP_ARMING_FMU_ARMED;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
if (armed.lockdown && !_lockdown_override) {
|
||||||
clear |= PX4IO_P_SETUP_ARMING_FMU_ARMED;
|
set |= PX4IO_P_SETUP_ARMING_LOCKDOWN;
|
||||||
|
} else {
|
||||||
|
clear |= PX4IO_P_SETUP_ARMING_LOCKDOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do not set failsafe if circuit breaker is enabled */
|
||||||
|
if (armed.force_failsafe && !_cb_flighttermination) {
|
||||||
|
set |= PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE;
|
||||||
|
} else {
|
||||||
|
clear |= PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX this is for future support in the commander
|
||||||
|
// but can be removed if unneeded
|
||||||
|
// if (armed.termination_failsafe) {
|
||||||
|
// set |= PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE;
|
||||||
|
// } else {
|
||||||
|
// clear |= PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (armed.ready_to_arm) {
|
||||||
|
set |= PX4IO_P_SETUP_ARMING_IO_ARM_OK;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
clear |= PX4IO_P_SETUP_ARMING_IO_ARM_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (armed.lockdown && !_lockdown_override) {
|
if (have_control_mode == OK) {
|
||||||
set |= PX4IO_P_SETUP_ARMING_LOCKDOWN;
|
if (control_mode.flag_external_manual_override_ok) {
|
||||||
} else {
|
set |= PX4IO_P_SETUP_ARMING_MANUAL_OVERRIDE_OK;
|
||||||
clear |= PX4IO_P_SETUP_ARMING_LOCKDOWN;
|
} else {
|
||||||
}
|
clear |= PX4IO_P_SETUP_ARMING_MANUAL_OVERRIDE_OK;
|
||||||
|
}
|
||||||
/* Do not set failsafe if circuit breaker is enabled */
|
|
||||||
if (armed.force_failsafe && !_cb_flighttermination) {
|
|
||||||
set |= PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE;
|
|
||||||
} else {
|
|
||||||
clear |= PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX this is for future support in the commander
|
|
||||||
// but can be removed if unneeded
|
|
||||||
// if (armed.termination_failsafe) {
|
|
||||||
// set |= PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE;
|
|
||||||
// } else {
|
|
||||||
// clear |= PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (armed.ready_to_arm) {
|
|
||||||
set |= PX4IO_P_SETUP_ARMING_IO_ARM_OK;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
clear |= PX4IO_P_SETUP_ARMING_IO_ARM_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (control_mode.flag_external_manual_override_ok) {
|
|
||||||
set |= PX4IO_P_SETUP_ARMING_MANUAL_OVERRIDE_OK;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
clear |= PX4IO_P_SETUP_ARMING_MANUAL_OVERRIDE_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return io_reg_modify(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_ARMING, clear, set);
|
return io_reg_modify(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_ARMING, clear, set);
|
||||||
@@ -2193,7 +2200,7 @@ PX4IO::ioctl(file * filep, int cmd, unsigned long arg)
|
|||||||
struct pwm_output_values* pwm = (struct pwm_output_values*)arg;
|
struct pwm_output_values* pwm = (struct pwm_output_values*)arg;
|
||||||
if (pwm->channel_count > _max_actuators)
|
if (pwm->channel_count > _max_actuators)
|
||||||
/* fail with error */
|
/* fail with error */
|
||||||
return E2BIG;
|
return -E2BIG;
|
||||||
|
|
||||||
/* copy values to registers in IO */
|
/* copy values to registers in IO */
|
||||||
ret = io_reg_set(PX4IO_PAGE_FAILSAFE_PWM, 0, pwm->values, pwm->channel_count);
|
ret = io_reg_set(PX4IO_PAGE_FAILSAFE_PWM, 0, pwm->values, pwm->channel_count);
|
||||||
@@ -2212,7 +2219,7 @@ PX4IO::ioctl(file * filep, int cmd, unsigned long arg)
|
|||||||
struct pwm_output_values* pwm = (struct pwm_output_values*)arg;
|
struct pwm_output_values* pwm = (struct pwm_output_values*)arg;
|
||||||
if (pwm->channel_count > _max_actuators)
|
if (pwm->channel_count > _max_actuators)
|
||||||
/* fail with error */
|
/* fail with error */
|
||||||
return E2BIG;
|
return -E2BIG;
|
||||||
|
|
||||||
/* copy values to registers in IO */
|
/* copy values to registers in IO */
|
||||||
ret = io_reg_set(PX4IO_PAGE_DISARMED_PWM, 0, pwm->values, pwm->channel_count);
|
ret = io_reg_set(PX4IO_PAGE_DISARMED_PWM, 0, pwm->values, pwm->channel_count);
|
||||||
@@ -2231,7 +2238,7 @@ PX4IO::ioctl(file * filep, int cmd, unsigned long arg)
|
|||||||
struct pwm_output_values* pwm = (struct pwm_output_values*)arg;
|
struct pwm_output_values* pwm = (struct pwm_output_values*)arg;
|
||||||
if (pwm->channel_count > _max_actuators)
|
if (pwm->channel_count > _max_actuators)
|
||||||
/* fail with error */
|
/* fail with error */
|
||||||
return E2BIG;
|
return -E2BIG;
|
||||||
|
|
||||||
/* copy values to registers in IO */
|
/* copy values to registers in IO */
|
||||||
ret = io_reg_set(PX4IO_PAGE_CONTROL_MIN_PWM, 0, pwm->values, pwm->channel_count);
|
ret = io_reg_set(PX4IO_PAGE_CONTROL_MIN_PWM, 0, pwm->values, pwm->channel_count);
|
||||||
@@ -2250,7 +2257,7 @@ PX4IO::ioctl(file * filep, int cmd, unsigned long arg)
|
|||||||
struct pwm_output_values* pwm = (struct pwm_output_values*)arg;
|
struct pwm_output_values* pwm = (struct pwm_output_values*)arg;
|
||||||
if (pwm->channel_count > _max_actuators)
|
if (pwm->channel_count > _max_actuators)
|
||||||
/* fail with error */
|
/* fail with error */
|
||||||
return E2BIG;
|
return -E2BIG;
|
||||||
|
|
||||||
/* copy values to registers in IO */
|
/* copy values to registers in IO */
|
||||||
ret = io_reg_set(PX4IO_PAGE_CONTROL_MAX_PWM, 0, pwm->values, pwm->channel_count);
|
ret = io_reg_set(PX4IO_PAGE_CONTROL_MAX_PWM, 0, pwm->values, pwm->channel_count);
|
||||||
@@ -2587,9 +2594,9 @@ PX4IO::ioctl(file * filep, int cmd, unsigned long arg)
|
|||||||
on param_get()
|
on param_get()
|
||||||
*/
|
*/
|
||||||
struct pwm_output_rc_config* config = (struct pwm_output_rc_config*)arg;
|
struct pwm_output_rc_config* config = (struct pwm_output_rc_config*)arg;
|
||||||
if (config->channel >= _max_actuators) {
|
if (config->channel >= RC_INPUT_MAX_CHANNELS) {
|
||||||
/* fail with error */
|
/* fail with error */
|
||||||
return E2BIG;
|
return -E2BIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy values to registers in IO */
|
/* copy values to registers in IO */
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
############################################################################
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 PX4 Development Team. All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in
|
||||||
|
# the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||||
|
# used to endorse or promote products derived from this software
|
||||||
|
# without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||||
|
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Makefile to build the TeraRanger One range finder driver
|
||||||
|
#
|
||||||
|
|
||||||
|
MODULE_COMMAND = trone
|
||||||
|
|
||||||
|
SRCS = trone.cpp
|
||||||
|
|
||||||
|
MODULE_STACKSIZE = 1200
|
||||||
|
|
||||||
|
MAXOPTIMIZATION = -Os
|
||||||
@@ -0,0 +1,913 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 PX4 Development Team. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||||
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file trone.cpp
|
||||||
|
* @author Luis Rodrigues
|
||||||
|
*
|
||||||
|
* Driver for the TeraRanger One range finders connected via I2C.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <nuttx/config.h>
|
||||||
|
|
||||||
|
#include <drivers/device/i2c.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <nuttx/arch.h>
|
||||||
|
#include <nuttx/wqueue.h>
|
||||||
|
#include <nuttx/clock.h>
|
||||||
|
|
||||||
|
#include <systemlib/perf_counter.h>
|
||||||
|
#include <systemlib/err.h>
|
||||||
|
|
||||||
|
#include <drivers/drv_hrt.h>
|
||||||
|
#include <drivers/drv_range_finder.h>
|
||||||
|
#include <drivers/device/ringbuffer.h>
|
||||||
|
|
||||||
|
#include <uORB/uORB.h>
|
||||||
|
#include <uORB/topics/subsystem_info.h>
|
||||||
|
|
||||||
|
#include <board_config.h>
|
||||||
|
|
||||||
|
/* Configuration Constants */
|
||||||
|
#define TRONE_BUS PX4_I2C_BUS_EXPANSION
|
||||||
|
#define TRONE_BASEADDR 0x30 /* 7-bit address */
|
||||||
|
#define TRONE_DEVICE_PATH "/dev/trone"
|
||||||
|
|
||||||
|
/* TRONE Registers addresses */
|
||||||
|
|
||||||
|
#define TRONE_MEASURE_REG 0x00 /* Measure range register */
|
||||||
|
|
||||||
|
/* Device limits */
|
||||||
|
#define TRONE_MIN_DISTANCE (0.20f)
|
||||||
|
#define TRONE_MAX_DISTANCE (14.00f)
|
||||||
|
|
||||||
|
#define TRONE_CONVERSION_INTERVAL 50000 /* 50ms */
|
||||||
|
|
||||||
|
/* oddly, ERROR is not defined for c++ */
|
||||||
|
#ifdef ERROR
|
||||||
|
# undef ERROR
|
||||||
|
#endif
|
||||||
|
static const int ERROR = -1;
|
||||||
|
|
||||||
|
#ifndef CONFIG_SCHED_WORKQUEUE
|
||||||
|
# error This requires CONFIG_SCHED_WORKQUEUE.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class TRONE : public device::I2C
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TRONE(int bus = TRONE_BUS, int address = TRONE_BASEADDR);
|
||||||
|
virtual ~TRONE();
|
||||||
|
|
||||||
|
virtual int init();
|
||||||
|
|
||||||
|
virtual ssize_t read(struct file *filp, char *buffer, size_t buflen);
|
||||||
|
virtual int ioctl(struct file *filp, int cmd, unsigned long arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diagnostics - print some basic information about the driver.
|
||||||
|
*/
|
||||||
|
void print_info();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int probe();
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _min_distance;
|
||||||
|
float _max_distance;
|
||||||
|
work_s _work;
|
||||||
|
RingBuffer *_reports;
|
||||||
|
bool _sensor_ok;
|
||||||
|
int _measure_ticks;
|
||||||
|
bool _collect_phase;
|
||||||
|
int _class_instance;
|
||||||
|
|
||||||
|
orb_advert_t _range_finder_topic;
|
||||||
|
|
||||||
|
perf_counter_t _sample_perf;
|
||||||
|
perf_counter_t _comms_errors;
|
||||||
|
perf_counter_t _buffer_overflows;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the device supported by the driver is present at a
|
||||||
|
* specific address.
|
||||||
|
*
|
||||||
|
* @param address The I2C bus address to probe.
|
||||||
|
* @return True if the device is present.
|
||||||
|
*/
|
||||||
|
int probe_address(uint8_t address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the automatic measurement state machine and start it.
|
||||||
|
*
|
||||||
|
* @note This function is called at open and error time. It might make sense
|
||||||
|
* to make it more aggressive about resetting the bus in case of errors.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the automatic measurement state machine.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the min and max distance thresholds if you want the end points of the sensors
|
||||||
|
* range to be brought in at all, otherwise it will use the defaults TRONE_MIN_DISTANCE
|
||||||
|
* and TRONE_MAX_DISTANCE
|
||||||
|
*/
|
||||||
|
void set_minimum_distance(float min);
|
||||||
|
void set_maximum_distance(float max);
|
||||||
|
float get_minimum_distance();
|
||||||
|
float get_maximum_distance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a poll cycle; collect from the previous measurement
|
||||||
|
* and start a new one.
|
||||||
|
*/
|
||||||
|
void cycle();
|
||||||
|
int measure();
|
||||||
|
int collect();
|
||||||
|
/**
|
||||||
|
* Static trampoline from the workq context; because we don't have a
|
||||||
|
* generic workq wrapper yet.
|
||||||
|
*
|
||||||
|
* @param arg Instance pointer for the driver that is polling.
|
||||||
|
*/
|
||||||
|
static void cycle_trampoline(void *arg);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t crc_table[] = {
|
||||||
|
0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31,
|
||||||
|
0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
|
||||||
|
0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9,
|
||||||
|
0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
|
||||||
|
0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1,
|
||||||
|
0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
|
||||||
|
0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe,
|
||||||
|
0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
|
||||||
|
0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16,
|
||||||
|
0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
|
||||||
|
0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80,
|
||||||
|
0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
|
||||||
|
0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8,
|
||||||
|
0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
|
||||||
|
0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10,
|
||||||
|
0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
|
||||||
|
0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f,
|
||||||
|
0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
|
||||||
|
0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
|
||||||
|
0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
|
||||||
|
0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef,
|
||||||
|
0xfa, 0xfd, 0xf4, 0xf3
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t crc8(uint8_t *p, uint8_t len){
|
||||||
|
uint16_t i;
|
||||||
|
uint16_t crc = 0x0;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
i = (crc ^ *p++) & 0xFF;
|
||||||
|
crc = (crc_table[i] ^ (crc << 8)) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Driver 'main' command.
|
||||||
|
*/
|
||||||
|
extern "C" __EXPORT int trone_main(int argc, char *argv[]);
|
||||||
|
|
||||||
|
TRONE::TRONE(int bus, int address) :
|
||||||
|
I2C("TRONE", TRONE_DEVICE_PATH, bus, address, 100000),
|
||||||
|
_min_distance(TRONE_MIN_DISTANCE),
|
||||||
|
_max_distance(TRONE_MAX_DISTANCE),
|
||||||
|
_reports(nullptr),
|
||||||
|
_sensor_ok(false),
|
||||||
|
_measure_ticks(0),
|
||||||
|
_collect_phase(false),
|
||||||
|
_class_instance(-1),
|
||||||
|
_range_finder_topic(-1),
|
||||||
|
_sample_perf(perf_alloc(PC_ELAPSED, "trone_read")),
|
||||||
|
_comms_errors(perf_alloc(PC_COUNT, "trone_comms_errors")),
|
||||||
|
_buffer_overflows(perf_alloc(PC_COUNT, "trone_buffer_overflows"))
|
||||||
|
{
|
||||||
|
// up the retries since the device misses the first measure attempts
|
||||||
|
I2C::_retries = 3;
|
||||||
|
|
||||||
|
// enable debug() calls
|
||||||
|
_debug_enabled = false;
|
||||||
|
|
||||||
|
// work_cancel in the dtor will explode if we don't do this...
|
||||||
|
memset(&_work, 0, sizeof(_work));
|
||||||
|
}
|
||||||
|
|
||||||
|
TRONE::~TRONE()
|
||||||
|
{
|
||||||
|
/* make sure we are truly inactive */
|
||||||
|
stop();
|
||||||
|
|
||||||
|
/* free any existing reports */
|
||||||
|
if (_reports != nullptr) {
|
||||||
|
delete _reports;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_class_instance != -1) {
|
||||||
|
unregister_class_devname(RANGE_FINDER_DEVICE_PATH, _class_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free perf counters
|
||||||
|
perf_free(_sample_perf);
|
||||||
|
perf_free(_comms_errors);
|
||||||
|
perf_free(_buffer_overflows);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TRONE::init()
|
||||||
|
{
|
||||||
|
int ret = ERROR;
|
||||||
|
|
||||||
|
/* do I2C init (and probe) first */
|
||||||
|
if (I2C::init() != OK) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate basic report buffers */
|
||||||
|
_reports = new RingBuffer(2, sizeof(range_finder_report));
|
||||||
|
|
||||||
|
if (_reports == nullptr) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
_class_instance = register_class_devname(RANGE_FINDER_DEVICE_PATH);
|
||||||
|
|
||||||
|
if (_class_instance == CLASS_DEVICE_PRIMARY) {
|
||||||
|
/* get a publish handle on the range finder topic */
|
||||||
|
struct range_finder_report rf_report;
|
||||||
|
measure();
|
||||||
|
_reports->get(&rf_report);
|
||||||
|
_range_finder_topic = orb_advertise(ORB_ID(sensor_range_finder), &rf_report);
|
||||||
|
|
||||||
|
if (_range_finder_topic < 0) {
|
||||||
|
debug("failed to create sensor_range_finder object. Did you start uOrb?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = OK;
|
||||||
|
/* sensor is ok, but we don't really know if it is within range */
|
||||||
|
_sensor_ok = true;
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TRONE::probe()
|
||||||
|
{
|
||||||
|
return measure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TRONE::set_minimum_distance(float min)
|
||||||
|
{
|
||||||
|
_min_distance = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TRONE::set_maximum_distance(float max)
|
||||||
|
{
|
||||||
|
_max_distance = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
TRONE::get_minimum_distance()
|
||||||
|
{
|
||||||
|
return _min_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
TRONE::get_maximum_distance()
|
||||||
|
{
|
||||||
|
return _max_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TRONE::ioctl(struct file *filp, int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
switch (cmd) {
|
||||||
|
|
||||||
|
case SENSORIOCSPOLLRATE: {
|
||||||
|
switch (arg) {
|
||||||
|
|
||||||
|
/* switching to manual polling */
|
||||||
|
case SENSOR_POLLRATE_MANUAL:
|
||||||
|
stop();
|
||||||
|
_measure_ticks = 0;
|
||||||
|
return OK;
|
||||||
|
|
||||||
|
/* external signalling (DRDY) not supported */
|
||||||
|
case SENSOR_POLLRATE_EXTERNAL:
|
||||||
|
|
||||||
|
/* zero would be bad */
|
||||||
|
case 0:
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* set default/max polling rate */
|
||||||
|
case SENSOR_POLLRATE_MAX:
|
||||||
|
case SENSOR_POLLRATE_DEFAULT: {
|
||||||
|
/* do we need to start internal polling? */
|
||||||
|
bool want_start = (_measure_ticks == 0);
|
||||||
|
|
||||||
|
/* set interval for next measurement to minimum legal value */
|
||||||
|
_measure_ticks = USEC2TICK(TRONE_CONVERSION_INTERVAL);
|
||||||
|
|
||||||
|
/* if we need to start the poll state machine, do it */
|
||||||
|
if (want_start) {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* adjust to a legal polling interval in Hz */
|
||||||
|
default: {
|
||||||
|
/* do we need to start internal polling? */
|
||||||
|
bool want_start = (_measure_ticks == 0);
|
||||||
|
|
||||||
|
/* convert hz to tick interval via microseconds */
|
||||||
|
unsigned ticks = USEC2TICK(1000000 / arg);
|
||||||
|
|
||||||
|
/* check against maximum rate */
|
||||||
|
if (ticks < USEC2TICK(TRONE_CONVERSION_INTERVAL)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update interval for next measurement */
|
||||||
|
_measure_ticks = ticks;
|
||||||
|
|
||||||
|
/* if we need to start the poll state machine, do it */
|
||||||
|
if (want_start) {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case SENSORIOCGPOLLRATE:
|
||||||
|
if (_measure_ticks == 0) {
|
||||||
|
return SENSOR_POLLRATE_MANUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1000 / _measure_ticks);
|
||||||
|
|
||||||
|
case SENSORIOCSQUEUEDEPTH: {
|
||||||
|
/* lower bound is mandatory, upper bound is a sanity check */
|
||||||
|
if ((arg < 1) || (arg > 100)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
irqstate_t flags = irqsave();
|
||||||
|
|
||||||
|
if (!_reports->resize(arg)) {
|
||||||
|
irqrestore(flags);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
irqrestore(flags);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SENSORIOCGQUEUEDEPTH:
|
||||||
|
return _reports->size();
|
||||||
|
|
||||||
|
case SENSORIOCRESET:
|
||||||
|
/* XXX implement this */
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
case RANGEFINDERIOCSETMINIUMDISTANCE: {
|
||||||
|
set_minimum_distance(*(float *)arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RANGEFINDERIOCSETMAXIUMDISTANCE: {
|
||||||
|
set_maximum_distance(*(float *)arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* give it to the superclass */
|
||||||
|
return I2C::ioctl(filp, cmd, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
TRONE::read(struct file *filp, char *buffer, size_t buflen)
|
||||||
|
{
|
||||||
|
unsigned count = buflen / sizeof(struct range_finder_report);
|
||||||
|
struct range_finder_report *rbuf = reinterpret_cast<struct range_finder_report *>(buffer);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* buffer must be large enough */
|
||||||
|
if (count < 1) {
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if automatic measurement is enabled */
|
||||||
|
if (_measure_ticks > 0) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* While there is space in the caller's buffer, and reports, copy them.
|
||||||
|
* Note that we may be pre-empted by the workq thread while we are doing this;
|
||||||
|
* we are careful to avoid racing with them.
|
||||||
|
*/
|
||||||
|
while (count--) {
|
||||||
|
if (_reports->get(rbuf)) {
|
||||||
|
ret += sizeof(*rbuf);
|
||||||
|
rbuf++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if there was no data, warn the caller */
|
||||||
|
return ret ? ret : -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* manual measurement - run one conversion */
|
||||||
|
do {
|
||||||
|
_reports->flush();
|
||||||
|
|
||||||
|
/* trigger a measurement */
|
||||||
|
if (OK != measure()) {
|
||||||
|
ret = -EIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait for it to complete */
|
||||||
|
usleep(TRONE_CONVERSION_INTERVAL);
|
||||||
|
|
||||||
|
/* run the collection phase */
|
||||||
|
if (OK != collect()) {
|
||||||
|
ret = -EIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* state machine will have generated a report, copy it out */
|
||||||
|
if (_reports->get(rbuf)) {
|
||||||
|
ret = sizeof(*rbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TRONE::measure()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send the command to begin a measurement.
|
||||||
|
*/
|
||||||
|
const uint8_t cmd = TRONE_MEASURE_REG;
|
||||||
|
ret = transfer(&cmd, sizeof(cmd), nullptr, 0);
|
||||||
|
|
||||||
|
if (OK != ret) {
|
||||||
|
perf_count(_comms_errors);
|
||||||
|
log("i2c::transfer returned %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = OK;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TRONE::collect()
|
||||||
|
{
|
||||||
|
int ret = -EIO;
|
||||||
|
|
||||||
|
/* read from the sensor */
|
||||||
|
uint8_t val[3] = {0, 0, 0};
|
||||||
|
|
||||||
|
perf_begin(_sample_perf);
|
||||||
|
|
||||||
|
ret = transfer(nullptr, 0, &val[0], 3);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
log("error reading from sensor: %d", ret);
|
||||||
|
perf_count(_comms_errors);
|
||||||
|
perf_end(_sample_perf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t distance = (val[0] << 8) | val[1];
|
||||||
|
float si_units = distance * 0.001f; /* mm to m */
|
||||||
|
struct range_finder_report report;
|
||||||
|
|
||||||
|
/* this should be fairly close to the end of the measurement, so the best approximation of the time */
|
||||||
|
report.timestamp = hrt_absolute_time();
|
||||||
|
report.error_count = perf_event_count(_comms_errors);
|
||||||
|
report.distance = si_units;
|
||||||
|
report.valid = crc8(val, 2) == val[2] && si_units > get_minimum_distance() && si_units < get_maximum_distance() ? 1 : 0;
|
||||||
|
|
||||||
|
|
||||||
|
/* publish it, if we are the primary */
|
||||||
|
if (_range_finder_topic >= 0) {
|
||||||
|
orb_publish(ORB_ID(sensor_range_finder), _range_finder_topic, &report);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_reports->force(&report)) {
|
||||||
|
perf_count(_buffer_overflows);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* notify anyone waiting for data */
|
||||||
|
poll_notify(POLLIN);
|
||||||
|
|
||||||
|
ret = OK;
|
||||||
|
|
||||||
|
perf_end(_sample_perf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TRONE::start()
|
||||||
|
{
|
||||||
|
/* reset the report ring and state machine */
|
||||||
|
_collect_phase = false;
|
||||||
|
_reports->flush();
|
||||||
|
|
||||||
|
/* schedule a cycle to start things */
|
||||||
|
work_queue(HPWORK, &_work, (worker_t)&TRONE::cycle_trampoline, this, 1);
|
||||||
|
|
||||||
|
/* notify about state change */
|
||||||
|
struct subsystem_info_s info = {
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
SUBSYSTEM_TYPE_RANGEFINDER
|
||||||
|
};
|
||||||
|
static orb_advert_t pub = -1;
|
||||||
|
|
||||||
|
if (pub > 0) {
|
||||||
|
orb_publish(ORB_ID(subsystem_info), pub, &info);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pub = orb_advertise(ORB_ID(subsystem_info), &info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TRONE::stop()
|
||||||
|
{
|
||||||
|
work_cancel(HPWORK, &_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TRONE::cycle_trampoline(void *arg)
|
||||||
|
{
|
||||||
|
TRONE *dev = (TRONE *)arg;
|
||||||
|
|
||||||
|
dev->cycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TRONE::cycle()
|
||||||
|
{
|
||||||
|
/* collection phase? */
|
||||||
|
if (_collect_phase) {
|
||||||
|
|
||||||
|
/* perform collection */
|
||||||
|
if (OK != collect()) {
|
||||||
|
log("collection error");
|
||||||
|
/* restart the measurement state machine */
|
||||||
|
start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* next phase is measurement */
|
||||||
|
_collect_phase = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is there a collect->measure gap?
|
||||||
|
*/
|
||||||
|
if (_measure_ticks > USEC2TICK(TRONE_CONVERSION_INTERVAL)) {
|
||||||
|
|
||||||
|
/* schedule a fresh cycle call when we are ready to measure again */
|
||||||
|
work_queue(HPWORK,
|
||||||
|
&_work,
|
||||||
|
(worker_t)&TRONE::cycle_trampoline,
|
||||||
|
this,
|
||||||
|
_measure_ticks - USEC2TICK(TRONE_CONVERSION_INTERVAL));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* measurement phase */
|
||||||
|
if (OK != measure()) {
|
||||||
|
log("measure error");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* next phase is collection */
|
||||||
|
_collect_phase = true;
|
||||||
|
|
||||||
|
/* schedule a fresh cycle call when the measurement is done */
|
||||||
|
work_queue(HPWORK,
|
||||||
|
&_work,
|
||||||
|
(worker_t)&TRONE::cycle_trampoline,
|
||||||
|
this,
|
||||||
|
USEC2TICK(TRONE_CONVERSION_INTERVAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TRONE::print_info()
|
||||||
|
{
|
||||||
|
perf_print_counter(_sample_perf);
|
||||||
|
perf_print_counter(_comms_errors);
|
||||||
|
perf_print_counter(_buffer_overflows);
|
||||||
|
printf("poll interval: %u ticks\n", _measure_ticks);
|
||||||
|
_reports->print_info("report queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local functions in support of the shell command.
|
||||||
|
*/
|
||||||
|
namespace trone
|
||||||
|
{
|
||||||
|
|
||||||
|
/* oddly, ERROR is not defined for c++ */
|
||||||
|
#ifdef ERROR
|
||||||
|
# undef ERROR
|
||||||
|
#endif
|
||||||
|
const int ERROR = -1;
|
||||||
|
|
||||||
|
TRONE *g_dev;
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
void test();
|
||||||
|
void reset();
|
||||||
|
void info();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the driver.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (g_dev != nullptr) {
|
||||||
|
errx(1, "already started");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create the driver */
|
||||||
|
g_dev = new TRONE(TRONE_BUS);
|
||||||
|
|
||||||
|
|
||||||
|
if (g_dev == nullptr) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OK != g_dev->init()) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the poll rate to default, starts automatic data collection */
|
||||||
|
fd = open(TRONE_DEVICE_PATH, O_RDONLY);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
|
||||||
|
if (g_dev != nullptr) {
|
||||||
|
delete g_dev;
|
||||||
|
g_dev = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
errx(1, "driver start failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the driver
|
||||||
|
*/
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
if (g_dev != nullptr) {
|
||||||
|
delete g_dev;
|
||||||
|
g_dev = nullptr;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
errx(1, "driver not running");
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform some basic functional tests on the driver;
|
||||||
|
* make sure we can collect data from the sensor in polled
|
||||||
|
* and automatic modes.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
test()
|
||||||
|
{
|
||||||
|
struct range_finder_report report;
|
||||||
|
ssize_t sz;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
int fd = open(TRONE_DEVICE_PATH, O_RDONLY);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
err(1, "%s open failed (try 'trone start' if the driver is not running", TRONE_DEVICE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do a simple demand read */
|
||||||
|
sz = read(fd, &report, sizeof(report));
|
||||||
|
|
||||||
|
if (sz != sizeof(report)) {
|
||||||
|
err(1, "immediate read failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
warnx("single read");
|
||||||
|
warnx("measurement: %0.2f m", (double)report.distance);
|
||||||
|
warnx("time: %lld", report.timestamp);
|
||||||
|
|
||||||
|
/* start the sensor polling at 2Hz */
|
||||||
|
if (OK != ioctl(fd, SENSORIOCSPOLLRATE, 2)) {
|
||||||
|
errx(1, "failed to set 2Hz poll rate");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read the sensor 50x and report each value */
|
||||||
|
for (unsigned i = 0; i < 50; i++) {
|
||||||
|
struct pollfd fds;
|
||||||
|
|
||||||
|
/* wait for data to be ready */
|
||||||
|
fds.fd = fd;
|
||||||
|
fds.events = POLLIN;
|
||||||
|
ret = poll(&fds, 1, 2000);
|
||||||
|
|
||||||
|
if (ret != 1) {
|
||||||
|
errx(1, "timed out waiting for sensor data");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now go get it */
|
||||||
|
sz = read(fd, &report, sizeof(report));
|
||||||
|
|
||||||
|
if (sz != sizeof(report)) {
|
||||||
|
err(1, "periodic read failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
warnx("periodic read %u", i);
|
||||||
|
warnx("valid %u", report.valid);
|
||||||
|
warnx("measurement: %0.3f", (double)report.distance);
|
||||||
|
warnx("time: %lld", report.timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset the sensor polling to default rate */
|
||||||
|
if (OK != ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT)) {
|
||||||
|
errx(1, "failed to set default poll rate");
|
||||||
|
}
|
||||||
|
|
||||||
|
errx(0, "PASS");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the driver.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
reset()
|
||||||
|
{
|
||||||
|
int fd = open(TRONE_DEVICE_PATH, O_RDONLY);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
err(1, "failed ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, SENSORIOCRESET, 0) < 0) {
|
||||||
|
err(1, "driver reset failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT) < 0) {
|
||||||
|
err(1, "driver poll restart failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a little info about the driver.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
info()
|
||||||
|
{
|
||||||
|
if (g_dev == nullptr) {
|
||||||
|
errx(1, "driver not running");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("state @ %p\n", g_dev);
|
||||||
|
g_dev->print_info();
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int
|
||||||
|
trone_main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Start/load the driver.
|
||||||
|
*/
|
||||||
|
if (!strcmp(argv[1], "start")) {
|
||||||
|
trone::start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop the driver
|
||||||
|
*/
|
||||||
|
if (!strcmp(argv[1], "stop")) {
|
||||||
|
trone::stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test the driver/device.
|
||||||
|
*/
|
||||||
|
if (!strcmp(argv[1], "test")) {
|
||||||
|
trone::test();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset the driver.
|
||||||
|
*/
|
||||||
|
if (!strcmp(argv[1], "reset")) {
|
||||||
|
trone::reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print driver information.
|
||||||
|
*/
|
||||||
|
if (!strcmp(argv[1], "info") || !strcmp(argv[1], "status")) {
|
||||||
|
trone::info();
|
||||||
|
}
|
||||||
|
|
||||||
|
errx(1, "unrecognized command, try 'start', 'test', 'reset' or 'info'");
|
||||||
|
}
|
||||||
@@ -308,8 +308,8 @@ int flow_position_estimator_thread_main(int argc, char *argv[])
|
|||||||
if (vehicle_liftoff || params.debug)
|
if (vehicle_liftoff || params.debug)
|
||||||
{
|
{
|
||||||
/* copy flow */
|
/* copy flow */
|
||||||
flow_speed[0] = flow.flow_comp_x_m;
|
flow_speed[0] = flow.pixel_flow_x_integral / (flow.integration_timespan / 1e6f) * flow.ground_distance_m;
|
||||||
flow_speed[1] = flow.flow_comp_y_m;
|
flow_speed[1] = flow.pixel_flow_y_integral / (flow.integration_timespan / 1e6f) * flow.ground_distance_m;
|
||||||
flow_speed[2] = 0.0f;
|
flow_speed[2] = 0.0f;
|
||||||
|
|
||||||
/* convert to bodyframe velocity */
|
/* convert to bodyframe velocity */
|
||||||
|
|||||||
@@ -206,10 +206,6 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds
|
|||||||
0, 0, 1.f
|
0, 0, 1.f
|
||||||
}; /**< init: identity matrix */
|
}; /**< init: identity matrix */
|
||||||
|
|
||||||
// print text
|
|
||||||
printf("Extended Kalman Filter Attitude Estimator initialized..\n\n");
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
int overloadcounter = 19;
|
int overloadcounter = 19;
|
||||||
|
|
||||||
/* Initialize filter */
|
/* Initialize filter */
|
||||||
|
|||||||
@@ -310,12 +310,16 @@ int commander_main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(argv[1], "arm")) {
|
if (!strcmp(argv[1], "arm")) {
|
||||||
arm_disarm(true, mavlink_fd, "command line");
|
int mavlink_fd_local = open(MAVLINK_LOG_DEVICE, 0);
|
||||||
|
arm_disarm(true, mavlink_fd_local, "command line");
|
||||||
|
close(mavlink_fd_local);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(argv[1], "disarm")) {
|
if (!strcmp(argv[1], "disarm")) {
|
||||||
arm_disarm(false, mavlink_fd, "command line");
|
int mavlink_fd_local = open(MAVLINK_LOG_DEVICE, 0);
|
||||||
|
arm_disarm(false, mavlink_fd_local, "command line");
|
||||||
|
close(mavlink_fd_local);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1544,7 +1548,7 @@ int commander_thread_main(int argc, char *argv[])
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (status.rc_signal_lost) {
|
if (status.rc_signal_lost) {
|
||||||
mavlink_log_critical(mavlink_fd, "RC signal regained");
|
mavlink_log_critical(mavlink_fd, "RC SIGNAL REGAINED after %llums",(hrt_absolute_time()-status.rc_signal_lost_timestamp)/1000);
|
||||||
status_changed = true;
|
status_changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1645,8 +1649,9 @@ int commander_thread_main(int argc, char *argv[])
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!status.rc_signal_lost) {
|
if (!status.rc_signal_lost) {
|
||||||
mavlink_log_critical(mavlink_fd, "RC SIGNAL LOST");
|
mavlink_log_critical(mavlink_fd, "RC SIGNAL LOST (at t=%llums)",hrt_absolute_time()/1000);
|
||||||
status.rc_signal_lost = true;
|
status.rc_signal_lost = true;
|
||||||
|
status.rc_signal_lost_timestamp=sp_man.timestamp;
|
||||||
status_changed = true;
|
status_changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -706,7 +706,7 @@ int prearm_check(const struct vehicle_status_s *status, const int mavlink_fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fabsf(airspeed.indicated_airspeed_m_s > 6.0f)) {
|
if (fabsf(airspeed.indicated_airspeed_m_s > 6.0f)) {
|
||||||
mavlink_log_critical(mavlink_fd, "AIRSPEED WARNING: WIND OR CALIBRATION MISSING");
|
mavlink_log_critical(mavlink_fd, "AIRSPEED WARNING: WIND OR CALIBRATION ISSUE");
|
||||||
// XXX do not make this fatal yet
|
// XXX do not make this fatal yet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -629,9 +629,6 @@ task_main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
work_q_item_t *work;
|
work_q_item_t *work;
|
||||||
|
|
||||||
/* inform about start */
|
|
||||||
warnx("Initializing..");
|
|
||||||
|
|
||||||
/* Initialize global variables */
|
/* Initialize global variables */
|
||||||
g_key_offsets[0] = 0;
|
g_key_offsets[0] = 0;
|
||||||
|
|
||||||
@@ -694,16 +691,15 @@ task_main(int argc, char *argv[])
|
|||||||
if (sys_restart_val == DM_INIT_REASON_POWER_ON) {
|
if (sys_restart_val == DM_INIT_REASON_POWER_ON) {
|
||||||
warnx("Power on restart");
|
warnx("Power on restart");
|
||||||
_restart(DM_INIT_REASON_POWER_ON);
|
_restart(DM_INIT_REASON_POWER_ON);
|
||||||
}
|
} else if (sys_restart_val == DM_INIT_REASON_IN_FLIGHT) {
|
||||||
else if (sys_restart_val == DM_INIT_REASON_IN_FLIGHT) {
|
|
||||||
warnx("In flight restart");
|
warnx("In flight restart");
|
||||||
_restart(DM_INIT_REASON_IN_FLIGHT);
|
_restart(DM_INIT_REASON_IN_FLIGHT);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
warnx("Unknown restart");
|
warnx("Unknown restart");
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
warnx("Unknown restart");
|
warnx("Unknown restart");
|
||||||
|
}
|
||||||
|
|
||||||
/* We use two file descriptors, one for the caller context and one for the worker thread */
|
/* We use two file descriptors, one for the caller context and one for the worker thread */
|
||||||
/* They are actually the same but we need to some way to reject caller request while the */
|
/* They are actually the same but we need to some way to reject caller request while the */
|
||||||
|
|||||||
@@ -1405,7 +1405,7 @@ Mavlink::task_main(int argc, char *argv[])
|
|||||||
configure_stream("POSITION_TARGET_GLOBAL_INT", 3.0f);
|
configure_stream("POSITION_TARGET_GLOBAL_INT", 3.0f);
|
||||||
configure_stream("ATTITUDE_TARGET", 3.0f);
|
configure_stream("ATTITUDE_TARGET", 3.0f);
|
||||||
configure_stream("DISTANCE_SENSOR", 0.5f);
|
configure_stream("DISTANCE_SENSOR", 0.5f);
|
||||||
configure_stream("OPTICAL_FLOW", 5.0f);
|
configure_stream("OPTICAL_FLOW_RAD", 5.0f);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MAVLINK_MODE_ONBOARD:
|
case MAVLINK_MODE_ONBOARD:
|
||||||
|
|||||||
@@ -1834,33 +1834,32 @@ protected:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MavlinkStreamOpticalFlowRad : public MavlinkStream
|
||||||
class MavlinkStreamOpticalFlow : public MavlinkStream
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const char *get_name() const
|
const char *get_name() const
|
||||||
{
|
{
|
||||||
return MavlinkStreamOpticalFlow::get_name_static();
|
return MavlinkStreamOpticalFlowRad::get_name_static();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *get_name_static()
|
static const char *get_name_static()
|
||||||
{
|
{
|
||||||
return "OPTICAL_FLOW";
|
return "OPTICAL_FLOW_RAD";
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t get_id()
|
uint8_t get_id()
|
||||||
{
|
{
|
||||||
return MAVLINK_MSG_ID_OPTICAL_FLOW;
|
return MAVLINK_MSG_ID_OPTICAL_FLOW_RAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MavlinkStream *new_instance(Mavlink *mavlink)
|
static MavlinkStream *new_instance(Mavlink *mavlink)
|
||||||
{
|
{
|
||||||
return new MavlinkStreamOpticalFlow(mavlink);
|
return new MavlinkStreamOpticalFlowRad(mavlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned get_size()
|
unsigned get_size()
|
||||||
{
|
{
|
||||||
return _flow_sub->is_published() ? (MAVLINK_MSG_ID_OPTICAL_FLOW_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES) : 0;
|
return _flow_sub->is_published() ? (MAVLINK_MSG_ID_OPTICAL_FLOW_RAD_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1868,11 +1867,11 @@ private:
|
|||||||
uint64_t _flow_time;
|
uint64_t _flow_time;
|
||||||
|
|
||||||
/* do not allow top copying this class */
|
/* do not allow top copying this class */
|
||||||
MavlinkStreamOpticalFlow(MavlinkStreamOpticalFlow &);
|
MavlinkStreamOpticalFlowRad(MavlinkStreamOpticalFlowRad &);
|
||||||
MavlinkStreamOpticalFlow& operator = (const MavlinkStreamOpticalFlow &);
|
MavlinkStreamOpticalFlowRad& operator = (const MavlinkStreamOpticalFlowRad &);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit MavlinkStreamOpticalFlow(Mavlink *mavlink) : MavlinkStream(mavlink),
|
explicit MavlinkStreamOpticalFlowRad(Mavlink *mavlink) : MavlinkStream(mavlink),
|
||||||
_flow_sub(_mavlink->add_orb_subscription(ORB_ID(optical_flow))),
|
_flow_sub(_mavlink->add_orb_subscription(ORB_ID(optical_flow))),
|
||||||
_flow_time(0)
|
_flow_time(0)
|
||||||
{}
|
{}
|
||||||
@@ -1882,18 +1881,23 @@ protected:
|
|||||||
struct optical_flow_s flow;
|
struct optical_flow_s flow;
|
||||||
|
|
||||||
if (_flow_sub->update(&_flow_time, &flow)) {
|
if (_flow_sub->update(&_flow_time, &flow)) {
|
||||||
mavlink_optical_flow_t msg;
|
mavlink_optical_flow_rad_t msg;
|
||||||
|
|
||||||
msg.time_usec = flow.timestamp;
|
msg.time_usec = flow.timestamp;
|
||||||
msg.sensor_id = flow.sensor_id;
|
msg.sensor_id = flow.sensor_id;
|
||||||
msg.flow_x = flow.flow_raw_x;
|
msg.integrated_x = flow.pixel_flow_x_integral;
|
||||||
msg.flow_y = flow.flow_raw_y;
|
msg.integrated_y = flow.pixel_flow_y_integral;
|
||||||
msg.flow_comp_m_x = flow.flow_comp_x_m;
|
msg.integrated_xgyro = flow.gyro_x_rate_integral;
|
||||||
msg.flow_comp_m_y = flow.flow_comp_y_m;
|
msg.integrated_ygyro = flow.gyro_y_rate_integral;
|
||||||
|
msg.integrated_zgyro = flow.gyro_z_rate_integral;
|
||||||
|
msg.distance = flow.ground_distance_m;
|
||||||
msg.quality = flow.quality;
|
msg.quality = flow.quality;
|
||||||
msg.ground_distance = flow.ground_distance_m;
|
msg.integration_time_us = flow.integration_timespan;
|
||||||
|
msg.sensor_id = flow.sensor_id;
|
||||||
|
msg.time_delta_distance_us = flow.time_since_last_sonar_update;
|
||||||
|
msg.temperature = flow.gyro_temperature;
|
||||||
|
|
||||||
_mavlink->send_message(MAVLINK_MSG_ID_OPTICAL_FLOW, &msg);
|
_mavlink->send_message(MAVLINK_MSG_ID_OPTICAL_FLOW_RAD, &msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2199,7 +2203,7 @@ StreamListItem *streams_list[] = {
|
|||||||
new StreamListItem(&MavlinkStreamAttitudeTarget::new_instance, &MavlinkStreamAttitudeTarget::get_name_static),
|
new StreamListItem(&MavlinkStreamAttitudeTarget::new_instance, &MavlinkStreamAttitudeTarget::get_name_static),
|
||||||
new StreamListItem(&MavlinkStreamRCChannelsRaw::new_instance, &MavlinkStreamRCChannelsRaw::get_name_static),
|
new StreamListItem(&MavlinkStreamRCChannelsRaw::new_instance, &MavlinkStreamRCChannelsRaw::get_name_static),
|
||||||
new StreamListItem(&MavlinkStreamManualControl::new_instance, &MavlinkStreamManualControl::get_name_static),
|
new StreamListItem(&MavlinkStreamManualControl::new_instance, &MavlinkStreamManualControl::get_name_static),
|
||||||
new StreamListItem(&MavlinkStreamOpticalFlow::new_instance, &MavlinkStreamOpticalFlow::get_name_static),
|
new StreamListItem(&MavlinkStreamOpticalFlowRad::new_instance, &MavlinkStreamOpticalFlowRad::get_name_static),
|
||||||
new StreamListItem(&MavlinkStreamAttitudeControls::new_instance, &MavlinkStreamAttitudeControls::get_name_static),
|
new StreamListItem(&MavlinkStreamAttitudeControls::new_instance, &MavlinkStreamAttitudeControls::get_name_static),
|
||||||
new StreamListItem(&MavlinkStreamNamedValueFloat::new_instance, &MavlinkStreamNamedValueFloat::get_name_static),
|
new StreamListItem(&MavlinkStreamNamedValueFloat::new_instance, &MavlinkStreamNamedValueFloat::get_name_static),
|
||||||
new StreamListItem(&MavlinkStreamCameraCapture::new_instance, &MavlinkStreamCameraCapture::get_name_static),
|
new StreamListItem(&MavlinkStreamCameraCapture::new_instance, &MavlinkStreamCameraCapture::get_name_static),
|
||||||
|
|||||||
@@ -144,8 +144,8 @@ MavlinkReceiver::handle_message(mavlink_message_t *msg)
|
|||||||
handle_message_command_int(msg);
|
handle_message_command_int(msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MAVLINK_MSG_ID_OPTICAL_FLOW:
|
case MAVLINK_MSG_ID_OPTICAL_FLOW_RAD:
|
||||||
handle_message_optical_flow(msg);
|
handle_message_optical_flow_rad(msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MAVLINK_MSG_ID_SET_MODE:
|
case MAVLINK_MSG_ID_SET_MODE:
|
||||||
@@ -352,24 +352,27 @@ MavlinkReceiver::handle_message_command_int(mavlink_message_t *msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MavlinkReceiver::handle_message_optical_flow(mavlink_message_t *msg)
|
MavlinkReceiver::handle_message_optical_flow_rad(mavlink_message_t *msg)
|
||||||
{
|
{
|
||||||
/* optical flow */
|
/* optical flow */
|
||||||
mavlink_optical_flow_t flow;
|
mavlink_optical_flow_rad_t flow;
|
||||||
mavlink_msg_optical_flow_decode(msg, &flow);
|
mavlink_msg_optical_flow_rad_decode(msg, &flow);
|
||||||
|
|
||||||
struct optical_flow_s f;
|
struct optical_flow_s f;
|
||||||
memset(&f, 0, sizeof(f));
|
memset(&f, 0, sizeof(f));
|
||||||
|
|
||||||
f.timestamp = hrt_absolute_time();
|
f.timestamp = flow.time_usec;
|
||||||
f.flow_timestamp = flow.time_usec;
|
f.integration_timespan = flow.integration_time_us;
|
||||||
f.flow_raw_x = flow.flow_x;
|
f.pixel_flow_x_integral = flow.integrated_x;
|
||||||
f.flow_raw_y = flow.flow_y;
|
f.pixel_flow_y_integral = flow.integrated_y;
|
||||||
f.flow_comp_x_m = flow.flow_comp_m_x;
|
f.gyro_x_rate_integral = flow.integrated_xgyro;
|
||||||
f.flow_comp_y_m = flow.flow_comp_m_y;
|
f.gyro_y_rate_integral = flow.integrated_ygyro;
|
||||||
f.ground_distance_m = flow.ground_distance;
|
f.gyro_z_rate_integral = flow.integrated_zgyro;
|
||||||
|
f.time_since_last_sonar_update = flow.time_delta_distance_us;
|
||||||
|
f.ground_distance_m = flow.distance;
|
||||||
f.quality = flow.quality;
|
f.quality = flow.quality;
|
||||||
f.sensor_id = flow.sensor_id;
|
f.sensor_id = flow.sensor_id;
|
||||||
|
f.gyro_temperature = flow.temperature;
|
||||||
|
|
||||||
if (_flow_pub < 0) {
|
if (_flow_pub < 0) {
|
||||||
_flow_pub = orb_advertise(ORB_ID(optical_flow), &f);
|
_flow_pub = orb_advertise(ORB_ID(optical_flow), &f);
|
||||||
@@ -389,13 +392,18 @@ MavlinkReceiver::handle_message_hil_optical_flow(mavlink_message_t *msg)
|
|||||||
struct optical_flow_s f;
|
struct optical_flow_s f;
|
||||||
memset(&f, 0, sizeof(f));
|
memset(&f, 0, sizeof(f));
|
||||||
|
|
||||||
f.timestamp = hrt_absolute_time();
|
f.timestamp = hrt_absolute_time(); // XXX we rely on the system time for now and not flow.time_usec;
|
||||||
f.flow_timestamp = flow.time_usec;
|
f.integration_timespan = flow.integration_time_us;
|
||||||
f.flow_raw_x = flow.integrated_x;
|
f.pixel_flow_x_integral = flow.integrated_x;
|
||||||
f.flow_raw_y = flow.integrated_y;
|
f.pixel_flow_y_integral = flow.integrated_y;
|
||||||
|
f.gyro_x_rate_integral = flow.integrated_xgyro;
|
||||||
|
f.gyro_y_rate_integral = flow.integrated_ygyro;
|
||||||
|
f.gyro_z_rate_integral = flow.integrated_zgyro;
|
||||||
|
f.time_since_last_sonar_update = flow.time_delta_distance_us;
|
||||||
f.ground_distance_m = flow.distance;
|
f.ground_distance_m = flow.distance;
|
||||||
f.quality = flow.quality;
|
f.quality = flow.quality;
|
||||||
f.sensor_id = flow.sensor_id;
|
f.sensor_id = flow.sensor_id;
|
||||||
|
f.gyro_temperature = flow.temperature;
|
||||||
|
|
||||||
if (_flow_pub < 0) {
|
if (_flow_pub < 0) {
|
||||||
_flow_pub = orb_advertise(ORB_ID(optical_flow), &f);
|
_flow_pub = orb_advertise(ORB_ID(optical_flow), &f);
|
||||||
@@ -538,12 +546,16 @@ MavlinkReceiver::handle_message_set_position_target_local_ned(mavlink_message_t
|
|||||||
offboard_control_sp.ignore &= ~(1 << i);
|
offboard_control_sp.ignore &= ~(1 << i);
|
||||||
offboard_control_sp.ignore |= (set_position_target_local_ned.type_mask & (1 << i));
|
offboard_control_sp.ignore |= (set_position_target_local_ned.type_mask & (1 << i));
|
||||||
}
|
}
|
||||||
|
|
||||||
offboard_control_sp.ignore &= ~(1 << OFB_IGN_BIT_YAW);
|
offboard_control_sp.ignore &= ~(1 << OFB_IGN_BIT_YAW);
|
||||||
offboard_control_sp.ignore |= (set_position_target_local_ned.type_mask & (1 << 10)) <<
|
if (set_position_target_local_ned.type_mask & (1 << 10)) {
|
||||||
OFB_IGN_BIT_YAW;
|
offboard_control_sp.ignore |= (1 << OFB_IGN_BIT_YAW);
|
||||||
|
}
|
||||||
|
|
||||||
offboard_control_sp.ignore &= ~(1 << OFB_IGN_BIT_YAWRATE);
|
offboard_control_sp.ignore &= ~(1 << OFB_IGN_BIT_YAWRATE);
|
||||||
offboard_control_sp.ignore |= (set_position_target_local_ned.type_mask & (1 << 11)) <<
|
if (set_position_target_local_ned.type_mask & (1 << 11)) {
|
||||||
OFB_IGN_BIT_YAWRATE;
|
offboard_control_sp.ignore |= (1 << OFB_IGN_BIT_YAWRATE);
|
||||||
|
}
|
||||||
|
|
||||||
offboard_control_sp.timestamp = hrt_absolute_time();
|
offboard_control_sp.timestamp = hrt_absolute_time();
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ private:
|
|||||||
void handle_message(mavlink_message_t *msg);
|
void handle_message(mavlink_message_t *msg);
|
||||||
void handle_message_command_long(mavlink_message_t *msg);
|
void handle_message_command_long(mavlink_message_t *msg);
|
||||||
void handle_message_command_int(mavlink_message_t *msg);
|
void handle_message_command_int(mavlink_message_t *msg);
|
||||||
void handle_message_optical_flow(mavlink_message_t *msg);
|
void handle_message_optical_flow_rad(mavlink_message_t *msg);
|
||||||
void handle_message_hil_optical_flow(mavlink_message_t *msg);
|
void handle_message_hil_optical_flow(mavlink_message_t *msg);
|
||||||
void handle_message_set_mode(mavlink_message_t *msg);
|
void handle_message_set_mode(mavlink_message_t *msg);
|
||||||
void handle_message_vicon_position_estimate(mavlink_message_t *msg);
|
void handle_message_vicon_position_estimate(mavlink_message_t *msg);
|
||||||
|
|||||||
@@ -721,8 +721,6 @@ MulticopterAttitudeControl::task_main_trampoline(int argc, char *argv[])
|
|||||||
void
|
void
|
||||||
MulticopterAttitudeControl::task_main()
|
MulticopterAttitudeControl::task_main()
|
||||||
{
|
{
|
||||||
warnx("started");
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* do subscriptions
|
* do subscriptions
|
||||||
|
|||||||
@@ -535,7 +535,7 @@ MulticopterPositionControl::reset_pos_sp()
|
|||||||
- _params.vel_ff(0) * _sp_move_rate(0)) / _params.pos_p(0);
|
- _params.vel_ff(0) * _sp_move_rate(0)) / _params.pos_p(0);
|
||||||
_pos_sp(1) = _pos(1) + (_vel(1) - _att_sp.R_body[1][2] * _att_sp.thrust / _params.vel_p(1)
|
_pos_sp(1) = _pos(1) + (_vel(1) - _att_sp.R_body[1][2] * _att_sp.thrust / _params.vel_p(1)
|
||||||
- _params.vel_ff(1) * _sp_move_rate(1)) / _params.pos_p(1);
|
- _params.vel_ff(1) * _sp_move_rate(1)) / _params.pos_p(1);
|
||||||
mavlink_log_info(_mavlink_fd, "[mpc] reset pos sp: %.2f, %.2f", (double)_pos_sp(0), (double)_pos_sp(1));
|
mavlink_log_info(_mavlink_fd, "[mpc] reset pos sp: %d, %d", (int)_pos_sp(0), (int)_pos_sp(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +545,7 @@ MulticopterPositionControl::reset_alt_sp()
|
|||||||
if (_reset_alt_sp) {
|
if (_reset_alt_sp) {
|
||||||
_reset_alt_sp = false;
|
_reset_alt_sp = false;
|
||||||
_pos_sp(2) = _pos(2) + (_vel(2) - _params.vel_ff(2) * _sp_move_rate(2)) / _params.pos_p(2);
|
_pos_sp(2) = _pos(2) + (_vel(2) - _params.vel_ff(2) * _sp_move_rate(2)) / _params.pos_p(2);
|
||||||
mavlink_log_info(_mavlink_fd, "[mpc] reset alt sp: %.2f", -(double)_pos_sp(2));
|
mavlink_log_info(_mavlink_fd, "[mpc] reset alt sp: %d", -(int)_pos_sp(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -857,10 +857,8 @@ MulticopterPositionControl::control_auto(float dt)
|
|||||||
void
|
void
|
||||||
MulticopterPositionControl::task_main()
|
MulticopterPositionControl::task_main()
|
||||||
{
|
{
|
||||||
warnx("started");
|
|
||||||
|
|
||||||
_mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
|
_mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
|
||||||
mavlink_log_info(_mavlink_fd, "[mpc] started");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* do subscriptions
|
* do subscriptions
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ private:
|
|||||||
|
|
||||||
bool _can_loiter_at_sp; /**< flags if current position SP can be used to loiter */
|
bool _can_loiter_at_sp; /**< flags if current position SP can be used to loiter */
|
||||||
bool _pos_sp_triplet_updated; /**< flags if position SP triplet needs to be published */
|
bool _pos_sp_triplet_updated; /**< flags if position SP triplet needs to be published */
|
||||||
|
bool _pos_sp_triplet_published_invalid_once; /**< flags if position SP triplet has been published once to UORB */
|
||||||
|
|
||||||
control::BlockParamFloat _param_loiter_radius; /**< loiter radius for fixedwing */
|
control::BlockParamFloat _param_loiter_radius; /**< loiter radius for fixedwing */
|
||||||
control::BlockParamFloat _param_acceptance_radius; /**< acceptance for takeoff */
|
control::BlockParamFloat _param_acceptance_radius; /**< acceptance for takeoff */
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ Navigator::Navigator() :
|
|||||||
_gpsFailure(this, "GPSF"),
|
_gpsFailure(this, "GPSF"),
|
||||||
_can_loiter_at_sp(false),
|
_can_loiter_at_sp(false),
|
||||||
_pos_sp_triplet_updated(false),
|
_pos_sp_triplet_updated(false),
|
||||||
|
_pos_sp_triplet_published_invalid_once(false),
|
||||||
_param_loiter_radius(this, "LOITER_RAD"),
|
_param_loiter_radius(this, "LOITER_RAD"),
|
||||||
_param_acceptance_radius(this, "ACC_RAD"),
|
_param_acceptance_radius(this, "ACC_RAD"),
|
||||||
_param_datalinkloss_obc(this, "DLL_OBC"),
|
_param_datalinkloss_obc(this, "DLL_OBC"),
|
||||||
@@ -427,12 +428,15 @@ Navigator::task_main()
|
|||||||
_can_loiter_at_sp = false;
|
_can_loiter_at_sp = false;
|
||||||
break;
|
break;
|
||||||
case NAVIGATION_STATE_AUTO_MISSION:
|
case NAVIGATION_STATE_AUTO_MISSION:
|
||||||
|
_pos_sp_triplet_published_invalid_once = false;
|
||||||
_navigation_mode = &_mission;
|
_navigation_mode = &_mission;
|
||||||
break;
|
break;
|
||||||
case NAVIGATION_STATE_AUTO_LOITER:
|
case NAVIGATION_STATE_AUTO_LOITER:
|
||||||
|
_pos_sp_triplet_published_invalid_once = false;
|
||||||
_navigation_mode = &_loiter;
|
_navigation_mode = &_loiter;
|
||||||
break;
|
break;
|
||||||
case NAVIGATION_STATE_AUTO_RCRECOVER:
|
case NAVIGATION_STATE_AUTO_RCRECOVER:
|
||||||
|
_pos_sp_triplet_published_invalid_once = false;
|
||||||
if (_param_rcloss_obc.get() != 0) {
|
if (_param_rcloss_obc.get() != 0) {
|
||||||
_navigation_mode = &_rcLoss;
|
_navigation_mode = &_rcLoss;
|
||||||
} else {
|
} else {
|
||||||
@@ -440,11 +444,13 @@ Navigator::task_main()
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NAVIGATION_STATE_AUTO_RTL:
|
case NAVIGATION_STATE_AUTO_RTL:
|
||||||
_navigation_mode = &_rtl;
|
_pos_sp_triplet_published_invalid_once = false;
|
||||||
|
_navigation_mode = &_rtl;
|
||||||
break;
|
break;
|
||||||
case NAVIGATION_STATE_AUTO_RTGS:
|
case NAVIGATION_STATE_AUTO_RTGS:
|
||||||
/* Use complex data link loss mode only when enabled via param
|
/* Use complex data link loss mode only when enabled via param
|
||||||
* otherwise use rtl */
|
* otherwise use rtl */
|
||||||
|
_pos_sp_triplet_published_invalid_once = false;
|
||||||
if (_param_datalinkloss_obc.get() != 0) {
|
if (_param_datalinkloss_obc.get() != 0) {
|
||||||
_navigation_mode = &_dataLinkLoss;
|
_navigation_mode = &_dataLinkLoss;
|
||||||
} else {
|
} else {
|
||||||
@@ -452,9 +458,11 @@ Navigator::task_main()
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NAVIGATION_STATE_AUTO_LANDENGFAIL:
|
case NAVIGATION_STATE_AUTO_LANDENGFAIL:
|
||||||
|
_pos_sp_triplet_published_invalid_once = false;
|
||||||
_navigation_mode = &_engineFailure;
|
_navigation_mode = &_engineFailure;
|
||||||
break;
|
break;
|
||||||
case NAVIGATION_STATE_AUTO_LANDGPSFAIL:
|
case NAVIGATION_STATE_AUTO_LANDGPSFAIL:
|
||||||
|
_pos_sp_triplet_published_invalid_once = false;
|
||||||
_navigation_mode = &_gpsFailure;
|
_navigation_mode = &_gpsFailure;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -468,9 +476,9 @@ Navigator::task_main()
|
|||||||
_navigation_mode_array[i]->run(_navigation_mode == _navigation_mode_array[i]);
|
_navigation_mode_array[i]->run(_navigation_mode == _navigation_mode_array[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if nothing is running, set position setpoint triplet invalid */
|
/* if nothing is running, set position setpoint triplet invalid once */
|
||||||
if (_navigation_mode == nullptr) {
|
if (_navigation_mode == nullptr && !_pos_sp_triplet_published_invalid_once) {
|
||||||
// TODO publish empty sp only once
|
_pos_sp_triplet_published_invalid_once = true;
|
||||||
_pos_sp_triplet.previous.valid = false;
|
_pos_sp_triplet.previous.valid = false;
|
||||||
_pos_sp_triplet.current.valid = false;
|
_pos_sp_triplet.current.valid = false;
|
||||||
_pos_sp_triplet.next.valid = false;
|
_pos_sp_triplet.next.valid = false;
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ int position_estimator_inav_main(int argc, char *argv[])
|
|||||||
thread_should_exit = true;
|
thread_should_exit = true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
warnx("app not started");
|
warnx("not started");
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -169,10 +169,10 @@ int position_estimator_inav_main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (!strcmp(argv[1], "status")) {
|
if (!strcmp(argv[1], "status")) {
|
||||||
if (thread_running) {
|
if (thread_running) {
|
||||||
warnx("app is running");
|
warnx("is running");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
warnx("app not started");
|
warnx("not started");
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -210,10 +210,8 @@ static void write_debug_log(const char *msg, float dt, float x_est[2], float y_e
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
int position_estimator_inav_thread_main(int argc, char *argv[])
|
int position_estimator_inav_thread_main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
warnx("started");
|
|
||||||
int mavlink_fd;
|
int mavlink_fd;
|
||||||
mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
|
mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
|
||||||
mavlink_log_info(mavlink_fd, "[inav] started");
|
|
||||||
|
|
||||||
float x_est[2] = { 0.0f, 0.0f }; // pos, vel
|
float x_est[2] = { 0.0f, 0.0f }; // pos, vel
|
||||||
float y_est[2] = { 0.0f, 0.0f }; // pos, vel
|
float y_est[2] = { 0.0f, 0.0f }; // pos, vel
|
||||||
@@ -298,7 +296,7 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
|
|||||||
float w_flow = 0.0f;
|
float w_flow = 0.0f;
|
||||||
|
|
||||||
float sonar_prev = 0.0f;
|
float sonar_prev = 0.0f;
|
||||||
hrt_abstime flow_prev = 0; // time of last flow measurement
|
//hrt_abstime flow_prev = 0; // time of last flow measurement
|
||||||
hrt_abstime sonar_time = 0; // time of last sonar measurement (not filtered)
|
hrt_abstime sonar_time = 0; // time of last sonar measurement (not filtered)
|
||||||
hrt_abstime sonar_valid_time = 0; // time of last sonar measurement used for correction (filtered)
|
hrt_abstime sonar_valid_time = 0; // time of last sonar measurement used for correction (filtered)
|
||||||
|
|
||||||
@@ -389,8 +387,8 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
|
|||||||
} else {
|
} else {
|
||||||
wait_baro = false;
|
wait_baro = false;
|
||||||
baro_offset /= (float) baro_init_cnt;
|
baro_offset /= (float) baro_init_cnt;
|
||||||
warnx("baro offs: %.2f", (double)baro_offset);
|
warnx("baro offs: %d", (int)baro_offset);
|
||||||
mavlink_log_info(mavlink_fd, "[inav] baro offs: %.2f", (double)baro_offset);
|
mavlink_log_info(mavlink_fd, "[inav] baro offs: %d", (int)baro_offset);
|
||||||
local_pos.z_valid = true;
|
local_pos.z_valid = true;
|
||||||
local_pos.v_z_valid = true;
|
local_pos.v_z_valid = true;
|
||||||
}
|
}
|
||||||
@@ -491,8 +489,8 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
|
|||||||
orb_copy(ORB_ID(optical_flow), optical_flow_sub, &flow);
|
orb_copy(ORB_ID(optical_flow), optical_flow_sub, &flow);
|
||||||
|
|
||||||
/* calculate time from previous update */
|
/* calculate time from previous update */
|
||||||
float flow_dt = flow_prev > 0 ? (flow.flow_timestamp - flow_prev) * 1e-6f : 0.1f;
|
// float flow_dt = flow_prev > 0 ? (flow.flow_timestamp - flow_prev) * 1e-6f : 0.1f;
|
||||||
flow_prev = flow.flow_timestamp;
|
// flow_prev = flow.flow_timestamp;
|
||||||
|
|
||||||
if ((flow.ground_distance_m > 0.31f) &&
|
if ((flow.ground_distance_m > 0.31f) &&
|
||||||
(flow.ground_distance_m < 4.0f) &&
|
(flow.ground_distance_m < 4.0f) &&
|
||||||
@@ -520,7 +518,7 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
|
|||||||
sonar_valid_time = t;
|
sonar_valid_time = t;
|
||||||
sonar_valid = true;
|
sonar_valid = true;
|
||||||
local_pos.surface_bottom_timestamp = t;
|
local_pos.surface_bottom_timestamp = t;
|
||||||
mavlink_log_info(mavlink_fd, "[inav] new surface level: %.2f", (double)surface_offset);
|
mavlink_log_info(mavlink_fd, "[inav] new surface level: %d", (int)surface_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -550,8 +548,9 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
|
|||||||
|
|
||||||
/* convert raw flow to angular flow (rad/s) */
|
/* convert raw flow to angular flow (rad/s) */
|
||||||
float flow_ang[2];
|
float flow_ang[2];
|
||||||
flow_ang[0] = flow.flow_raw_x * params.flow_k / 1000.0f / flow_dt;
|
//todo check direction of x und y axis
|
||||||
flow_ang[1] = flow.flow_raw_y * params.flow_k / 1000.0f / flow_dt;
|
flow_ang[0] = flow.pixel_flow_x_integral/(float)flow.integration_timespan*1000000.0f;//flow.flow_raw_x * params.flow_k / 1000.0f / flow_dt;
|
||||||
|
flow_ang[1] = flow.pixel_flow_y_integral/(float)flow.integration_timespan*1000000.0f;//flow.flow_raw_y * params.flow_k / 1000.0f / flow_dt;
|
||||||
/* flow measurements vector */
|
/* flow measurements vector */
|
||||||
float flow_m[3];
|
float flow_m[3];
|
||||||
flow_m[0] = -flow_ang[0] * flow_dist;
|
flow_m[0] = -flow_ang[0] * flow_dist;
|
||||||
@@ -721,8 +720,9 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
|
|||||||
|
|
||||||
/* initialize projection */
|
/* initialize projection */
|
||||||
map_projection_init(&ref, lat, lon);
|
map_projection_init(&ref, lat, lon);
|
||||||
warnx("init ref: lat=%.7f, lon=%.7f, alt=%.2f", (double)lat, (double)lon, (double)alt);
|
// XXX replace this print
|
||||||
mavlink_log_info(mavlink_fd, "[inav] init ref: %.7f, %.7f, %.2f", (double)lat, (double)lon, (double)alt);
|
warnx("init ref: lat=%.7f, lon=%.7f, alt=%8.4f", (double)lat, (double)lon, (double)alt);
|
||||||
|
mavlink_log_info(mavlink_fd, "[inav] init ref: %.7f, %.7f, %8.4f", (double)lat, (double)lon, (double)alt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -353,12 +353,16 @@ static unsigned mixer_text_length = 0;
|
|||||||
int
|
int
|
||||||
mixer_handle_text(const void *buffer, size_t length)
|
mixer_handle_text(const void *buffer, size_t length)
|
||||||
{
|
{
|
||||||
/* do not allow a mixer change while safety off */
|
/* do not allow a mixer change while safety off and FMU armed */
|
||||||
if ((r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF)) {
|
if ((r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF) &&
|
||||||
|
(r_setup_arming & PX4IO_P_SETUP_ARMING_FMU_ARMED)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* abort if we're in the mixer */
|
/* disable mixing, will be enabled once load is complete */
|
||||||
|
r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_MIXER_OK);
|
||||||
|
|
||||||
|
/* abort if we're in the mixer - the caller is expected to retry */
|
||||||
if (in_mixer) {
|
if (in_mixer) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -367,17 +371,16 @@ mixer_handle_text(const void *buffer, size_t length)
|
|||||||
|
|
||||||
isr_debug(2, "mix txt %u", length);
|
isr_debug(2, "mix txt %u", length);
|
||||||
|
|
||||||
if (length < sizeof(px4io_mixdata))
|
if (length < sizeof(px4io_mixdata)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned text_length = length - sizeof(px4io_mixdata);
|
unsigned text_length = length - sizeof(px4io_mixdata);
|
||||||
|
|
||||||
switch (msg->action) {
|
switch (msg->action) {
|
||||||
case F2I_MIXER_ACTION_RESET:
|
case F2I_MIXER_ACTION_RESET:
|
||||||
isr_debug(2, "reset");
|
isr_debug(2, "reset");
|
||||||
|
|
||||||
/* FIRST mark the mixer as invalid */
|
|
||||||
r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK;
|
|
||||||
/* THEN actually delete it */
|
/* THEN actually delete it */
|
||||||
mixer_group.reset();
|
mixer_group.reset();
|
||||||
mixer_text_length = 0;
|
mixer_text_length = 0;
|
||||||
@@ -386,9 +389,6 @@ mixer_handle_text(const void *buffer, size_t length)
|
|||||||
case F2I_MIXER_ACTION_APPEND:
|
case F2I_MIXER_ACTION_APPEND:
|
||||||
isr_debug(2, "append %d", length);
|
isr_debug(2, "append %d", length);
|
||||||
|
|
||||||
/* disable mixing during the update */
|
|
||||||
r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK;
|
|
||||||
|
|
||||||
/* check for overflow - this would be really fatal */
|
/* check for overflow - this would be really fatal */
|
||||||
if ((mixer_text_length + text_length + 1) > sizeof(mixer_text)) {
|
if ((mixer_text_length + text_length + 1) > sizeof(mixer_text)) {
|
||||||
r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK;
|
r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK;
|
||||||
|
|||||||
@@ -407,11 +407,11 @@ registers_set(uint8_t page, uint8_t offset, const uint16_t *values, unsigned num
|
|||||||
|
|
||||||
/* handle text going to the mixer parser */
|
/* handle text going to the mixer parser */
|
||||||
case PX4IO_PAGE_MIXERLOAD:
|
case PX4IO_PAGE_MIXERLOAD:
|
||||||
if (!(r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF) ||
|
/* do not change the mixer if FMU is armed and IO's safety is off
|
||||||
(r_status_flags & PX4IO_P_STATUS_FLAGS_OUTPUTS_ARMED)) {
|
* this state defines an active system. This check is done in the
|
||||||
return mixer_handle_text(values, num_values * sizeof(*values));
|
* text handling function.
|
||||||
}
|
*/
|
||||||
break;
|
return mixer_handle_text(values, num_values * sizeof(*values));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* avoid offset wrap */
|
/* avoid offset wrap */
|
||||||
@@ -583,8 +583,7 @@ registers_set_one(uint8_t page, uint8_t offset, uint16_t value)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PX4IO_P_SETUP_REBOOT_BL:
|
case PX4IO_P_SETUP_REBOOT_BL:
|
||||||
if ((r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF) ||
|
if (r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF) {
|
||||||
(r_status_flags & PX4IO_P_STATUS_FLAGS_OUTPUTS_ARMED)) {
|
|
||||||
// don't allow reboot while armed
|
// don't allow reboot while armed
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -630,10 +629,9 @@ registers_set_one(uint8_t page, uint8_t offset, uint16_t value)
|
|||||||
case PX4IO_PAGE_RC_CONFIG: {
|
case PX4IO_PAGE_RC_CONFIG: {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do not allow a RC config change while outputs armed
|
* do not allow a RC config change while safety is off
|
||||||
*/
|
*/
|
||||||
if ((r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF) ||
|
if (r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF) {
|
||||||
(r_status_flags & PX4IO_P_STATUS_FLAGS_OUTPUTS_ARMED)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -496,6 +496,8 @@ static void *logwriter_thread(void *arg)
|
|||||||
/* set name */
|
/* set name */
|
||||||
prctl(PR_SET_NAME, "sdlog2_writer", 0);
|
prctl(PR_SET_NAME, "sdlog2_writer", 0);
|
||||||
|
|
||||||
|
perf_counter_t perf_write = perf_alloc(PC_ELAPSED, "sd write");
|
||||||
|
|
||||||
int log_fd = open_log_file();
|
int log_fd = open_log_file();
|
||||||
|
|
||||||
if (log_fd < 0) {
|
if (log_fd < 0) {
|
||||||
@@ -553,7 +555,9 @@ static void *logwriter_thread(void *arg)
|
|||||||
n = available;
|
n = available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
perf_begin(perf_write);
|
||||||
n = write(log_fd, read_ptr, n);
|
n = write(log_fd, read_ptr, n);
|
||||||
|
perf_end(perf_write);
|
||||||
|
|
||||||
should_wait = (n == available) && !is_part;
|
should_wait = (n == available) && !is_part;
|
||||||
|
|
||||||
@@ -586,6 +590,9 @@ static void *logwriter_thread(void *arg)
|
|||||||
fsync(log_fd);
|
fsync(log_fd);
|
||||||
close(log_fd);
|
close(log_fd);
|
||||||
|
|
||||||
|
/* free performance counter */
|
||||||
|
perf_free(perf_write);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1511,11 +1518,14 @@ int sdlog2_thread_main(int argc, char *argv[])
|
|||||||
/* --- FLOW --- */
|
/* --- FLOW --- */
|
||||||
if (copy_if_updated(ORB_ID(optical_flow), subs.flow_sub, &buf.flow)) {
|
if (copy_if_updated(ORB_ID(optical_flow), subs.flow_sub, &buf.flow)) {
|
||||||
log_msg.msg_type = LOG_FLOW_MSG;
|
log_msg.msg_type = LOG_FLOW_MSG;
|
||||||
log_msg.body.log_FLOW.flow_raw_x = buf.flow.flow_raw_x;
|
log_msg.body.log_FLOW.ground_distance_m = buf.flow.ground_distance_m;
|
||||||
log_msg.body.log_FLOW.flow_raw_y = buf.flow.flow_raw_y;
|
log_msg.body.log_FLOW.gyro_temperature = buf.flow.gyro_temperature;
|
||||||
log_msg.body.log_FLOW.flow_comp_x = buf.flow.flow_comp_x_m;
|
log_msg.body.log_FLOW.gyro_x_rate_integral = buf.flow.gyro_x_rate_integral;
|
||||||
log_msg.body.log_FLOW.flow_comp_y = buf.flow.flow_comp_y_m;
|
log_msg.body.log_FLOW.gyro_y_rate_integral = buf.flow.gyro_y_rate_integral;
|
||||||
log_msg.body.log_FLOW.distance = buf.flow.ground_distance_m;
|
log_msg.body.log_FLOW.gyro_z_rate_integral = buf.flow.gyro_z_rate_integral;
|
||||||
|
log_msg.body.log_FLOW.integration_timespan = buf.flow.integration_timespan;
|
||||||
|
log_msg.body.log_FLOW.pixel_flow_x_integral = buf.flow.pixel_flow_x_integral;
|
||||||
|
log_msg.body.log_FLOW.pixel_flow_y_integral = buf.flow.pixel_flow_y_integral;
|
||||||
log_msg.body.log_FLOW.quality = buf.flow.quality;
|
log_msg.body.log_FLOW.quality = buf.flow.quality;
|
||||||
log_msg.body.log_FLOW.sensor_id = buf.flow.sensor_id;
|
log_msg.body.log_FLOW.sensor_id = buf.flow.sensor_id;
|
||||||
LOGBUFFER_WRITE_AND_COUNT(FLOW);
|
LOGBUFFER_WRITE_AND_COUNT(FLOW);
|
||||||
|
|||||||
@@ -200,13 +200,19 @@ struct log_ARSP_s {
|
|||||||
/* --- FLOW - OPTICAL FLOW --- */
|
/* --- FLOW - OPTICAL FLOW --- */
|
||||||
#define LOG_FLOW_MSG 15
|
#define LOG_FLOW_MSG 15
|
||||||
struct log_FLOW_s {
|
struct log_FLOW_s {
|
||||||
int16_t flow_raw_x;
|
uint64_t timestamp;
|
||||||
int16_t flow_raw_y;
|
|
||||||
float flow_comp_x;
|
|
||||||
float flow_comp_y;
|
|
||||||
float distance;
|
|
||||||
uint8_t quality;
|
|
||||||
uint8_t sensor_id;
|
uint8_t sensor_id;
|
||||||
|
float pixel_flow_x_integral;
|
||||||
|
float pixel_flow_y_integral;
|
||||||
|
float gyro_x_rate_integral;
|
||||||
|
float gyro_y_rate_integral;
|
||||||
|
float gyro_z_rate_integral;
|
||||||
|
float ground_distance_m;
|
||||||
|
uint32_t integration_timespan;
|
||||||
|
uint32_t time_since_last_sonar_update;
|
||||||
|
uint16_t frame_count_since_last_readout;
|
||||||
|
int16_t gyro_temperature;
|
||||||
|
uint8_t quality;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* --- GPOS - GLOBAL POSITION ESTIMATE --- */
|
/* --- GPOS - GLOBAL POSITION ESTIMATE --- */
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
#include "topics/vehicle_attitude_setpoint.h"
|
#include "topics/vehicle_attitude_setpoint.h"
|
||||||
#include "topics/vehicle_rates_setpoint.h"
|
#include "topics/vehicle_rates_setpoint.h"
|
||||||
#include "topics/actuator_outputs.h"
|
#include "topics/actuator_outputs.h"
|
||||||
|
#include "topics/actuator_direct.h"
|
||||||
#include "topics/encoders.h"
|
#include "topics/encoders.h"
|
||||||
#include "topics/tecs_status.h"
|
#include "topics/tecs_status.h"
|
||||||
|
|
||||||
@@ -76,6 +77,7 @@ template class __EXPORT Publication<vehicle_global_velocity_setpoint_s>;
|
|||||||
template class __EXPORT Publication<vehicle_attitude_setpoint_s>;
|
template class __EXPORT Publication<vehicle_attitude_setpoint_s>;
|
||||||
template class __EXPORT Publication<vehicle_rates_setpoint_s>;
|
template class __EXPORT Publication<vehicle_rates_setpoint_s>;
|
||||||
template class __EXPORT Publication<actuator_outputs_s>;
|
template class __EXPORT Publication<actuator_outputs_s>;
|
||||||
|
template class __EXPORT Publication<actuator_direct_s>;
|
||||||
template class __EXPORT Publication<encoders_s>;
|
template class __EXPORT Publication<encoders_s>;
|
||||||
template class __EXPORT Publication<tecs_status_s>;
|
template class __EXPORT Publication<tecs_status_s>;
|
||||||
|
|
||||||
|
|||||||
@@ -192,6 +192,9 @@ ORB_DEFINE(actuator_outputs_1, struct actuator_outputs_s);
|
|||||||
ORB_DEFINE(actuator_outputs_2, struct actuator_outputs_s);
|
ORB_DEFINE(actuator_outputs_2, struct actuator_outputs_s);
|
||||||
ORB_DEFINE(actuator_outputs_3, struct actuator_outputs_s);
|
ORB_DEFINE(actuator_outputs_3, struct actuator_outputs_s);
|
||||||
|
|
||||||
|
#include "topics/actuator_direct.h"
|
||||||
|
ORB_DEFINE(actuator_direct, struct actuator_direct_s);
|
||||||
|
|
||||||
#include "topics/multirotor_motor_limits.h"
|
#include "topics/multirotor_motor_limits.h"
|
||||||
ORB_DEFINE(multirotor_motor_limits, struct multirotor_motor_limits_s);
|
ORB_DEFINE(multirotor_motor_limits, struct multirotor_motor_limits_s);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 PX4 Development Team. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||||
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file actuator_direct.h
|
||||||
|
*
|
||||||
|
* Actuator direct values.
|
||||||
|
*
|
||||||
|
* Values published to this topic are the direct actuator values which
|
||||||
|
* should be passed to actuators, bypassing mixing
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TOPIC_ACTUATOR_DIRECT_H
|
||||||
|
#define TOPIC_ACTUATOR_DIRECT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../uORB.h"
|
||||||
|
|
||||||
|
#define NUM_ACTUATORS_DIRECT 16
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup topics
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct actuator_direct_s {
|
||||||
|
uint64_t timestamp; /**< timestamp in us since system boot */
|
||||||
|
float values[NUM_ACTUATORS_DIRECT]; /**< actuator values, from -1 to 1 */
|
||||||
|
unsigned nvalues; /**< number of valid values */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* actuator direct ORB */
|
||||||
|
ORB_DECLARE(actuator_direct);
|
||||||
|
|
||||||
|
#endif // TOPIC_ACTUATOR_DIRECT_H
|
||||||
@@ -55,16 +55,22 @@
|
|||||||
*/
|
*/
|
||||||
struct optical_flow_s {
|
struct optical_flow_s {
|
||||||
|
|
||||||
uint64_t timestamp; /**< in microseconds since system start */
|
uint64_t timestamp; /**< in microseconds since system start */
|
||||||
|
|
||||||
uint64_t flow_timestamp; /**< timestamp from flow sensor */
|
|
||||||
int16_t flow_raw_x; /**< flow in pixels in X direction, not rotation-compensated */
|
|
||||||
int16_t flow_raw_y; /**< flow in pixels in Y direction, not rotation-compensated */
|
|
||||||
float flow_comp_x_m; /**< speed over ground in meters, rotation-compensated */
|
|
||||||
float flow_comp_y_m; /**< speed over ground in meters, rotation-compensated */
|
|
||||||
float ground_distance_m; /**< Altitude / distance to ground in meters */
|
|
||||||
uint8_t quality; /**< Quality of the measurement, 0: bad quality, 255: maximum quality */
|
|
||||||
uint8_t sensor_id; /**< id of the sensor emitting the flow value */
|
uint8_t sensor_id; /**< id of the sensor emitting the flow value */
|
||||||
|
float pixel_flow_x_integral; /**< accumulated optical flow in radians around x axis */
|
||||||
|
float pixel_flow_y_integral; /**< accumulated optical flow in radians around y axis */
|
||||||
|
float gyro_x_rate_integral; /**< accumulated gyro value in radians around x axis */
|
||||||
|
float gyro_y_rate_integral; /**< accumulated gyro value in radians around y axis */
|
||||||
|
float gyro_z_rate_integral; /**< accumulated gyro value in radians around z axis */
|
||||||
|
float ground_distance_m; /**< Altitude / distance to ground in meters */
|
||||||
|
uint32_t integration_timespan; /**<accumulation timespan in microseconds */
|
||||||
|
uint32_t time_since_last_sonar_update;/**< time since last sonar update in microseconds */
|
||||||
|
uint16_t frame_count_since_last_readout;/**< number of accumulated frames in timespan */
|
||||||
|
int16_t gyro_temperature;/**< Temperature * 100 in centi-degrees Celsius */
|
||||||
|
uint8_t quality; /**< Average of quality of accumulated frames, 0: bad quality, 255: maximum quality */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ struct vehicle_status_s {
|
|||||||
|
|
||||||
bool rc_signal_found_once;
|
bool rc_signal_found_once;
|
||||||
bool rc_signal_lost; /**< true if RC reception lost */
|
bool rc_signal_lost; /**< true if RC reception lost */
|
||||||
|
uint64_t rc_signal_lost_timestamp; /**< Time at which the RC reception was lost */
|
||||||
bool rc_signal_lost_cmd; /**< true if RC lost mode is commanded */
|
bool rc_signal_lost_cmd; /**< true if RC lost mode is commanded */
|
||||||
bool rc_input_blocked; /**< set if RC input should be ignored */
|
bool rc_input_blocked; /**< set if RC input should be ignored */
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ int UavcanEscController::init()
|
|||||||
|
|
||||||
void UavcanEscController::update_outputs(float *outputs, unsigned num_outputs)
|
void UavcanEscController::update_outputs(float *outputs, unsigned num_outputs)
|
||||||
{
|
{
|
||||||
if ((outputs == nullptr) || (num_outputs > uavcan::equipment::esc::RawCommand::FieldTypes::cmd::MaxSize)) {
|
if ((outputs == nullptr) ||
|
||||||
|
(num_outputs > uavcan::equipment::esc::RawCommand::FieldTypes::cmd::MaxSize) ||
|
||||||
|
(num_outputs > CONNECTED_ESC_MAX)) {
|
||||||
perf_count(_perfcnt_invalid_input);
|
perf_count(_perfcnt_invalid_input);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,10 +103,15 @@ void UavcanEscController::update_outputs(float *outputs, unsigned num_outputs)
|
|||||||
for (unsigned i = 0; i < num_outputs; i++) {
|
for (unsigned i = 0; i < num_outputs; i++) {
|
||||||
if (_armed_mask & MOTOR_BIT(i)) {
|
if (_armed_mask & MOTOR_BIT(i)) {
|
||||||
float scaled = (outputs[i] + 1.0F) * 0.5F * cmd_max;
|
float scaled = (outputs[i] + 1.0F) * 0.5F * cmd_max;
|
||||||
if (scaled < 1.0F) {
|
// trim negative values back to 0. Previously
|
||||||
scaled = 1.0F; // Since we're armed, we don't want to stop it completely
|
// we set this to 0.1, which meant motors kept
|
||||||
}
|
// spinning when armed, but that should be a
|
||||||
|
// policy decision for a specific vehicle
|
||||||
|
// type, as it is not appropriate for all
|
||||||
|
// types of vehicles (eg. fixed wing).
|
||||||
|
if (scaled < 0.0F) {
|
||||||
|
scaled = 0.0F;
|
||||||
|
}
|
||||||
if (scaled > cmd_max) {
|
if (scaled > cmd_max) {
|
||||||
scaled = cmd_max;
|
scaled = cmd_max;
|
||||||
perf_count(_perfcnt_scaling_error);
|
perf_count(_perfcnt_scaling_error);
|
||||||
|
|||||||
@@ -91,11 +91,7 @@ void UavcanBarometerBridge::air_data_sub_cb(const uavcan::ReceivedDataStructure<
|
|||||||
{
|
{
|
||||||
auto report = ::baro_report();
|
auto report = ::baro_report();
|
||||||
|
|
||||||
report.timestamp = msg.getUtcTimestamp().toUSec();
|
report.timestamp = msg.getMonotonicTimestamp().toUSec();
|
||||||
if (report.timestamp == 0) {
|
|
||||||
report.timestamp = msg.getMonotonicTimestamp().toUSec();
|
|
||||||
}
|
|
||||||
|
|
||||||
report.temperature = msg.static_temperature;
|
report.temperature = msg.static_temperature;
|
||||||
report.pressure = msg.static_pressure / 100.0F; // Convert to millibar
|
report.pressure = msg.static_pressure / 100.0F; // Convert to millibar
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void UavcanGnssBridge::gnss_fix_sub_cb(const uavcan::ReceivedDataStructure<uavca
|
|||||||
|
|
||||||
auto report = ::vehicle_gps_position_s();
|
auto report = ::vehicle_gps_position_s();
|
||||||
|
|
||||||
report.timestamp_position = hrt_absolute_time();
|
report.timestamp_position = msg.getMonotonicTimestamp().toUSec();
|
||||||
report.lat = msg.latitude_deg_1e8 / 10;
|
report.lat = msg.latitude_deg_1e8 / 10;
|
||||||
report.lon = msg.longitude_deg_1e8 / 10;
|
report.lon = msg.longitude_deg_1e8 / 10;
|
||||||
report.alt = msg.height_msl_mm;
|
report.alt = msg.height_msl_mm;
|
||||||
|
|||||||
@@ -43,8 +43,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <drivers/drv_hrt.h>
|
|
||||||
|
|
||||||
#include <uORB/uORB.h>
|
#include <uORB/uORB.h>
|
||||||
#include <uORB/topics/vehicle_gps_position.h>
|
#include <uORB/topics/vehicle_gps_position.h>
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@
|
|||||||
|
|
||||||
#include "mag.hpp"
|
#include "mag.hpp"
|
||||||
|
|
||||||
|
#include <systemlib/err.h>
|
||||||
|
|
||||||
static const orb_id_t MAG_TOPICS[3] = {
|
static const orb_id_t MAG_TOPICS[3] = {
|
||||||
ORB_ID(sensor_mag0),
|
ORB_ID(sensor_mag0),
|
||||||
ORB_ID(sensor_mag1),
|
ORB_ID(sensor_mag1),
|
||||||
@@ -71,9 +73,36 @@ int UavcanMagnetometerBridge::init()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t UavcanMagnetometerBridge::read(struct file *filp, char *buffer, size_t buflen)
|
||||||
|
{
|
||||||
|
static uint64_t last_read = 0;
|
||||||
|
struct mag_report *mag_buf = reinterpret_cast<struct mag_report *>(buffer);
|
||||||
|
|
||||||
|
/* buffer must be large enough */
|
||||||
|
unsigned count = buflen / sizeof(struct mag_report);
|
||||||
|
if (count < 1) {
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_read < _report.timestamp) {
|
||||||
|
/* copy report */
|
||||||
|
lock();
|
||||||
|
*mag_buf = _report;
|
||||||
|
last_read = _report.timestamp;
|
||||||
|
unlock();
|
||||||
|
return sizeof(struct mag_report);
|
||||||
|
} else {
|
||||||
|
/* no new data available, warn caller */
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int UavcanMagnetometerBridge::ioctl(struct file *filp, int cmd, unsigned long arg)
|
int UavcanMagnetometerBridge::ioctl(struct file *filp, int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
case SENSORIOCSQUEUEDEPTH: {
|
||||||
|
return OK; // Pretend that this stuff is supported to keep APM happy
|
||||||
|
}
|
||||||
case MAGIOCSSCALE: {
|
case MAGIOCSSCALE: {
|
||||||
std::memcpy(&_scale, reinterpret_cast<const void*>(arg), sizeof(_scale));
|
std::memcpy(&_scale, reinterpret_cast<const void*>(arg), sizeof(_scale));
|
||||||
return 0;
|
return 0;
|
||||||
@@ -86,7 +115,7 @@ int UavcanMagnetometerBridge::ioctl(struct file *filp, int cmd, unsigned long ar
|
|||||||
return 0; // Nothing to do
|
return 0; // Nothing to do
|
||||||
}
|
}
|
||||||
case MAGIOCGEXTERNAL: {
|
case MAGIOCGEXTERNAL: {
|
||||||
return 0; // We don't want anyone to transform the coordinate frame, so we declare it onboard
|
return 1; // declare it external rise it's priority and to allow for correct orientation compensation
|
||||||
}
|
}
|
||||||
case MAGIOCSSAMPLERATE: {
|
case MAGIOCSSAMPLERATE: {
|
||||||
return 0; // Pretend that this stuff is supported to keep the sensor app happy
|
return 0; // Pretend that this stuff is supported to keep the sensor app happy
|
||||||
@@ -108,18 +137,14 @@ int UavcanMagnetometerBridge::ioctl(struct file *filp, int cmd, unsigned long ar
|
|||||||
|
|
||||||
void UavcanMagnetometerBridge::mag_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::Magnetometer> &msg)
|
void UavcanMagnetometerBridge::mag_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::Magnetometer> &msg)
|
||||||
{
|
{
|
||||||
auto report = ::mag_report();
|
lock();
|
||||||
|
_report.range_ga = 1.3F; // Arbitrary number, doesn't really mean anything
|
||||||
|
_report.timestamp = msg.getMonotonicTimestamp().toUSec();
|
||||||
|
|
||||||
report.range_ga = 1.3F; // Arbitrary number, doesn't really mean anything
|
_report.x = (msg.magnetic_field[0] - _scale.x_offset) * _scale.x_scale;
|
||||||
|
_report.y = (msg.magnetic_field[1] - _scale.y_offset) * _scale.y_scale;
|
||||||
|
_report.z = (msg.magnetic_field[2] - _scale.z_offset) * _scale.z_scale;
|
||||||
|
unlock();
|
||||||
|
|
||||||
report.timestamp = msg.getUtcTimestamp().toUSec();
|
publish(msg.getSrcNodeID().get(), &_report);
|
||||||
if (report.timestamp == 0) {
|
|
||||||
report.timestamp = msg.getMonotonicTimestamp().toUSec();
|
|
||||||
}
|
|
||||||
|
|
||||||
report.x = (msg.magnetic_field[0] - _scale.x_offset) * _scale.x_scale;
|
|
||||||
report.y = (msg.magnetic_field[1] - _scale.y_offset) * _scale.y_scale;
|
|
||||||
report.z = (msg.magnetic_field[2] - _scale.z_offset) * _scale.z_scale;
|
|
||||||
|
|
||||||
publish(msg.getSrcNodeID().get(), &report);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ public:
|
|||||||
int init() override;
|
int init() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ssize_t read(struct file *filp, char *buffer, size_t buflen);
|
||||||
int ioctl(struct file *filp, int cmd, unsigned long arg) override;
|
int ioctl(struct file *filp, int cmd, unsigned long arg) override;
|
||||||
|
|
||||||
void mag_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::Magnetometer> &msg);
|
void mag_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::Magnetometer> &msg);
|
||||||
@@ -65,4 +66,5 @@ private:
|
|||||||
|
|
||||||
uavcan::Subscriber<uavcan::equipment::ahrs::Magnetometer, MagCbBinder> _sub_mag;
|
uavcan::Subscriber<uavcan::equipment::ahrs::Magnetometer, MagCbBinder> _sub_mag;
|
||||||
mag_scale _scale = {};
|
mag_scale _scale = {};
|
||||||
|
mag_report _report = {};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -269,6 +269,24 @@ void UavcanNode::node_spin_once()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
add a fd to the list of polled events. This assumes you want
|
||||||
|
POLLIN for now.
|
||||||
|
*/
|
||||||
|
int UavcanNode::add_poll_fd(int fd)
|
||||||
|
{
|
||||||
|
int ret = _poll_fds_num;
|
||||||
|
if (_poll_fds_num >= UAVCAN_NUM_POLL_FDS) {
|
||||||
|
errx(1, "uavcan: too many poll fds, exiting");
|
||||||
|
}
|
||||||
|
_poll_fds[_poll_fds_num] = ::pollfd();
|
||||||
|
_poll_fds[_poll_fds_num].fd = fd;
|
||||||
|
_poll_fds[_poll_fds_num].events = POLLIN;
|
||||||
|
_poll_fds_num += 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int UavcanNode::run()
|
int UavcanNode::run()
|
||||||
{
|
{
|
||||||
(void)pthread_mutex_lock(&_node_mutex);
|
(void)pthread_mutex_lock(&_node_mutex);
|
||||||
@@ -280,9 +298,9 @@ int UavcanNode::run()
|
|||||||
|
|
||||||
_armed_sub = orb_subscribe(ORB_ID(actuator_armed));
|
_armed_sub = orb_subscribe(ORB_ID(actuator_armed));
|
||||||
_test_motor_sub = orb_subscribe(ORB_ID(test_motor));
|
_test_motor_sub = orb_subscribe(ORB_ID(test_motor));
|
||||||
|
_actuator_direct_sub = orb_subscribe(ORB_ID(actuator_direct));
|
||||||
|
|
||||||
actuator_outputs_s outputs;
|
memset(&_outputs, 0, sizeof(_outputs));
|
||||||
memset(&outputs, 0, sizeof(outputs));
|
|
||||||
|
|
||||||
const int busevent_fd = ::open(uavcan_stm32::BusEvent::DevName, 0);
|
const int busevent_fd = ::open(uavcan_stm32::BusEvent::DevName, 0);
|
||||||
if (busevent_fd < 0)
|
if (busevent_fd < 0)
|
||||||
@@ -304,11 +322,15 @@ int UavcanNode::run()
|
|||||||
* the value returned from poll() to detect whether actuator control has timed out or not.
|
* the value returned from poll() to detect whether actuator control has timed out or not.
|
||||||
* Instead, all ORB events need to be checked individually (see below).
|
* Instead, all ORB events need to be checked individually (see below).
|
||||||
*/
|
*/
|
||||||
_poll_fds_num = 0;
|
add_poll_fd(busevent_fd);
|
||||||
_poll_fds[_poll_fds_num] = ::pollfd();
|
|
||||||
_poll_fds[_poll_fds_num].fd = busevent_fd;
|
/*
|
||||||
_poll_fds[_poll_fds_num].events = POLLIN;
|
* setup poll to look for actuator direct input if we are
|
||||||
_poll_fds_num += 1;
|
* subscribed to the topic
|
||||||
|
*/
|
||||||
|
if (_actuator_direct_sub != -1) {
|
||||||
|
_actuator_direct_poll_fd_num = add_poll_fd(_actuator_direct_sub);
|
||||||
|
}
|
||||||
|
|
||||||
while (!_task_should_exit) {
|
while (!_task_should_exit) {
|
||||||
// update actuator controls subscriptions if needed
|
// update actuator controls subscriptions if needed
|
||||||
@@ -326,6 +348,8 @@ int UavcanNode::run()
|
|||||||
|
|
||||||
node_spin_once(); // Non-blocking
|
node_spin_once(); // Non-blocking
|
||||||
|
|
||||||
|
bool new_output = false;
|
||||||
|
|
||||||
// this would be bad...
|
// this would be bad...
|
||||||
if (poll_ret < 0) {
|
if (poll_ret < 0) {
|
||||||
log("poll error %d", errno);
|
log("poll error %d", errno);
|
||||||
@@ -333,24 +357,39 @@ int UavcanNode::run()
|
|||||||
} else {
|
} else {
|
||||||
// get controls for required topics
|
// get controls for required topics
|
||||||
bool controls_updated = false;
|
bool controls_updated = false;
|
||||||
unsigned poll_id = 1;
|
|
||||||
for (unsigned i = 0; i < NUM_ACTUATOR_CONTROL_GROUPS; i++) {
|
for (unsigned i = 0; i < NUM_ACTUATOR_CONTROL_GROUPS; i++) {
|
||||||
if (_control_subs[i] > 0) {
|
if (_control_subs[i] > 0) {
|
||||||
if (_poll_fds[poll_id].revents & POLLIN) {
|
if (_poll_fds[_poll_ids[i]].revents & POLLIN) {
|
||||||
controls_updated = true;
|
controls_updated = true;
|
||||||
orb_copy(_control_topics[i], _control_subs[i], &_controls[i]);
|
orb_copy(_control_topics[i], _control_subs[i], &_controls[i]);
|
||||||
}
|
}
|
||||||
poll_id++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
see if we have any direct actuator updates
|
||||||
|
*/
|
||||||
|
if (_actuator_direct_sub != -1 &&
|
||||||
|
(_poll_fds[_actuator_direct_poll_fd_num].revents & POLLIN) &&
|
||||||
|
orb_copy(ORB_ID(actuator_direct), _actuator_direct_sub, &_actuator_direct) == OK &&
|
||||||
|
!_test_in_progress) {
|
||||||
|
if (_actuator_direct.nvalues > NUM_ACTUATOR_OUTPUTS) {
|
||||||
|
_actuator_direct.nvalues = NUM_ACTUATOR_OUTPUTS;
|
||||||
|
}
|
||||||
|
memcpy(&_outputs.output[0], &_actuator_direct.values[0],
|
||||||
|
_actuator_direct.nvalues*sizeof(float));
|
||||||
|
_outputs.noutputs = _actuator_direct.nvalues;
|
||||||
|
new_output = true;
|
||||||
|
}
|
||||||
|
|
||||||
// can we mix?
|
// can we mix?
|
||||||
if (_test_in_progress) {
|
if (_test_in_progress) {
|
||||||
float test_outputs[NUM_ACTUATOR_OUTPUTS] = {};
|
memset(&_outputs, 0, sizeof(_outputs));
|
||||||
test_outputs[_test_motor.motor_number] = _test_motor.value*2.0f-1.0f;
|
if (_test_motor.motor_number < NUM_ACTUATOR_OUTPUTS) {
|
||||||
|
_outputs.output[_test_motor.motor_number] = _test_motor.value*2.0f-1.0f;
|
||||||
// Output to the bus
|
_outputs.noutputs = _test_motor.motor_number+1;
|
||||||
_esc_controller.update_outputs(test_outputs, NUM_ACTUATOR_OUTPUTS);
|
}
|
||||||
|
new_output = true;
|
||||||
} else if (controls_updated && (_mixers != nullptr)) {
|
} else if (controls_updated && (_mixers != nullptr)) {
|
||||||
|
|
||||||
// XXX one output group has 8 outputs max,
|
// XXX one output group has 8 outputs max,
|
||||||
@@ -358,39 +397,41 @@ int UavcanNode::run()
|
|||||||
unsigned num_outputs_max = 8;
|
unsigned num_outputs_max = 8;
|
||||||
|
|
||||||
// Do mixing
|
// Do mixing
|
||||||
outputs.noutputs = _mixers->mix(&outputs.output[0], num_outputs_max);
|
_outputs.noutputs = _mixers->mix(&_outputs.output[0], num_outputs_max);
|
||||||
outputs.timestamp = hrt_absolute_time();
|
|
||||||
|
|
||||||
// iterate actuators
|
new_output = true;
|
||||||
for (unsigned i = 0; i < outputs.noutputs; i++) {
|
|
||||||
// last resort: catch NaN, INF and out-of-band errors
|
|
||||||
if (!isfinite(outputs.output[i])) {
|
|
||||||
/*
|
|
||||||
* Value is NaN, INF or out of band - set to the minimum value.
|
|
||||||
* This will be clearly visible on the servo status and will limit the risk of accidentally
|
|
||||||
* spinning motors. It would be deadly in flight.
|
|
||||||
*/
|
|
||||||
outputs.output[i] = -1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// limit outputs to valid range
|
|
||||||
|
|
||||||
// never go below min
|
|
||||||
if (outputs.output[i] < -1.0f) {
|
|
||||||
outputs.output[i] = -1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// never go below max
|
|
||||||
if (outputs.output[i] > 1.0f) {
|
|
||||||
outputs.output[i] = 1.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output to the bus
|
|
||||||
_esc_controller.update_outputs(outputs.output, outputs.noutputs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (new_output) {
|
||||||
|
// iterate actuators, checking for valid values
|
||||||
|
for (uint8_t i = 0; i < _outputs.noutputs; i++) {
|
||||||
|
// last resort: catch NaN, INF and out-of-band errors
|
||||||
|
if (!isfinite(_outputs.output[i])) {
|
||||||
|
/*
|
||||||
|
* Value is NaN, INF or out of band - set to the minimum value.
|
||||||
|
* This will be clearly visible on the servo status and will limit the risk of accidentally
|
||||||
|
* spinning motors. It would be deadly in flight.
|
||||||
|
*/
|
||||||
|
_outputs.output[i] = -1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// never go below min
|
||||||
|
if (_outputs.output[i] < -1.0f) {
|
||||||
|
_outputs.output[i] = -1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// never go above max
|
||||||
|
if (_outputs.output[i] > 1.0f) {
|
||||||
|
_outputs.output[i] = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Output to the bus
|
||||||
|
_outputs.timestamp = hrt_absolute_time();
|
||||||
|
_esc_controller.update_outputs(_outputs.output, _outputs.noutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check motor test state
|
// Check motor test state
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
orb_check(_test_motor_sub, &updated);
|
orb_check(_test_motor_sub, &updated);
|
||||||
@@ -459,7 +500,6 @@ UavcanNode::subscribe()
|
|||||||
uint32_t sub_groups = _groups_required & ~_groups_subscribed;
|
uint32_t sub_groups = _groups_required & ~_groups_subscribed;
|
||||||
uint32_t unsub_groups = _groups_subscribed & ~_groups_required;
|
uint32_t unsub_groups = _groups_subscribed & ~_groups_required;
|
||||||
// the first fd used by CAN
|
// the first fd used by CAN
|
||||||
_poll_fds_num = 1;
|
|
||||||
for (unsigned i = 0; i < NUM_ACTUATOR_CONTROL_GROUPS; i++) {
|
for (unsigned i = 0; i < NUM_ACTUATOR_CONTROL_GROUPS; i++) {
|
||||||
if (sub_groups & (1 << i)) {
|
if (sub_groups & (1 << i)) {
|
||||||
warnx("subscribe to actuator_controls_%d", i);
|
warnx("subscribe to actuator_controls_%d", i);
|
||||||
@@ -472,9 +512,7 @@ UavcanNode::subscribe()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_control_subs[i] > 0) {
|
if (_control_subs[i] > 0) {
|
||||||
_poll_fds[_poll_fds_num].fd = _control_subs[i];
|
_poll_ids[i] = add_poll_fd(_control_subs[i]);
|
||||||
_poll_fds[_poll_fds_num].events = POLLIN;
|
|
||||||
_poll_fds_num++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -572,6 +610,14 @@ UavcanNode::print_info()
|
|||||||
(unsigned)_groups_subscribed, (unsigned)_groups_required, _poll_fds_num);
|
(unsigned)_groups_subscribed, (unsigned)_groups_required, _poll_fds_num);
|
||||||
printf("ESC mixer: %s\n", (_mixers == nullptr) ? "NONE" : "OK");
|
printf("ESC mixer: %s\n", (_mixers == nullptr) ? "NONE" : "OK");
|
||||||
|
|
||||||
|
if (_outputs.noutputs != 0) {
|
||||||
|
printf("ESC output: ");
|
||||||
|
for (uint8_t i=0; i<_outputs.noutputs; i++) {
|
||||||
|
printf("%d ", (int)(_outputs.output[i]*1000));
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Sensor bridges
|
// Sensor bridges
|
||||||
auto br = _sensor_bridges.getHead();
|
auto br = _sensor_bridges.getHead();
|
||||||
while (br != nullptr) {
|
while (br != nullptr) {
|
||||||
@@ -590,7 +636,7 @@ UavcanNode::print_info()
|
|||||||
static void print_usage()
|
static void print_usage()
|
||||||
{
|
{
|
||||||
warnx("usage: \n"
|
warnx("usage: \n"
|
||||||
"\tuavcan {start|status|stop}");
|
"\tuavcan {start|status|stop|arm|disarm}");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" __EXPORT int uavcan_main(int argc, char *argv[]);
|
extern "C" __EXPORT int uavcan_main(int argc, char *argv[]);
|
||||||
@@ -637,6 +683,16 @@ int uavcan_main(int argc, char *argv[])
|
|||||||
::exit(0);
|
::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!std::strcmp(argv[1], "arm")) {
|
||||||
|
inst->arm_actuators(true);
|
||||||
|
::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::strcmp(argv[1], "disarm")) {
|
||||||
|
inst->arm_actuators(false);
|
||||||
|
::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (!std::strcmp(argv[1], "stop")) {
|
if (!std::strcmp(argv[1], "stop")) {
|
||||||
delete inst;
|
delete inst;
|
||||||
::exit(0);
|
::exit(0);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include <uORB/topics/actuator_outputs.h>
|
#include <uORB/topics/actuator_outputs.h>
|
||||||
#include <uORB/topics/actuator_armed.h>
|
#include <uORB/topics/actuator_armed.h>
|
||||||
#include <uORB/topics/test_motor.h>
|
#include <uORB/topics/test_motor.h>
|
||||||
|
#include <uORB/topics/actuator_direct.h>
|
||||||
|
|
||||||
#include "actuators/esc.hpp"
|
#include "actuators/esc.hpp"
|
||||||
#include "sensors/sensor_bridge.hpp"
|
#include "sensors/sensor_bridge.hpp"
|
||||||
@@ -57,6 +58,9 @@
|
|||||||
#define NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN 4
|
#define NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN 4
|
||||||
#define UAVCAN_DEVICE_PATH "/dev/uavcan/esc"
|
#define UAVCAN_DEVICE_PATH "/dev/uavcan/esc"
|
||||||
|
|
||||||
|
// we add two to allow for actuator_direct and busevent
|
||||||
|
#define UAVCAN_NUM_POLL_FDS (NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN+2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A UAVCAN node.
|
* A UAVCAN node.
|
||||||
*/
|
*/
|
||||||
@@ -97,6 +101,8 @@ private:
|
|||||||
int init(uavcan::NodeID node_id);
|
int init(uavcan::NodeID node_id);
|
||||||
void node_spin_once();
|
void node_spin_once();
|
||||||
int run();
|
int run();
|
||||||
|
int add_poll_fd(int fd); ///< add a fd to poll list, returning index into _poll_fds[]
|
||||||
|
|
||||||
|
|
||||||
int _task = -1; ///< handle to the OS task
|
int _task = -1; ///< handle to the OS task
|
||||||
bool _task_should_exit = false; ///< flag to indicate to tear down the CAN driver
|
bool _task_should_exit = false; ///< flag to indicate to tear down the CAN driver
|
||||||
@@ -125,6 +131,15 @@ private:
|
|||||||
int _control_subs[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
|
int _control_subs[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
|
||||||
actuator_controls_s _controls[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
|
actuator_controls_s _controls[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
|
||||||
orb_id_t _control_topics[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
|
orb_id_t _control_topics[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
|
||||||
pollfd _poll_fds[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN + 1] = {}; ///< +1 for /dev/uavcan/busevent
|
pollfd _poll_fds[UAVCAN_NUM_POLL_FDS] = {};
|
||||||
unsigned _poll_fds_num = 0;
|
unsigned _poll_fds_num = 0;
|
||||||
|
|
||||||
|
int _actuator_direct_sub = -1; ///< uORB subscription of the actuator_direct topic
|
||||||
|
uint8_t _actuator_direct_poll_fd_num;
|
||||||
|
actuator_direct_s _actuator_direct;
|
||||||
|
|
||||||
|
actuator_outputs_s _outputs;
|
||||||
|
|
||||||
|
// index into _poll_fds for each _control_subs handle
|
||||||
|
uint8_t _poll_ids[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,9 +59,10 @@ __EXPORT int motor_test_main(int argc, char *argv[]);
|
|||||||
static void motor_test(unsigned channel, float value);
|
static void motor_test(unsigned channel, float value);
|
||||||
static void usage(const char *reason);
|
static void usage(const char *reason);
|
||||||
|
|
||||||
|
static orb_advert_t _test_motor_pub;
|
||||||
|
|
||||||
void motor_test(unsigned channel, float value)
|
void motor_test(unsigned channel, float value)
|
||||||
{
|
{
|
||||||
orb_advert_t _test_motor_pub;
|
|
||||||
struct test_motor_s _test_motor;
|
struct test_motor_s _test_motor;
|
||||||
|
|
||||||
_test_motor.motor_number = channel;
|
_test_motor.motor_number = channel;
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
############################################################################
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 PX4 Development Team. All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in
|
||||||
|
# the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||||
|
# used to endorse or promote products derived from this software
|
||||||
|
# without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||||
|
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Dump file utility
|
||||||
|
#
|
||||||
|
|
||||||
|
MODULE_COMMAND = reflect
|
||||||
|
SRCS = reflect.c
|
||||||
|
|
||||||
|
MAXOPTIMIZATION = -Os
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Andrew Tridgell. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||||
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file reflect.c
|
||||||
|
*
|
||||||
|
* simple data reflector for load testing terminals (especially USB)
|
||||||
|
*
|
||||||
|
* @author Andrew Tridgell
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <nuttx/config.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <systemlib/err.h>
|
||||||
|
|
||||||
|
__EXPORT int reflect_main(int argc, char *argv[]);
|
||||||
|
|
||||||
|
// memory corruption checking
|
||||||
|
#define MAX_BLOCKS 1000
|
||||||
|
static uint32_t nblocks;
|
||||||
|
struct block {
|
||||||
|
uint32_t v[256];
|
||||||
|
};
|
||||||
|
static struct block *blocks[MAX_BLOCKS];
|
||||||
|
|
||||||
|
#define VALUE(i) ((i*7) ^ 0xDEADBEEF)
|
||||||
|
|
||||||
|
static void allocate_blocks(void)
|
||||||
|
{
|
||||||
|
while (nblocks < MAX_BLOCKS) {
|
||||||
|
blocks[nblocks] = calloc(1, sizeof(struct block));
|
||||||
|
if (blocks[nblocks] == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (uint32_t i=0; i<sizeof(blocks[nblocks]->v)/sizeof(uint32_t); i++) {
|
||||||
|
blocks[nblocks]->v[i] = VALUE(i);
|
||||||
|
}
|
||||||
|
nblocks++;
|
||||||
|
}
|
||||||
|
printf("Allocated %u blocks\n", nblocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_blocks(void)
|
||||||
|
{
|
||||||
|
for (uint32_t n=0; n<nblocks; n++) {
|
||||||
|
for (uint32_t i=0; i<sizeof(blocks[nblocks]->v)/sizeof(uint32_t); i++) {
|
||||||
|
assert(blocks[n]->v[i] == VALUE(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
reflect_main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
uint32_t total = 0;
|
||||||
|
printf("Starting reflector\n");
|
||||||
|
|
||||||
|
allocate_blocks();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char buf[128];
|
||||||
|
ssize_t n = read(0, buf, sizeof(buf));
|
||||||
|
if (n < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (n > 0) {
|
||||||
|
write(1, buf, n);
|
||||||
|
}
|
||||||
|
total += n;
|
||||||
|
if (total > 1024000) {
|
||||||
|
check_blocks();
|
||||||
|
total = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user