LCOV - code coverage report
Current view: top level - SITL - SIM_AirSim.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 191 0.0 %
Date: 2026-09-09 11:02:08 Functions: 0 9 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 AirSim
      17             : */
      18             : 
      19             : #include "SIM_config.h"
      20             : 
      21             : #if AP_SIM_AIRSIM_ENABLED
      22             : 
      23             : #include "SIM_AirSim.h"
      24             : 
      25             : #include <stdio.h>
      26             : #include <arpa/inet.h>
      27             : #include <errno.h>
      28             : 
      29             : #include <AP_HAL/AP_HAL.h>
      30             : #include <AP_Logger/AP_Logger.h>
      31             : #include <AP_HAL/utility/replace.h>
      32             : 
      33             : #define UDP_TIMEOUT_MS 100
      34             : 
      35             : extern const AP_HAL::HAL& hal;
      36             : 
      37             : using namespace SITL;
      38             : 
      39           0 : AirSim::AirSim(const char *frame_str) :
      40             :         Aircraft(frame_str),
      41           0 :         sock(true)
      42             : {
      43           0 :     if (strstr(frame_str, "-copter")) {
      44           0 :         output_type = OutputType::Copter;
      45           0 :     } else if (strstr(frame_str, "-rover")) {
      46           0 :         output_type = OutputType::Rover;
      47             :     } else {
      48             :         // default to copter
      49           0 :         output_type = OutputType::Copter;
      50             :     }
      51             : 
      52           0 :         printf("Starting SITL Airsim type %u\n", (unsigned)output_type);
      53           0 : }
      54             : 
      55             : /*
      56             :         Create & set in/out socket
      57             : */
      58           0 : void AirSim::set_interface_ports(const char* address, const int port_in, const int port_out)
      59             : {
      60             :     static const char *port_in_addr = "0.0.0.0";
      61             : 
      62           0 :     if (!sock.bind(port_in_addr, port_in)) {
      63           0 :                 printf("Unable to bind Airsim sensor_in socket at port %u - Error: %s\n",
      64           0 :                                  port_in, strerror(errno));
      65           0 :                 return;
      66             :         }
      67           0 :         printf("Bind SITL sensor input at %s:%u\n", port_in_addr, port_in);
      68           0 :         sock.set_blocking(false);
      69           0 :         sock.reuseaddress();
      70             : 
      71           0 :         airsim_ip = address;
      72           0 :         airsim_control_port = port_out;
      73           0 :         airsim_sensor_port = port_in;
      74             : 
      75           0 :         printf("AirSim control interface set to %s:%u\n", airsim_ip, airsim_control_port);
      76             : }
      77             : 
      78             : /*
      79             :         Decode and send servos
      80             : */
      81           0 : void AirSim::output_copter(const sitl_input& input)
      82             : {
      83             :     servo_packet pkt;
      84             : 
      85           0 :         for (uint8_t i=0; i<kArduCopterRotorControlCount; i++) {
      86           0 :                 pkt.pwm[i] = input.servos[i];
      87             :         }
      88             : 
      89           0 :         ssize_t send_ret = sock.sendto(&pkt, sizeof(pkt), airsim_ip, airsim_control_port);
      90           0 :         if (send_ret != sizeof(pkt)) {
      91           0 :                 if (send_ret <= 0) {
      92           0 :                         printf("Unable to send servo output to %s:%u - Error: %s, Return value: %ld\n",
      93           0 :                    airsim_ip, airsim_control_port, strerror(errno), (long)send_ret);
      94             :                 } else {
      95           0 :                         printf("Sent %ld bytes instead of %lu bytes\n", (long)send_ret, (unsigned long)sizeof(pkt));
      96             :                 }
      97             :         }
      98           0 : }
      99             : 
     100           0 : void AirSim::output_rover(const sitl_input& input)
     101             : {
     102             :     rover_packet pkt;
     103             : 
     104           0 :     pkt.steering = 2*((input.servos[0]-1000)/1000.0f - 0.5f);
     105           0 :     pkt.throttle = 2*((input.servos[2]-1000)/1000.0f - 0.5f);
     106             : 
     107           0 :     ssize_t send_ret = sock.sendto(&pkt, sizeof(pkt), airsim_ip, airsim_control_port);
     108           0 :     if (send_ret != sizeof(pkt)) {
     109           0 :         if (send_ret <= 0) {
     110           0 :             printf("Unable to send control output to %s:%u - Error: %s, Return value: %ld\n",
     111           0 :                      airsim_ip, airsim_control_port, strerror(errno), (long)send_ret);
     112             :         } else {
     113           0 :             printf("Sent %ld bytes instead of %lu bytes\n", (long)send_ret, (unsigned long)sizeof(pkt));
     114             :         }
     115             :     }
     116           0 : }
     117             : 
     118             : /*
     119             :     Wrapper function to send servo output
     120             : */
     121           0 : void AirSim::output_servos(const sitl_input& input)
     122             : {
     123           0 :     switch (output_type) {
     124           0 :     case OutputType::Copter:
     125           0 :         output_copter(input);
     126           0 :         break;
     127             : 
     128           0 :     case OutputType::Rover:
     129           0 :         output_rover(input);
     130           0 :         break;
     131             :     }
     132           0 : }
     133             : 
     134             : /*
     135             :   very simple JSON parser for sensor data
     136             :   called with pointer to one row of sensor data, nul terminated
     137             : 
     138             :   This parser does not do any syntax checking, and is not at all
     139             :   general purpose
     140             : */
     141           0 : bool AirSim::parse_sensors(const char *json)
     142             : {
     143             :     // printf("%s\n", json);
     144           0 :     for (uint16_t i=0; i<ARRAY_SIZE(keytable); i++) {
     145           0 :         struct keytable &key = keytable[i];
     146             : 
     147             :         /* look for section header */
     148           0 :         const char *p = strstr(json, key.section);
     149           0 :         if (!p) {
     150             :             // we don't have this sensor
     151           0 :             continue;
     152             :         }
     153           0 :         p += strlen(key.section)+1;
     154             : 
     155             :         // find key inside section
     156           0 :         p = strstr(p, key.key);
     157           0 :         if (!p) {
     158           0 :             printf("Failed to find key %s/%s\n", key.section, key.key);
     159           0 :             return false;
     160             :         }
     161             : 
     162           0 :         p += strlen(key.key)+3;
     163           0 :         switch (key.type) {
     164           0 :             case DATA_UINT64:
     165           0 :                 *((uint64_t *)key.ptr) = strtoul(p, nullptr, 10);
     166           0 :                 break;
     167             : 
     168           0 :             case DATA_FLOAT:
     169           0 :                 *((float *)key.ptr) = strtof(p, nullptr);
     170           0 :                 break;
     171             : 
     172           0 :             case DATA_DOUBLE:
     173           0 :                 *((double *)key.ptr) = atof(p);
     174           0 :                 break;
     175             : 
     176           0 :             case DATA_VECTOR3F: {
     177           0 :                 Vector3f *v = (Vector3f *)key.ptr;
     178           0 :                 if (sscanf(p, "[%f, %f, %f]", &v->x, &v->y, &v->z) != 3) {
     179           0 :                     printf("Failed to parse Vector3f for %s/%s\n", key.section, key.key);
     180           0 :                     return false;
     181             :                 }
     182           0 :                 break;
     183             :             }
     184             : 
     185           0 :             case DATA_VECTOR3F_ARRAY: {
     186             :                 // - array of floats that represent [x,y,z] coordinate for each point hit within the range
     187             :                 //       x0, y0, z0, x1, y1, z1, ..., xn, yn, zn
     188             :                 // example: [23.1,0.677024,1.4784,-8.97607135772705,-8.976069450378418,-8.642673492431641e-07,]
     189           0 :                 if (*p++ != '[') {
     190           0 :                     return false;
     191             :                 }
     192           0 :                 uint16_t n = 0;
     193           0 :                 vector3f_array *v = (vector3f_array *)key.ptr;
     194             :                 while (true) {
     195           0 :                     if (n >= v->length) {
     196           0 :                         Vector3f *d = (Vector3f *)realloc(v->data, sizeof(Vector3f)*(n+1));
     197           0 :                         if (d == nullptr) {
     198           0 :                             return false;
     199             :                         }
     200           0 :                         v->data = d;
     201           0 :                         v->length = n+1;
     202             :                     }
     203           0 :                     if (sscanf(p, "%f,%f,%f,", &v->data[n].x, &v->data[n].y, &v->data[n].z) != 3) {
     204           0 :                         printf("Failed to parse Vector3f for %s/%s[%u]\n", key.section, key.key, n);
     205           0 :                         return false;
     206             :                     }
     207           0 :                     n++;
     208             :                     // Goto 3rd occurence of ,
     209           0 :                     p = strchr(p,',');
     210           0 :                     if (!p) {
     211           0 :                         return false;
     212             :                     }
     213           0 :                     p++;
     214           0 :                     p = strchr(p,',');
     215           0 :                     if (!p) {
     216           0 :                         return false;
     217             :                     }
     218           0 :                     p++;
     219           0 :                     p = strchr(p,',');
     220           0 :                     if (!p) {
     221           0 :                         return false;
     222             :                     }
     223           0 :                     p++;
     224             :                     // Reached end of point cloud
     225           0 :                     if (p[0] == ']') {
     226           0 :                         break;
     227             :                     }
     228           0 :                 }
     229           0 :                 v->length = n;
     230           0 :                 break;
     231             :             }
     232             : 
     233           0 :             case DATA_FLOAT_ARRAY: {
     234             :                 // example: [18.0, 12.694079399108887]
     235           0 :                 if (*p++ != '[') {
     236           0 :                     return false;
     237             :                 }
     238           0 :                 uint16_t n = 0;
     239           0 :                 float_array *v = (float_array *)key.ptr;
     240             :                 while (true) {
     241           0 :                     if (n >= v->length) {
     242           0 :                         float *d = (float *)realloc(v->data, sizeof(float)*(n+1));
     243           0 :                         if (d == nullptr) {
     244           0 :                             return false;
     245             :                         }
     246           0 :                         v->data = d;
     247           0 :                         v->length = n+1;
     248             :                     }
     249           0 :                     v->data[n] = strtof(p, nullptr);
     250           0 :                     n++;
     251           0 :                     p = strchr(p,',');
     252           0 :                     if (!p) {
     253           0 :                         break;
     254             :                     }
     255           0 :                     p++;
     256           0 :                 }
     257           0 :                 v->length = n;
     258           0 :                 break;
     259             :             }
     260             :         }
     261             :     }
     262           0 :     return true;
     263             : }
     264             : 
     265             : /*
     266             :         Receive new sensor data from simulator
     267             :         This is a blocking function
     268             : */
     269           0 : void AirSim::recv_fdm(const sitl_input& input)
     270             : {
     271             :     // Receive sensor packet
     272           0 :     ssize_t ret = sock.recv(&sensor_buffer[sensor_buffer_len], sizeof(sensor_buffer)-sensor_buffer_len, UDP_TIMEOUT_MS);
     273           0 :     uint32_t wait_ms = UDP_TIMEOUT_MS;
     274           0 :     while (ret <= 0) {
     275             :         // printf("No sensor message received - %s\n", strerror(errno));
     276           0 :         ret = sock.recv(&sensor_buffer[sensor_buffer_len], sizeof(sensor_buffer)-sensor_buffer_len, UDP_TIMEOUT_MS);
     277           0 :         wait_ms += UDP_TIMEOUT_MS;
     278             : 
     279             :         // If no sensor message is received after 1 second, resend servos
     280             :         // this helps if messages are lost on the way, and both AP & Airsim are waiting for each ther
     281           0 :         if (wait_ms > 1000) {
     282           0 :             wait_ms = 0;
     283           0 :             printf("No sensor message received in last 1s, error - %s, resending servos\n", strerror(errno));
     284           0 :             output_servos(input);
     285             :         }
     286             :     }
     287             : 
     288             :     // convert '\n' into nul
     289           0 :     while (uint8_t *p = (uint8_t *)memchr(&sensor_buffer[sensor_buffer_len], '\n', ret)) {
     290           0 :         *p = 0;
     291           0 :     }
     292           0 :     sensor_buffer_len += ret;
     293             : 
     294           0 :     const uint8_t *p2 = (const uint8_t *)memrchr(sensor_buffer, 0, sensor_buffer_len);
     295           0 :     if (p2 == nullptr || p2 == sensor_buffer) {
     296           0 :         return;
     297             :     }
     298           0 :     const uint8_t *p1 = (const uint8_t *)memrchr(sensor_buffer, 0, p2 - sensor_buffer);
     299           0 :     if (p1 == nullptr) {
     300           0 :         return;
     301             :     }
     302             : 
     303           0 :     parse_sensors((const char *)(p1+1));
     304             : 
     305           0 :     memmove(sensor_buffer, p2, sensor_buffer_len - (p2 - sensor_buffer));
     306           0 :     sensor_buffer_len = sensor_buffer_len - (p2 - sensor_buffer);
     307             : 
     308           0 :     accel_body = state.imu.linear_acceleration;
     309           0 :     gyro = state.imu.angular_velocity;
     310           0 :     velocity_ef = state.velocity.world_linear_velocity;
     311             : 
     312           0 :     location = {
     313           0 :         int32_t(state.gps.lat * 1.0e7),
     314           0 :         int32_t(state.gps.lon * 1.0e7),
     315           0 :         int32_t(state.gps.alt * 100.0f),
     316             :         Location::AltFrame::ABSOLUTE
     317             :     };
     318             : 
     319           0 :     position = origin.get_distance_NED_double(location);
     320             : 
     321           0 :     dcm.from_euler(state.pose.roll, state.pose.pitch, state.pose.yaw);
     322             : 
     323           0 :     if (last_timestamp) {
     324           0 :         int deltat = state.timestamp - last_timestamp;
     325           0 :         time_now_us += deltat;
     326             : 
     327           0 :         if (deltat > 0 && deltat < 100000) {
     328           0 :             if (average_frame_time < 1) {
     329           0 :                 average_frame_time = deltat;
     330             :             }
     331           0 :             average_frame_time = average_frame_time * 0.98 + deltat * 0.02;
     332             :         }
     333             :     }
     334             : 
     335           0 :     scanner.points = state.lidar.points;
     336             : 
     337             :     // Update RC input, max 12 channels
     338           0 :     rcin_chan_count = MIN(state.rc.rc_channels.length, 12);
     339           0 :     for (uint8_t i=0; i < rcin_chan_count; i++) {
     340           0 :         rcin[i] = state.rc.rc_channels.data[i];
     341             :     }
     342             : 
     343             :     // Update Rangefinder data, max sensors limit as defined
     344           0 :     uint8_t rng_sensor_count = MIN(state.rng.rng_distances.length, ARRAY_SIZE(rangefinder_m));
     345           0 :     for (uint8_t i=0; i<rng_sensor_count; i++) {
     346           0 :         rangefinder_m[i] = state.rng.rng_distances.data[i];
     347             :     }
     348             : 
     349             :     // AirSim does not provide battery information, so use the standard battery model.
     350           0 :     update_battery();
     351             : 
     352             : #if 0
     353             : // @LoggerMessage: ASM1
     354             : // @Description: AirSim simulation data
     355             : // @Field: TimeUS: Time since system startup
     356             : // @Field: TUS: Simulation's timestamp
     357             : // @Field: R: Simulation's roll
     358             : // @Field: P: Simulation's pitch
     359             : // @Field: Y: Simulation's yaw
     360             : // @Field: GX: Simulated gyroscope, X-axis
     361             : // @Field: GY: Simulated gyroscope, Y-axis
     362             : // @Field: GZ: Simulated gyroscope, Z-axis
     363             :     AP::logger().WriteStreaming("ASM1", "TimeUS,TUS,R,P,Y,GX,GY,GZ",
     364             :                        "QQffffff",
     365             :                        AP_HAL::micros64(),
     366             :                        state.timestamp,
     367             :                        degrees(state.pose.roll),
     368             :                        degrees(state.pose.pitch),
     369             :                        degrees(state.pose.yaw),
     370             :                        degrees(gyro.x),
     371             :                        degrees(gyro.y),
     372             :                        degrees(gyro.z));
     373             : 
     374             :     Vector3f velocity_bf = dcm.transposed() * velocity_ef;
     375             : 
     376             : // @LoggerMessage: ASM2
     377             : // @Description: More AirSim simulation data
     378             : // @Field: TimeUS: Time since system startup
     379             : // @Field: AX: simulation's acceleration, X-axis
     380             : // @Field: AY: simulation's acceleration, Y-axis
     381             : // @Field: AZ: simulation's acceleration, Z-axis
     382             : // @Field: VX: simulation's velocity, X-axis
     383             : // @Field: VY: simulation's velocity, Y-axis
     384             : // @Field: VZ: simulation's velocity, Z-axis
     385             : // @Field: PX: simulation's position, X-axis
     386             : // @Field: PY: simulation's position, Y-axis
     387             : // @Field: PZ: simulation's position, Z-axis
     388             : // @Field: Alt: simulation's gps altitude
     389             : // @Field: SD: simulation's earth-frame speed-down
     390             :     AP::logger().WriteStreaming("ASM2", "TimeUS,AX,AY,AZ,VX,VY,VZ,PX,PY,PZ,Alt,SD",
     391             :                        "Qfffffffffff",
     392             :                        AP_HAL::micros64(),
     393             :                        accel_body.x,
     394             :                        accel_body.y,
     395             :                        accel_body.z,
     396             :                        velocity_bf.x,
     397             :                        velocity_bf.y,
     398             :                        velocity_bf.z,
     399             :                        position.x,
     400             :                        position.y,
     401             :                        position.z,
     402             :                        state.gps.alt,
     403             :                        velocity_ef.z);
     404             : #endif
     405             : 
     406           0 :     last_timestamp = state.timestamp;
     407             : }
     408             : 
     409             : /*
     410             :   update the AirSim simulation by one time step
     411             : */
     412           0 : void AirSim::update(const sitl_input& input)
     413             : {
     414             :     // Send servos to AirSim
     415           0 :     output_servos(input);
     416             : 
     417             :     // Receive sensor data
     418           0 :     recv_fdm(input);
     419             : 
     420             :     // update magnetic field
     421           0 :     update_mag_field_bf();
     422             : 
     423           0 :     report_FPS();
     424           0 : }
     425             : 
     426             : /*
     427             :   report frame rates
     428             :  */
     429           0 : void AirSim::report_FPS(void)
     430             : {
     431           0 :     if (frame_counter++ % 1000 == 0) {
     432           0 :         if (last_frame_count != 0) {
     433           0 :             printf("FPS avg=%.2f\n", 1.0e6/average_frame_time);
     434             :         }
     435           0 :         last_frame_count = state.timestamp;
     436             :     }
     437           0 : }
     438             : 
     439             : #endif  // AP_SIM_AIRSIM_ENABLED

Generated by: LCOV version 1.14