123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 |
- /*
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /*
- simulator connector for morse simulator
- */
- #include "SIM_Morse.h"
- #include <arpa/inet.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <AP_HAL/AP_HAL.h>
- #include <AP_Logger/AP_Logger.h>
- #include "pthread.h"
- #include <AP_HAL/utility/replace.h>
- extern const AP_HAL::HAL& hal;
- using namespace SITL;
- static const struct {
- const char *name;
- float value;
- bool save;
- } sim_defaults[] = {
- { "AHRS_EKF_TYPE", 10 },
- { "INS_GYR_CAL", 0 },
- { "RC1_MIN", 1000, true },
- { "RC1_MAX", 2000, true },
- { "RC2_MIN", 1000, true },
- { "RC2_MAX", 2000, true },
- { "RC3_MIN", 1000, true },
- { "RC3_MAX", 2000, true },
- { "RC4_MIN", 1000, true },
- { "RC4_MAX", 2000, true },
- { "RC2_REVERSED", 1 }, // interlink has reversed rc2
- { "SERVO1_MIN", 1000 },
- { "SERVO1_MAX", 2000 },
- { "SERVO2_MIN", 1000 },
- { "SERVO2_MAX", 2000 },
- { "SERVO3_MIN", 1000 },
- { "SERVO3_MAX", 2000 },
- { "SERVO4_MIN", 1000 },
- { "SERVO4_MAX", 2000 },
- { "SERVO5_MIN", 1000 },
- { "SERVO5_MAX", 2000 },
- { "SERVO6_MIN", 1000 },
- { "SERVO6_MAX", 2000 },
- { "SERVO6_MIN", 1000 },
- { "SERVO6_MAX", 2000 },
- { "INS_ACC2OFFS_X", 0.001 },
- { "INS_ACC2OFFS_Y", 0.001 },
- { "INS_ACC2OFFS_Z", 0.001 },
- { "INS_ACC2SCAL_X", 1.001 },
- { "INS_ACC2SCAL_Y", 1.001 },
- { "INS_ACC2SCAL_Z", 1.001 },
- { "INS_ACCOFFS_X", 0.001 },
- { "INS_ACCOFFS_Y", 0.001 },
- { "INS_ACCOFFS_Z", 0.001 },
- { "INS_ACCSCAL_X", 1.001 },
- { "INS_ACCSCAL_Y", 1.001 },
- { "INS_ACCSCAL_Z", 1.001 },
- };
- Morse::Morse(const char *frame_str) :
- Aircraft(frame_str)
- {
- char *saveptr = nullptr;
- char *s = strdup(frame_str);
- char *frame_option = strtok_r(s, ":", &saveptr);
- char *args1 = strtok_r(nullptr, ":", &saveptr);
- char *args2 = strtok_r(nullptr, ":", &saveptr);
- char *args3 = strtok_r(nullptr, ":", &saveptr);
- /*
- allow setting of IP, sensors port and control port
- format morse:IPADDRESS:SENSORS_PORT:CONTROL_PORT
- */
- if (args1) {
- morse_ip = args1;
- }
- if (args2) {
- morse_sensors_port = atoi(args2);
- morse_control_port = morse_sensors_port+1;
- }
- if (args3) {
- morse_control_port = atoi(args3);
- }
- if (strstr(frame_option, "-rover")) {
- output_type = OUTPUT_ROVER;
- } else if (strstr(frame_option, "-quad")) {
- output_type = OUTPUT_QUAD;
- } else if (strstr(frame_option, "-pwm")) {
- output_type = OUTPUT_PWM;
- } else {
- // default to rover
- output_type = OUTPUT_ROVER;
- }
- for (uint8_t i=0; i<ARRAY_SIZE(sim_defaults); i++) {
- AP_Param::set_default_by_name(sim_defaults[i].name, sim_defaults[i].value);
- if (sim_defaults[i].save) {
- enum ap_var_type ptype;
- AP_Param *p = AP_Param::find(sim_defaults[i].name, &ptype);
- if (!p->configured()) {
- p->save();
- }
- }
- }
- printf("Started Morse with %s:%u:%u type %u\n",
- morse_ip, morse_sensors_port, morse_control_port,
- (unsigned)output_type);
- }
- /*
- very simple JSON parser for sensor data
- called with pointer to one row of sensor data, nul terminated
- This parser does not do any syntax checking, and is not at all
- general purpose
- */
- bool Morse::parse_sensors(const char *json)
- {
- //printf("%s\n", json);
- for (uint16_t i=0; i<ARRAY_SIZE(keytable); i++) {
- struct keytable &key = keytable[i];
- /* look for section header */
- const char *p = strstr(json, key.section);
- if (!p) {
- // we don't have this sensor
- continue;
- }
- p += strlen(key.section)+1;
- // find key inside section
- p = strstr(p, key.key);
- if (!p) {
- printf("Failed to find key %s/%s\n", key.section, key.key);
- return false;
- }
- p += strlen(key.key)+3;
- switch (key.type) {
- case DATA_FLOAT:
- *((float *)key.ptr) = atof(p);
- break;
- case DATA_DOUBLE:
- *((double *)key.ptr) = atof(p);
- break;
- case DATA_VECTOR3F: {
- Vector3f *v = (Vector3f *)key.ptr;
- if (sscanf(p, "[%f, %f, %f]", &v->x, &v->y, &v->z) != 3) {
- printf("Failed to parse Vector3f for %s/%s\n", key.section, key.key);
- return false;
- }
- break;
- }
- case DATA_VECTOR3F_ARRAY: {
- // example: [[0.0, 0.0, 0.0], [-8.97607135772705, -8.976069450378418, -8.642673492431641e-07]]
- if (*p++ != '[') {
- return false;
- }
- uint16_t n = 0;
- struct vector3f_array *v = (struct vector3f_array *)key.ptr;
- while (true) {
- if (n >= v->length) {
- Vector3f *d = (Vector3f *)realloc(v->data, sizeof(Vector3f)*(n+1));
- if (d == nullptr) {
- return false;
- }
- v->data = d;
- v->length = n+1;
- }
- if (sscanf(p, "[%f, %f, %f]", &v->data[n].x, &v->data[n].y, &v->data[n].z) != 3) {
- printf("Failed to parse Vector3f for %s/%s[%u]\n", key.section, key.key, n);
- return false;
- }
- n++;
- p = strchr(p,']');
- if (!p) {
- return false;
- }
- p++;
- if (p[0] != ',') {
- break;
- }
- if (p[1] != ' ') {
- return false;
- }
- p += 2;
- }
- if (p[0] != ']') {
- return false;
- }
- v->length = n;
- break;
- }
- case DATA_FLOAT_ARRAY: {
- // example: [18.0, 12.694079399108887]
- if (*p++ != '[') {
- return false;
- }
- uint16_t n = 0;
- struct float_array *v = (struct float_array *)key.ptr;
- while (true) {
- if (n >= v->length) {
- float *d = (float *)realloc(v->data, sizeof(float)*(n+1));
- if (d == nullptr) {
- return false;
- }
- v->data = d;
- v->length = n+1;
- }
- v->data[n] = atof(p);
- n++;
- p = strchr(p,',');
- if (!p) {
- break;
- }
- p++;
- }
- v->length = n;
- break;
- }
- }
- }
- socket_frame_counter++;
- return true;
- }
- /*
- connect to the required sockets
- */
- bool Morse::connect_sockets(void)
- {
- if (!sensors_sock) {
- sensors_sock = new SocketAPM(false);
- if (!sensors_sock) {
- AP_HAL::panic("Out of memory for sensors socket");
- }
- if (!sensors_sock->connect(morse_ip, morse_sensors_port)) {
- usleep(100000);
- if (connect_counter++ == 20) {
- printf("Waiting to connect to sensors control on %s:%u\n",
- morse_ip, morse_sensors_port);
- connect_counter = 0;
- }
- delete sensors_sock;
- sensors_sock = nullptr;
- return false;
- }
- sensors_sock->reuseaddress();
- printf("Sensors connected\n");
- }
- if (!control_sock) {
- control_sock = new SocketAPM(false);
- if (!control_sock) {
- AP_HAL::panic("Out of memory for control socket");
- }
- if (!control_sock->connect(morse_ip, morse_control_port)) {
- usleep(100000);
- if (connect_counter++ == 20) {
- printf("Waiting to connect to control control on %s:%u\n",
- morse_ip, morse_control_port);
- connect_counter = 0;
- }
- delete control_sock;
- control_sock = nullptr;
- return false;
- }
- control_sock->reuseaddress();
- printf("Control connected\n");
- }
- return true;
- }
- /*
- get any new data from the sensors socket
- */
- bool Morse::sensors_receive(void)
- {
- ssize_t ret = sensors_sock->recv(&sensor_buffer[sensor_buffer_len], sizeof(sensor_buffer)-sensor_buffer_len, 0);
- if (ret <= 0) {
- no_data_counter++;
- if (no_data_counter == 1000) {
- no_data_counter = 0;
- delete sensors_sock;
- delete control_sock;
- sensors_sock = nullptr;
- control_sock = nullptr;
- }
- return false;
- }
- no_data_counter = 0;
- // convert '\n' into nul
- while (uint8_t *p = (uint8_t *)memchr(&sensor_buffer[sensor_buffer_len], '\n', ret)) {
- *p = 0;
- }
- sensor_buffer_len += ret;
- const uint8_t *p2 = (const uint8_t *)memrchr(sensor_buffer, 0, sensor_buffer_len);
- if (p2 == nullptr || p2 == sensor_buffer) {
- return false;
- }
- const uint8_t *p1 = (const uint8_t *)memrchr(sensor_buffer, 0, p2 - sensor_buffer);
- if (p1 == nullptr) {
- return false;
- }
- bool parse_ok = parse_sensors((const char *)(p1+1));
- memmove(sensor_buffer, p2, sensor_buffer_len - (p2 - sensor_buffer));
- sensor_buffer_len = sensor_buffer_len - (p2 - sensor_buffer);
- return parse_ok;
- }
- /*
- output control command assuming skid-steering rover
- */
- void Morse::output_rover(const struct sitl_input &input)
- {
- float motor1 = 2*((input.servos[0]-1000)/1000.0f - 0.5f);
- float motor2 = 2*((input.servos[2]-1000)/1000.0f - 0.5f);
- const float steer_rate_max_dps = 60;
- const float max_speed = 2;
- const float steering_rps = (motor1 - motor2) * radians(steer_rate_max_dps);
- const float speed_ms = 0.5*(motor1 + motor2) * max_speed;
- // construct a JSON packet for v and w
- char buf[60];
- snprintf(buf, sizeof(buf)-1, "{\"v\": %.3f, \"w\": %.2f}\n",
- speed_ms, -steering_rps);
- buf[sizeof(buf)-1] = 0;
- control_sock->send(buf, strlen(buf));
- }
- /*
- output control command assuming a 4 channel quad
- */
- void Morse::output_quad(const struct sitl_input &input)
- {
- const float max_thrust = 1500;
- float motors[4];
- for (uint8_t i=0; i<4; i++) {
- motors[i] = constrain_float(((input.servos[i]-1000)/1000.0f) * max_thrust, 0, max_thrust);
- }
- const float &m_right = motors[0];
- const float &m_left = motors[1];
- const float &m_front = motors[2];
- const float &m_back = motors[3];
- // quad format in Morse is:
- // m1: back
- // m2: right
- // m3: front
- // m4: left
- // construct a JSON packet for motors
- char buf[60];
- snprintf(buf, sizeof(buf)-1, "{\"engines\": [%.3f, %.3f, %.3f, %.3f]}\n",
- m_back, m_right, m_front, m_left);
- buf[sizeof(buf)-1] = 0;
- control_sock->send(buf, strlen(buf));
- }
- /*
- output all 16 channels as PWM values. This allows for general
- control of a robot
- */
- void Morse::output_pwm(const struct sitl_input &input)
- {
- char buf[200];
- snprintf(buf, sizeof(buf)-1, "{\"pwm\": [%u, %uf, %u, %u, %u, %uf, %u, %u, %u, %uf, %u, %u, %u, %uf, %u, %u]}\n",
- input.servos[0], input.servos[1], input.servos[2], input.servos[3],
- input.servos[4], input.servos[5], input.servos[6], input.servos[7],
- input.servos[8], input.servos[9], input.servos[10], input.servos[11],
- input.servos[12], input.servos[13], input.servos[14], input.servos[15]);
- buf[sizeof(buf)-1] = 0;
- control_sock->send(buf, strlen(buf));
- }
- /*
- update the Morse simulation by one time step
- */
- void Morse::update(const struct sitl_input &input)
- {
- if (!connect_sockets()) {
- return;
- }
- last_state = state;
- if (sensors_receive()) {
- // update average frame time used for extrapolation
- double dt = constrain_float(state.timestamp - last_state.timestamp, 0.001, 1.0/50);
- if (average_frame_time_s < 1.0e-6) {
- average_frame_time_s = dt;
- }
- average_frame_time_s = average_frame_time_s * 0.98 + dt * 0.02;
- }
- double dt_s = state.timestamp - last_state.timestamp;
- if (dt_s < 0 || dt_s > 1) {
- // cope with restarting while connected
- initial_time_s = time_now_us * 1.0e-6f;
- last_time_s = state.timestamp;
- return;
- }
- if (dt_s < 0.00001f) {
- float delta_time = 0.001;
- // don't go past the next expected frame
- if (delta_time + extrapolated_s > average_frame_time_s) {
- delta_time = average_frame_time_s - extrapolated_s;
- }
- if (delta_time <= 0) {
- usleep(1000);
- return;
- }
- time_now_us += delta_time * 1.0e6;
- extrapolate_sensors(delta_time);
- update_position();
- update_mag_field_bf();
- usleep(delta_time*1.0e6);
- extrapolated_s += delta_time;
- report_FPS();
- return;
- }
- extrapolated_s = 0;
-
- if (initial_time_s <= 0) {
- dt_s = 0.001f;
- initial_time_s = state.timestamp - dt_s;
- }
- // convert from state variables to ardupilot conventions
- dcm.from_euler(state.pose.roll, -state.pose.pitch, -state.pose.yaw);
- gyro = Vector3f(state.imu.angular_velocity[0],
- -state.imu.angular_velocity[1],
- -state.imu.angular_velocity[2]);
- velocity_ef = Vector3f(state.velocity.world_linear_velocity[0],
- -state.velocity.world_linear_velocity[1],
- -state.velocity.world_linear_velocity[2]);
- position = Vector3f(state.gps.x, -state.gps.y, -state.gps.z);
- // Morse IMU accel is NEU, convert to NED
- accel_body = Vector3f(state.imu.linear_acceleration[0],
- -state.imu.linear_acceleration[1],
- -state.imu.linear_acceleration[2]);
- // limit to 16G to match pixhawk1
- float a_limit = GRAVITY_MSS*16;
- accel_body.x = constrain_float(accel_body.x, -a_limit, a_limit);
- accel_body.y = constrain_float(accel_body.y, -a_limit, a_limit);
- accel_body.z = constrain_float(accel_body.z, -a_limit, a_limit);
- // fill in laser scanner results, if available
- scanner.points = state.scanner.points;
- scanner.ranges = state.scanner.ranges;
- update_position();
- time_advance();
- uint64_t new_time_us = (state.timestamp - initial_time_s)*1.0e6;
- if (new_time_us < time_now_us) {
- uint64_t dt_us = time_now_us - new_time_us;
- if (dt_us > 500000) {
- // time going backwards
- time_now_us = new_time_us;
- }
- } else {
- time_now_us = new_time_us;
- }
- last_time_s = state.timestamp;
- // update magnetic field
- update_mag_field_bf();
- switch (output_type) {
- case OUTPUT_ROVER:
- output_rover(input);
- break;
- case OUTPUT_QUAD:
- output_quad(input);
- break;
- case OUTPUT_PWM:
- output_pwm(input);
- break;
- }
- report_FPS();
- send_report();
- }
- /*
- report frame rates
- */
- void Morse::report_FPS(void)
- {
- if (frame_counter++ % 1000 == 0) {
- if (!is_zero(last_frame_count_s)) {
- uint64_t frames = socket_frame_counter - last_socket_frame_counter;
- last_socket_frame_counter = socket_frame_counter;
- double dt = state.timestamp - last_frame_count_s;
- printf("%.2f/%.2f FPS avg=%.2f\n",
- frames / dt, 1000 / dt, 1.0/average_frame_time_s);
- } else {
- printf("Initial position %f %f %f\n", position.x, position.y, position.z);
- }
- last_frame_count_s = state.timestamp;
- }
- }
- /*
- send a report to the vehicle control code over MAVLink
- */
- void Morse::send_report(void)
- {
- const uint32_t now = AP_HAL::millis();
- #if defined(__CYGWIN__) || defined(__CYGWIN64__)
- if (now < 10000) {
- // don't send lidar reports until 10s after startup. This
- // avoids a windows threading issue with non-blocking sockets
- // and the initial wait on uartA
- return;
- }
- #endif
- // this is usually loopback
- if (!mavlink.connected && mav_socket.connect(mavlink_loopback_address, mavlink_loopback_port)) {
- ::printf("Morse MAVLink loopback connected to %s:%u\n", mavlink_loopback_address, (unsigned)mavlink_loopback_port);
- mavlink.connected = true;
- }
- if (!mavlink.connected) {
- return;
- }
- // send a OBSTACLE_DISTANCE messages at 15 Hz
- if (now - send_report_last_ms >= (1000/15) && scanner.points.length == scanner.ranges.length && scanner.points.length > 0) {
- send_report_last_ms = now;
- mavlink_obstacle_distance_t packet {};
- packet.time_usec = AP_HAL::micros64();
- packet.min_distance = 1;
- packet.max_distance = 0;
- packet.sensor_type = MAV_DISTANCE_SENSOR_LASER;
- packet.increment = 0; // use increment_f
- packet.angle_offset = 180;
- packet.increment_f = (-5); // NOTE! This is negative because the distances[] arc is counter-clockwise
- for (uint8_t i=0; i<MAVLINK_MSG_OBSTACLE_DISTANCE_FIELD_DISTANCES_LEN; i++) {
- // default distance unless overwritten
- packet.distances[i] = 65535;
- if (i >= scanner.points.length) {
- continue;
- }
- // convert m to cm and sanity check
- const Vector2f v = Vector2f(scanner.points.data[i].x, scanner.points.data[i].y);
- const float distance_cm = v.length()*100;
- if (distance_cm < packet.min_distance || distance_cm >= 65535) {
- continue;
- }
- packet.distances[i] = distance_cm;
- const float max_cm = scanner.ranges.data[i] * 100.0;
- if (packet.max_distance < max_cm && max_cm > 0 && max_cm < 65535) {
- packet.max_distance = max_cm;
- }
- }
- mavlink_message_t msg;
- mavlink_status_t *chan0_status = mavlink_get_channel_status(MAVLINK_COMM_0);
- uint8_t saved_seq = chan0_status->current_tx_seq;
- chan0_status->current_tx_seq = mavlink.seq;
- uint16_t len = mavlink_msg_obstacle_distance_encode(
- 0,
- 0,
- &msg, &packet);
- chan0_status->current_tx_seq = saved_seq;
- uint8_t msgbuf[len];
- len = mavlink_msg_to_send_buffer(msgbuf, &msg);
- if (len > 0) {
- mav_socket.send(msgbuf, len);
- }
- }
- }
|