LCOV - code coverage report
Current view: top level - SITL - SIM_Morse.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 344 0.0 %
Date: 2026-09-09 11:02:08 Functions: 0 11 0.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : /*
      16             :   simulator connector for morse simulator
      17             : */
      18             : 
      19             : #include "SIM_Morse.h"
      20             : 
      21             : #if AP_SIM_MORSE_ENABLED
      22             : 
      23             : #include <arpa/inet.h>
      24             : #include <errno.h>
      25             : #include <fcntl.h>
      26             : #include <stdio.h>
      27             : #include <stdarg.h>
      28             : #include <sys/stat.h>
      29             : #include <sys/types.h>
      30             : 
      31             : #include <AP_HAL/AP_HAL.h>
      32             : #include "pthread.h"
      33             : #include <AP_HAL/utility/replace.h>
      34             : 
      35             : extern const AP_HAL::HAL& hal;
      36             : 
      37             : using namespace SITL;
      38             : 
      39             : static const struct {
      40             :     const char *name;
      41             :     float value;
      42             :     bool save;
      43             : } sim_defaults[] = {
      44             :     { "AHRS_EKF_TYPE", 10 },
      45             :     { "INS_GYR_CAL", 0 },
      46             :     { "RC1_MIN", 1000, true },
      47             :     { "RC1_MAX", 2000, true },
      48             :     { "RC2_MIN", 1000, true },
      49             :     { "RC2_MAX", 2000, true },
      50             :     { "RC3_MIN", 1000, true },
      51             :     { "RC3_MAX", 2000, true },
      52             :     { "RC4_MIN", 1000, true },
      53             :     { "RC4_MAX", 2000, true },
      54             :     { "RC2_REVERSED", 1 }, // interlink has reversed rc2
      55             :     { "SERVO1_MIN", 1000 },
      56             :     { "SERVO1_MAX", 2000 },
      57             :     { "SERVO2_MIN", 1000 },
      58             :     { "SERVO2_MAX", 2000 },
      59             :     { "SERVO3_MIN", 1000 },
      60             :     { "SERVO3_MAX", 2000 },
      61             :     { "SERVO4_MIN", 1000 },
      62             :     { "SERVO4_MAX", 2000 },
      63             :     { "SERVO5_MIN", 1000 },
      64             :     { "SERVO5_MAX", 2000 },
      65             :     { "SERVO6_MIN", 1000 },
      66             :     { "SERVO6_MAX", 2000 },
      67             :     { "SERVO6_MIN", 1000 },
      68             :     { "SERVO6_MAX", 2000 },
      69             :     { "INS_ACC2OFFS_X",    0.001 },
      70             :     { "INS_ACC2OFFS_Y",    0.001 },
      71             :     { "INS_ACC2OFFS_Z",    0.001 },
      72             :     { "INS_ACC2SCAL_X",    1.001 },
      73             :     { "INS_ACC2SCAL_Y",    1.001 },
      74             :     { "INS_ACC2SCAL_Z",    1.001 },
      75             :     { "INS_ACCOFFS_X",     0.001 },
      76             :     { "INS_ACCOFFS_Y",     0.001 },
      77             :     { "INS_ACCOFFS_Z",     0.001 },
      78             :     { "INS_ACCSCAL_X",     1.001 },
      79             :     { "INS_ACCSCAL_Y",     1.001 },
      80             :     { "INS_ACCSCAL_Z",     1.001 },
      81             : };
      82             : 
      83             : 
      84           0 : Morse::Morse(const char *frame_str) :
      85           0 :     Aircraft(frame_str)
      86             : {
      87           0 :     char *saveptr = nullptr;
      88           0 :     char *s = strdup(frame_str);
      89           0 :     char *frame_option = strtok_r(s, ":", &saveptr);
      90           0 :     char *args1 = strtok_r(nullptr, ":", &saveptr);
      91           0 :     char *args2 = strtok_r(nullptr, ":", &saveptr);
      92           0 :     char *args3 = strtok_r(nullptr, ":", &saveptr);
      93             :     /*
      94             :       allow setting of IP, sensors port and control port
      95             :       format morse:IPADDRESS:SENSORS_PORT:CONTROL_PORT
      96             :      */
      97           0 :     if (args1) {
      98           0 :         morse_ip = args1;
      99             :     }
     100           0 :     if (args2) {
     101           0 :         morse_sensors_port = atoi(args2);
     102           0 :         morse_control_port = morse_sensors_port+1;
     103             :     }
     104           0 :     if (args3) {
     105           0 :         morse_control_port = atoi(args3);
     106             :     }
     107             : 
     108           0 :     if (strstr(frame_option, "-rover")) {
     109           0 :         output_type = OUTPUT_ROVER_REGULAR;
     110           0 :     } else if (strstr(frame_option, "-skid")) {
     111           0 :         output_type = OUTPUT_ROVER_SKID;
     112           0 :     } else if (strstr(frame_option, "-quad")) {
     113           0 :         output_type = OUTPUT_QUAD;
     114           0 :     } else if (strstr(frame_option, "-pwm")) {
     115           0 :         output_type = OUTPUT_PWM;
     116             :     } else {
     117             :         // default to rover
     118           0 :         output_type = OUTPUT_ROVER_REGULAR;
     119             :     }
     120             : 
     121           0 :     for (uint8_t i=0; i<ARRAY_SIZE(sim_defaults); i++) {
     122           0 :         AP_Param::set_default_by_name(sim_defaults[i].name, sim_defaults[i].value);
     123           0 :         if (sim_defaults[i].save) {
     124             :             enum ap_var_type ptype;
     125           0 :             AP_Param *p = AP_Param::find(sim_defaults[i].name, &ptype);
     126           0 :             if (!p->configured()) {
     127           0 :                 p->save();
     128             :             }
     129             :         }
     130             :     }
     131           0 :     printf("Started Morse with %s:%u:%u type %u\n",
     132           0 :            morse_ip, morse_sensors_port, morse_control_port,
     133           0 :            (unsigned)output_type);
     134           0 : }
     135             : 
     136             : /*
     137             :   very simple JSON parser for sensor data
     138             :   called with pointer to one row of sensor data, nul terminated
     139             : 
     140             :   This parser does not do any syntax checking, and is not at all
     141             :   general purpose
     142             : */
     143           0 : bool Morse::parse_sensors(const char *json)
     144             : {
     145             :     //printf("%s\n", json);
     146           0 :     for (uint16_t i=0; i<ARRAY_SIZE(keytable); i++) {
     147           0 :         struct keytable &key = keytable[i];
     148             : 
     149             :         /* look for section header */
     150           0 :         const char *p = strstr(json, key.section);
     151           0 :         if (!p) {
     152             :             // we don't have this sensor
     153           0 :             continue;
     154             :         }
     155           0 :         p += strlen(key.section)+1;
     156             : 
     157             :         // find key inside section
     158           0 :         p = strstr(p, key.key);
     159           0 :         if (!p) {
     160           0 :             printf("Failed to find key %s/%s\n", key.section, key.key);
     161           0 :             return false;
     162             :         }
     163             : 
     164           0 :         p += strlen(key.key)+3;
     165           0 :         switch (key.type) {
     166           0 :         case DATA_FLOAT:
     167           0 :             *((float *)key.ptr) = strtof(p, nullptr);
     168           0 :             break;
     169             : 
     170           0 :         case DATA_DOUBLE:
     171           0 :             *((double *)key.ptr) = atof(p);
     172           0 :             break;
     173             : 
     174           0 :         case DATA_VECTOR3F: {
     175           0 :             Vector3f *v = (Vector3f *)key.ptr;
     176           0 :             if (sscanf(p, "[%f, %f, %f]", &v->x, &v->y, &v->z) != 3) {
     177           0 :                 printf("Failed to parse Vector3f for %s/%s\n", key.section, key.key);
     178           0 :                 return false;
     179             :             }
     180           0 :             break;
     181             :         }
     182             : 
     183           0 :         case DATA_VECTOR3F_ARRAY: {
     184             :             // example: [[0.0, 0.0, 0.0], [-8.97607135772705, -8.976069450378418, -8.642673492431641e-07]]
     185           0 :             if (*p++ != '[') {
     186           0 :                 return false;
     187             :             }
     188           0 :             uint16_t n = 0;
     189           0 :             struct vector3f_array *v = (struct vector3f_array *)key.ptr;
     190             :             while (true) {
     191           0 :                 if (n >= v->length) {
     192           0 :                     Vector3f *d = (Vector3f *)realloc(v->data, sizeof(Vector3f)*(n+1));
     193           0 :                     if (d == nullptr) {
     194           0 :                         return false;
     195             :                     }
     196           0 :                     v->data = d;
     197           0 :                     v->length = n+1;
     198             :                 }
     199           0 :                 if (sscanf(p, "[%f, %f, %f]", &v->data[n].x, &v->data[n].y, &v->data[n].z) != 3) {
     200           0 :                     printf("Failed to parse Vector3f for %s/%s[%u]\n", key.section, key.key, n);
     201           0 :                     return false;
     202             :                 }
     203           0 :                 n++;
     204           0 :                 p = strchr(p,']');
     205           0 :                 if (!p) {
     206           0 :                     return false;
     207             :                 }
     208           0 :                 p++;
     209           0 :                 if (p[0] != ',') {
     210           0 :                     break;
     211             :                 }
     212           0 :                 if (p[1] != ' ') {
     213           0 :                     return false;
     214             :                 }
     215           0 :                 p += 2;
     216           0 :             }
     217           0 :             if (p[0] != ']') {
     218           0 :                 return false;
     219             :             }
     220           0 :             v->length = n;
     221           0 :             break;
     222             :         }
     223             : 
     224           0 :         case DATA_FLOAT_ARRAY: {
     225             :             // example: [18.0, 12.694079399108887]
     226           0 :             if (*p++ != '[') {
     227           0 :                 return false;
     228             :             }
     229           0 :             uint16_t n = 0;
     230           0 :             struct float_array *v = (struct float_array *)key.ptr;
     231             :             while (true) {
     232           0 :                 if (n >= v->length) {
     233           0 :                     float *d = (float *)realloc(v->data, sizeof(float)*(n+1));
     234           0 :                     if (d == nullptr) {
     235           0 :                         return false;
     236             :                     }
     237           0 :                     v->data = d;
     238           0 :                     v->length = n+1;
     239             :                 }
     240           0 :                 v->data[n] = strtof(p, nullptr);
     241           0 :                 n++;
     242           0 :                 p = strchr(p,',');
     243           0 :                 if (!p) {
     244           0 :                     break;
     245             :                 }
     246           0 :                 p++;
     247           0 :             }
     248           0 :             v->length = n;
     249           0 :             break;
     250             :         }
     251             :         }
     252             :     }
     253           0 :     socket_frame_counter++;
     254           0 :     return true;
     255             : }
     256             : 
     257             : /*
     258             :   connect to the required sockets
     259             :  */
     260           0 : bool Morse::connect_sockets(void)
     261             : {
     262           0 :     if (!sensors_sock) {
     263           0 :         sensors_sock = NEW_NOTHROW SocketAPM_native(false);
     264           0 :         if (!sensors_sock) {
     265           0 :             AP_HAL::panic("Out of memory for sensors socket");
     266             :         }
     267           0 :         if (!sensors_sock->connect(morse_ip, morse_sensors_port)) {
     268           0 :             usleep(100000);
     269           0 :             if (connect_counter++ == 20) {
     270           0 :                 printf("Waiting to connect to sensors control on %s:%u\n",
     271           0 :                        morse_ip, morse_sensors_port);
     272           0 :                 connect_counter = 0;
     273             :             }
     274           0 :             delete sensors_sock;
     275           0 :             sensors_sock = nullptr;
     276           0 :             return false;
     277             :         }
     278           0 :         sensors_sock->reuseaddress();
     279           0 :         printf("Sensors connected\n");
     280             :     }
     281           0 :     if (!control_sock) {
     282           0 :         control_sock = NEW_NOTHROW SocketAPM_native(false);
     283           0 :         if (!control_sock) {
     284           0 :             AP_HAL::panic("Out of memory for control socket");
     285             :         }
     286           0 :         if (!control_sock->connect(morse_ip, morse_control_port)) {
     287           0 :             usleep(100000);
     288           0 :             if (connect_counter++ == 20) {
     289           0 :                 printf("Waiting to connect to control control on %s:%u\n",
     290           0 :                        morse_ip, morse_control_port);
     291           0 :                 connect_counter = 0;
     292             :             }
     293           0 :             delete control_sock;
     294           0 :             control_sock = nullptr;
     295           0 :             return false;
     296             :         }
     297           0 :         control_sock->reuseaddress();
     298           0 :         printf("Control connected\n");
     299             :     }
     300           0 :     return true;
     301             : }
     302             : 
     303             : /*
     304             :   get any new data from the sensors socket
     305             : */
     306           0 : bool Morse::sensors_receive(void)
     307             : {
     308           0 :     ssize_t ret = sensors_sock->recv(&sensor_buffer[sensor_buffer_len], sizeof(sensor_buffer)-sensor_buffer_len, 0);
     309           0 :     if (ret <= 0) {
     310           0 :         no_data_counter++;
     311           0 :         if (no_data_counter == 1000) {
     312           0 :             no_data_counter = 0;
     313           0 :             delete sensors_sock;
     314           0 :             delete control_sock;
     315           0 :             sensors_sock = nullptr;
     316           0 :             control_sock = nullptr;
     317             :         }
     318           0 :         return false;
     319             :     }
     320           0 :     no_data_counter = 0;
     321             : 
     322             :     // convert '\n' into nul
     323           0 :     while (uint8_t *p = (uint8_t *)memchr(&sensor_buffer[sensor_buffer_len], '\n', ret)) {
     324           0 :         *p = 0;
     325           0 :     }
     326           0 :     sensor_buffer_len += ret;
     327             : 
     328           0 :     const uint8_t *p2 = (const uint8_t *)memrchr(sensor_buffer, 0, sensor_buffer_len);
     329           0 :     if (p2 == nullptr || p2 == sensor_buffer) {
     330           0 :         return false;
     331             :     }
     332           0 :     const uint8_t *p1 = (const uint8_t *)memrchr(sensor_buffer, 0, p2 - sensor_buffer);
     333           0 :     if (p1 == nullptr) {
     334           0 :         return false;
     335             :     }
     336             : 
     337           0 :     bool parse_ok = parse_sensors((const char *)(p1+1));
     338             : 
     339           0 :     memmove(sensor_buffer, p2, sensor_buffer_len - (p2 - sensor_buffer));
     340           0 :     sensor_buffer_len = sensor_buffer_len - (p2 - sensor_buffer);
     341             : 
     342           0 :     return parse_ok;
     343             : }
     344             : 
     345             : /*
     346             :   output control command assuming steering/throttle rover
     347             :  */
     348           0 : void Morse::output_rover_regular(const struct sitl_input &input)
     349             : {
     350           0 :     float throttle = 2*((input.servos[2]-1000)/1000.0f - 0.5f);
     351           0 :     float ground_steer = 2*((input.servos[0]-1000)/1000.0f - 0.5f);
     352           0 :     float max_steer = radians(60);
     353           0 :     float max_speed = 20;
     354           0 :     float max_accel = 20;
     355             : 
     356             :      // speed in m/s in body frame
     357           0 :     Vector3f velocity_body = dcm.transposed() * velocity_ef;
     358             : 
     359             :     // speed along x axis, +ve is forward
     360           0 :     float speed = velocity_body.x;
     361             : 
     362             :     // target speed with current throttle
     363           0 :     float target_speed = throttle * max_speed;
     364             : 
     365             :     // linear acceleration in m/s/s - very crude model
     366           0 :     float accel = max_accel * (target_speed - speed) / max_speed;
     367             : 
     368             :     //force directly proportion to acceleration 
     369           0 :     float force = accel;
     370             : 
     371           0 :     float steer = ground_steer * max_steer;
     372             : 
     373             :     // construct a JSON packet for steer/force
     374             :     char buf[60];
     375           0 :     snprintf(buf, sizeof(buf)-1, "{\"steer\": %.3f, \"force\": %.2f, \"brake\": %.2f}\n",
     376           0 :              steer, -force, 0.0);
     377           0 :     buf[sizeof(buf)-1] = 0;
     378             : 
     379           0 :     control_sock->send(buf, strlen(buf));
     380           0 : }
     381             : /*
     382             :   output control command assuming skid-steering rover
     383             :  */
     384           0 : void Morse::output_rover_skid(const struct sitl_input &input)
     385             : {
     386           0 :     float motor1 = 2*((input.servos[0]-1000)/1000.0f - 0.5f);
     387           0 :     float motor2 = 2*((input.servos[2]-1000)/1000.0f - 0.5f);
     388           0 :     const float steer_rate_max_dps = 60;
     389           0 :     const float max_speed = 2;
     390             : 
     391           0 :     const float steering_rps = (motor1 - motor2) * radians(steer_rate_max_dps);
     392           0 :     const float speed_ms = 0.5*(motor1 + motor2) * max_speed;
     393             : 
     394             :     // construct a JSON packet for v and w
     395             :     char buf[60];
     396           0 :     snprintf(buf, sizeof(buf)-1, "{\"v\": %.3f, \"w\": %.2f}\n",
     397           0 :              speed_ms, -steering_rps);
     398           0 :     buf[sizeof(buf)-1] = 0;
     399             : 
     400           0 :     control_sock->send(buf, strlen(buf));
     401           0 : }
     402             : 
     403             : /*
     404             :   output control command assuming a 4 channel quad
     405             :  */
     406           0 : void Morse::output_quad(const struct sitl_input &input)
     407             : {
     408           0 :     const float max_thrust = 1500;
     409             :     float motors[4];
     410           0 :     for (uint8_t i=0; i<4; i++) {
     411           0 :         motors[i] = constrain_float(((input.servos[i]-1000)/1000.0f) * max_thrust, 0, max_thrust);
     412             :     }
     413           0 :     const float &m_right = motors[0];
     414           0 :     const float &m_left  = motors[1];
     415           0 :     const float &m_front = motors[2];
     416           0 :     const float &m_back  = motors[3];
     417             : 
     418             :     // quad format in Morse is:
     419             :     // m1: back
     420             :     // m2: right
     421             :     // m3: front
     422             :     // m4: left
     423             : 
     424             :     // construct a JSON packet for motors
     425             :     char buf[60];
     426           0 :     snprintf(buf, sizeof(buf)-1, "{\"engines\": [%.3f, %.3f, %.3f, %.3f]}\n",
     427           0 :              m_back, m_right, m_front, m_left);
     428           0 :     buf[sizeof(buf)-1] = 0;
     429             : 
     430           0 :     control_sock->send(buf, strlen(buf));
     431           0 : }
     432             : 
     433             : /*
     434             :   output all 16 channels as PWM values. This allows for general
     435             :   control of a robot
     436             :  */
     437           0 : void Morse::output_pwm(const struct sitl_input &input)
     438             : {
     439             :     char buf[200];
     440           0 :     snprintf(buf, sizeof(buf)-1, "{\"pwm\": [%u, %uf, %u, %u, %u, %uf, %u, %u, %u, %uf, %u, %u, %u, %uf, %u, %u]}\n",
     441           0 :              input.servos[0], input.servos[1], input.servos[2], input.servos[3],
     442           0 :              input.servos[4], input.servos[5], input.servos[6], input.servos[7],
     443           0 :              input.servos[8], input.servos[9], input.servos[10], input.servos[11],
     444           0 :              input.servos[12], input.servos[13], input.servos[14], input.servos[15]);
     445           0 :     buf[sizeof(buf)-1] = 0;
     446           0 :     control_sock->send(buf, strlen(buf));
     447           0 : }
     448             : 
     449             : 
     450             : /*
     451             :   update the Morse simulation by one time step
     452             :  */
     453           0 : void Morse::update(const struct sitl_input &input)
     454             : {
     455           0 :     update_battery();
     456           0 :     if (!connect_sockets()) {
     457           0 :         return;
     458             :     }
     459             : 
     460           0 :     last_state = state;
     461             : 
     462           0 :     if (sensors_receive()) {
     463             :         // update average frame time used for extrapolation
     464           0 :         double dt = constrain_float(state.timestamp - last_state.timestamp, 0.001, 1.0/50);
     465           0 :         if (average_frame_time_s < 1.0e-6) {
     466           0 :             average_frame_time_s = dt;
     467             :         }
     468           0 :         average_frame_time_s = average_frame_time_s * 0.98 + dt * 0.02;
     469             :     }
     470             : 
     471           0 :     double dt_s = state.timestamp - last_state.timestamp;
     472           0 :     if (dt_s < 0 || dt_s > 1) {
     473             :         // cope with restarting while connected
     474           0 :         initial_time_s = time_now_us * 1.0e-6f;
     475           0 :         last_time_s = state.timestamp;
     476           0 :         return;
     477             :     }
     478             : 
     479           0 :     if (dt_s < 0.00001f) {
     480           0 :         float delta_time = 0.001;
     481             :         // don't go past the next expected frame
     482           0 :         if (delta_time + extrapolated_s > average_frame_time_s) {
     483           0 :             delta_time = average_frame_time_s - extrapolated_s;
     484             :         }
     485           0 :         if (delta_time <= 0) {
     486           0 :             usleep(1000);
     487           0 :             return;
     488             :         }
     489           0 :         time_now_us += delta_time * 1.0e6;
     490           0 :         extrapolate_sensors(delta_time);
     491           0 :         update_position();
     492           0 :         update_mag_field_bf();
     493           0 :         usleep(delta_time*1.0e6);
     494           0 :         extrapolated_s += delta_time;
     495           0 :         report_FPS();
     496           0 :         return;
     497             :     }
     498             : 
     499           0 :     extrapolated_s = 0;
     500             :     
     501           0 :     if (initial_time_s <= 0) {
     502           0 :         dt_s = 0.001f;
     503           0 :         initial_time_s = state.timestamp - dt_s;
     504             :     }
     505             : 
     506             :     // convert from state variables to ardupilot conventions
     507           0 :     dcm.from_euler(state.pose.roll, -state.pose.pitch, -state.pose.yaw);
     508             : 
     509           0 :     gyro = Vector3f(state.imu.angular_velocity[0],
     510           0 :                     -state.imu.angular_velocity[1],
     511           0 :                     -state.imu.angular_velocity[2]);
     512             : 
     513           0 :     velocity_ef = Vector3f(state.velocity.world_linear_velocity[0],
     514           0 :                            -state.velocity.world_linear_velocity[1],
     515           0 :                            -state.velocity.world_linear_velocity[2]);
     516             : 
     517           0 :     position = Vector3d(state.gps.x, -state.gps.y, -state.gps.z);
     518           0 :     position.xy() += origin.get_distance_NE_double(home);
     519             : 
     520             :     // Morse IMU accel is NEU, convert to NED
     521           0 :     accel_body = Vector3f(state.imu.linear_acceleration[0],
     522           0 :                           -state.imu.linear_acceleration[1],
     523           0 :                           -state.imu.linear_acceleration[2]);
     524             : 
     525             :     // limit to 16G to match pixhawk1
     526           0 :     float a_limit = GRAVITY_MSS*16;
     527           0 :     accel_body.x = constrain_float(accel_body.x, -a_limit, a_limit);
     528           0 :     accel_body.y = constrain_float(accel_body.y, -a_limit, a_limit);
     529           0 :     accel_body.z = constrain_float(accel_body.z, -a_limit, a_limit);
     530             : 
     531             :     // fill in laser scanner results, if available
     532           0 :     scanner.points = state.scanner.points;
     533           0 :     scanner.ranges = state.scanner.ranges;
     534             : 
     535           0 :     update_position();
     536           0 :     time_advance();
     537           0 :     uint64_t new_time_us = (state.timestamp - initial_time_s)*1.0e6;
     538           0 :     if (new_time_us < time_now_us) {
     539           0 :         uint64_t dt_us = time_now_us - new_time_us;
     540           0 :         if (dt_us > 500000) {
     541             :             // time going backwards
     542           0 :             time_now_us = new_time_us;
     543             :         }
     544             :     } else {
     545           0 :         time_now_us = new_time_us;
     546             :     }
     547             : 
     548           0 :     last_time_s = state.timestamp;
     549             : 
     550             :     // update magnetic field
     551           0 :     update_mag_field_bf();
     552             : 
     553           0 :     switch (output_type) {
     554           0 :     case OUTPUT_ROVER_REGULAR:
     555           0 :         output_rover_regular(input);
     556           0 :         break;
     557           0 :     case OUTPUT_ROVER_SKID:
     558           0 :         output_rover_skid(input);
     559           0 :         break;
     560           0 :     case OUTPUT_QUAD:
     561           0 :         output_quad(input);
     562           0 :         break;
     563           0 :     case OUTPUT_PWM:
     564           0 :         output_pwm(input);
     565           0 :         break;
     566             :     }
     567             : 
     568           0 :     report_FPS();
     569             : 
     570             : #if HAL_GCS_ENABLED
     571           0 :     send_report();
     572             : #endif
     573             : }
     574             : 
     575             : 
     576             : /*
     577             :   report frame rates
     578             :  */
     579           0 : void Morse::report_FPS(void)
     580             : {
     581           0 :     if (frame_counter++ % 1000 == 0) {
     582           0 :         if (!is_zero(last_frame_count_s)) {
     583           0 :             uint64_t frames = socket_frame_counter - last_socket_frame_counter;
     584           0 :             last_socket_frame_counter = socket_frame_counter;
     585           0 :             double dt = state.timestamp - last_frame_count_s;
     586           0 :             printf("%.2f/%.2f FPS avg=%.2f\n",
     587           0 :                    frames / dt, 1000 / dt, 1.0/average_frame_time_s);
     588             :         } else {
     589           0 :             printf("Initial position %f %f %f\n", position.x, position.y, position.z);
     590             :         }
     591           0 :         last_frame_count_s = state.timestamp;
     592             :     }
     593           0 : }
     594             : 
     595             : 
     596             : 
     597             : #if HAL_GCS_ENABLED
     598             : /*
     599             :   send a report to the vehicle control code over MAVLink
     600             : */
     601           0 : void Morse::send_report(void)
     602             : {
     603           0 :     const uint32_t now = AP_HAL::millis();
     604             : #if defined(__CYGWIN__) || defined(__CYGWIN64__)
     605             :     if (now < 10000) {
     606             :         // don't send lidar reports until 10s after startup. This
     607             :         // avoids a windows threading issue with non-blocking sockets
     608             :         // and the initial wait on SERIAL0
     609             :         return;
     610             :     }
     611             : #endif
     612             : 
     613             :     // this is usually loopback
     614           0 :     if (!mavlink.connected && mav_socket.connect(mavlink_loopback_address, mavlink_loopback_port)) {
     615           0 :         ::printf("Morse MAVLink loopback connected to %s:%u\n", mavlink_loopback_address, (unsigned)mavlink_loopback_port);
     616           0 :         mavlink.connected = true;
     617             :     }
     618           0 :     if (!mavlink.connected) {
     619           0 :         return;
     620             :     }
     621             : 
     622             :     // send a OBSTACLE_DISTANCE messages at 15 Hz
     623           0 :     if (now - send_report_last_ms >= (1000/15) && scanner.points.length == scanner.ranges.length && scanner.points.length > 0) {
     624           0 :         send_report_last_ms = now;
     625             : 
     626           0 :         mavlink_obstacle_distance_t packet {};
     627           0 :         packet.time_usec = AP_HAL::micros64();
     628           0 :         packet.min_distance = 1;
     629             :         // the simulated rangefinder has an imposed 18m limit in
     630             :         // e.g. rover_scanner.py
     631           0 :         packet.max_distance = 5000;
     632           0 :         packet.sensor_type = MAV_DISTANCE_SENSOR_LASER;
     633           0 :         packet.increment = 0; // use increment_f
     634             : 
     635           0 :         packet.angle_offset = 180;
     636           0 :         packet.increment_f = (-5);  // NOTE! This is negative because the distances[] arc is counter-clockwise
     637             : 
     638           0 :         for (uint8_t i=0; i<MAVLINK_MSG_OBSTACLE_DISTANCE_FIELD_DISTANCES_LEN; i++) {
     639             : 
     640           0 :             if (i >= scanner.points.length) {
     641           0 :                 packet.distances[i] = 65535;
     642           0 :                 continue;
     643             :             }
     644             : 
     645             :             // convert m to cm and sanity check
     646           0 :             const Vector2f v = Vector2f(scanner.points.data[i].x, scanner.points.data[i].y);
     647           0 :             const float distance_cm = v.length()*100;
     648           0 :             if (distance_cm < packet.min_distance) {
     649           0 :                 packet.distances[i] = packet.max_distance + 1; // "no obstacle"
     650           0 :                 continue;
     651             :             }
     652             : 
     653           0 :             if (distance_cm > packet.max_distance) {
     654           0 :                 packet.distances[i] = packet.max_distance + 1; // "no obstacle"
     655           0 :                 continue;
     656             :             }
     657             : 
     658           0 :             packet.distances[i] = distance_cm;
     659             :         }
     660             : 
     661             :         mavlink_message_t msg;
     662           0 :         uint16_t len = mavlink_msg_obstacle_distance_encode_status(
     663           0 :                 mavlink_system.sysid,
     664             :                 13,
     665             :                 &mavlink.status,
     666             :                 &msg, &packet);
     667             : 
     668           0 :         uint8_t msgbuf[len];
     669           0 :         len = mavlink_msg_to_send_buffer(msgbuf, &msg);
     670           0 :         if (len > 0) {
     671           0 :             mav_socket.send(msgbuf, len);
     672             :         }
     673           0 :     }
     674             : 
     675             : }
     676             : #endif  // HAL_GCS_ENABLED
     677             : 
     678             : #endif  // AP_SIM_MORSE_ENABLED

Generated by: LCOV version 1.14