Created
December 10, 2025 18:05
-
-
Save max-verem/e30f94366b99315869fb870a648363be to your computer and use it in GitHub Desktop.
lab #8 proxy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #define DEFAULT_OUTPUT_PWM_FRAME_RATE 1000 | |
| #define DEFAULT_ATTITUDE 120.0 | |
| /* | |
| will listen for UDP packets: | |
| 9001 - Betaflight's PwmOut UDP link to RF9 | |
| 9002 - Betaflight's PwmOut UDP link to gazebo | |
| will send | |
| 127.0.0.1:9003 - FDM packets to Betaflight | |
| host:ip - PWM packets for ardupilot_gazebo plugin | |
| compile: | |
| sudo apt install libcjson-dev | |
| gcc -Wall -g -ggdb -O0 lab8_fdm_proxy_async.c -o lab8_fdm_proxy_async -lcjson -lm | |
| run: | |
| gz topic -t /world/iris_runway/model/iris_with_gimbal_lab2/model/gimbal/link/pitch_link/sensor/camera/image/enable_streaming -m gz.msgs.Boolean -p "data: 1" | |
| DISPLAY= gz sim -v4 -s -r --headless-rendering iris_runway_lab2.sdf | |
| sudo /usr/sbin/sysctl -w net.core.rmem_default=26214400 | |
| sudo /usr/sbin/sysctl -w net.core.rmem_max=26214400 | |
| ./lab8_fdm_proxy_async 10.1.5.65 9102 | |
| ./lab8_fdm_proxy_async 10.1.4.105 9102 | |
| */ | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <stdint.h> | |
| #include <inttypes.h> | |
| #include <unistd.h> | |
| #include <asm/termbits.h> | |
| #include <fcntl.h> | |
| #include <sys/ioctl.h> | |
| #include <sys/socket.h> | |
| #include <sys/timerfd.h> | |
| #include <sys/time.h> | |
| #include <pthread.h> | |
| #include <errno.h> | |
| #include <pthread.h> | |
| #include <arpa/inet.h> | |
| #include <netinet/in.h> | |
| #include <netinet/udp.h> | |
| #include <netinet/tcp.h> | |
| #include <netdb.h> | |
| #include <math.h> | |
| #include <cjson/cJSON.h> | |
| /* | |
| https://github.com/betaflight/betaflight/blob/4.5.3/src/main/target/SITL/target.h | |
| */ | |
| #define SIMULATOR_MAX_RC_CHANNELS 16 | |
| #define SIMULATOR_MAX_PWM_CHANNELS 16 | |
| typedef struct { | |
| double timestamp; // in seconds | |
| double imu_angular_velocity_rpy[3]; // rad/s -> range: +/- 8192; +/- 2000 deg/se | |
| double imu_linear_acceleration_xyz[3]; // m/s/s NED, body frame -> sim 1G = 9.80665, FC 1G = 256 | |
| double imu_orientation_quat[4]; //w, x, y, z | |
| double velocity_xyz[3]; // m/s, earth frame | |
| double position_xyz[3]; // meters, NED from origin | |
| double pressure; | |
| } fdm_packet; | |
| typedef struct { | |
| double timestamp; // in seconds | |
| uint16_t channels[SIMULATOR_MAX_RC_CHANNELS]; // RC channels | |
| } rc_packet; | |
| typedef struct { | |
| float motor_speed[4]; // normal: [0.0, 1.0], 3D: [-1.0, 1.0] | |
| } servo_packet; | |
| typedef struct { | |
| uint16_t motorCount; // Count of motor in the PWM output. | |
| float pwm_output_raw[SIMULATOR_MAX_PWM_CHANNELS]; // Raw PWM from 1100 to 1900 | |
| } servo_packet_raw; | |
| /* | |
| https://github.com/ArduPilot/ardupilot_gazebo/blob/main/include/ArduPilotPlugin.hh | |
| */ | |
| // The servo packet received from ArduPilot SITL. Defined in SIM_JSON.h. | |
| struct servo_packet_16 { | |
| uint16_t magic; // 18458 expected magic value | |
| uint16_t frame_rate; | |
| uint32_t frame_count; | |
| uint16_t pwm[16]; | |
| }; | |
| struct servo_packet_32 { | |
| uint16_t magic; // 29569 expected magic value | |
| uint16_t frame_rate; | |
| uint32_t frame_count; | |
| uint16_t pwm[32]; | |
| }; | |
| // ---------------------------------------------------------------- | |
| /* | |
| 127.0.0.1:9001 | |
| 127.0.0.1:9002 | |
| 127.0.0.1:9003 | |
| */ | |
| int create_udp_socket(char* addr, int port) | |
| { | |
| int fd, r; | |
| fd = socket(AF_INET, SOCK_DGRAM, 0); | |
| if(fd < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: socket(AF_INET, SOCK_DGRAM, 0) failed: r=%d (%s)\n", __FUNCTION__, r, strerror(r)); | |
| close(fd); | |
| return -r; | |
| }; | |
| if(port) | |
| { | |
| struct sockaddr_in s_addr = {0}; | |
| s_addr.sin_family = AF_INET; | |
| s_addr.sin_addr.s_addr = htonl(!addr ? INADDR_ANY : inet_addr(addr)); | |
| s_addr.sin_port = htons(port); | |
| r = bind(fd, (struct sockaddr *) &s_addr, sizeof(s_addr)); | |
| if(fd < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: bind(%s, %d) failed: r=%d (%s)\n", __FUNCTION__, | |
| inet_ntoa(s_addr.sin_addr), ntohs(s_addr.sin_port), r, strerror(r)); | |
| close(fd); | |
| return -r; | |
| }; | |
| }; | |
| return fd; | |
| }; | |
| static struct sockaddr_in s_addr_gazebo = {0}; | |
| static struct sockaddr_in s_addr_betaflight = {0}; | |
| static int | |
| output_pwm_frame_rate = DEFAULT_OUTPUT_PWM_FRAME_RATE, | |
| f_exit = 0; | |
| static int | |
| fd_in_pwm_betaflight_gazebo = -1, | |
| fd_in_pwm_betaflight_rf9 = -1, | |
| fd_out_pwm_gazebo = -1, | |
| fd_out_fdm_betaflight = -1; | |
| static struct | |
| { | |
| int64_t ts_ns; | |
| uint64_t | |
| in_pwm_betaflight_gazebo, | |
| in_pwm_betaflight_rf9, | |
| out_pwm_gazebo, | |
| in_fdm_gazebo, | |
| out_fdm_betaflight; | |
| } cnt_curr = {0}, cnt_prev = {0}, cnt_tmp = {0}; | |
| static void* proc_in_pwm_betaflight_rf9(void* p __attribute__((unused))); | |
| static void* proc_in_pwm_betaflight_gazebo(void* p __attribute__((unused))); | |
| static void* proc_out_pwm_gazebo(void* p __attribute__((unused))); | |
| static void* proc_in_fdm_gazebo(void* p __attribute__((unused))); | |
| static void in_pwm_timeout(int f_to_gazebo); | |
| static void in_pwm_packet(servo_packet_raw* rf9, servo_packet* gazebo); | |
| static int parse_json_fdm_packet(char *buf, fdm_packet *pkt); | |
| static fdm_packet last_fdm = {0}; | |
| static servo_packet last_servo_packet = {0}; | |
| static servo_packet_raw last_servo_packet_raw = {0}; | |
| static pthread_spinlock_t lock; | |
| int64_t get_clock_monotonic_raw_ns(); | |
| static double pkt_freq(int64_t t1, uint64_t v1, int64_t t2, uint64_t v2) | |
| { | |
| int64_t dT = t2 - t1; | |
| uint64_t dV = v2 - v1; | |
| if(!dT) return 0.0; | |
| return (dV * 10000000000LL / dT) / 10.0; | |
| }; | |
| // betaflight/src/main/sensors/barometer.c | |
| double pressureToAltitude(const double pressure) // result in cm (0.01m) | |
| { | |
| return (1.0 - pow(pressure / 101325.0, 0.190295)) * 4433000.0; | |
| } | |
| double altitude_to_pressure(const double altitude) | |
| { | |
| return 101325.0 * pow(1 - 2.25577e-05 * altitude, 5.25588); | |
| }; | |
| int main(int argc, char** argv) | |
| { | |
| pthread_t th_in_pwm_betaflight_rf9, th_in_pwm_betaflight_gazebo, th_out_pwm_gazebo, th_in_fdm_gazebo; | |
| if(0) | |
| { | |
| double a, p; | |
| double tests[] = {0.0, 0.1, 1.0, 10.0, 100.0, 1000.0, 2000.0, 4000.0, 11000, -1.0}; | |
| double *alts = tests; | |
| fprintf(stderr, "pressureToAltitude(%f)=%f\n", 101325.0, pressureToAltitude(101325.0)); | |
| fprintf(stderr, "pressureToAltitude(%f)=%f\n", 22632.1, pressureToAltitude(22632.1)); | |
| while(*alts >= 0.0) | |
| { | |
| p = altitude_to_pressure(*alts); | |
| a = pressureToAltitude(p); | |
| fprintf(stderr, "v=%f, p=%f, a=%f\n", *alts, p, a); | |
| alts++; | |
| }; | |
| exit(1); | |
| }; | |
| if(argc != 3) | |
| { | |
| fprintf(stderr, "Error: not enought arguments!\n\nUsage:\n\t%s <gazebo ip> <gazebo port>\n", argv[0]); | |
| return 1; | |
| }; | |
| s_addr_gazebo.sin_family = AF_INET; | |
| s_addr_gazebo.sin_addr.s_addr = inet_addr(argv[1]); | |
| s_addr_gazebo.sin_port = htons(atoi(argv[2])); | |
| s_addr_betaflight.sin_family = AF_INET; | |
| s_addr_betaflight.sin_addr.s_addr = inet_addr("127.0.0.1"); | |
| s_addr_betaflight.sin_port = htons(9003); | |
| fd_in_pwm_betaflight_rf9 = create_udp_socket(NULL, 9001); | |
| if(fd_in_pwm_betaflight_rf9 < 0) | |
| goto ex; | |
| fd_in_pwm_betaflight_gazebo = create_udp_socket(NULL, 9002); | |
| if(fd_in_pwm_betaflight_gazebo < 0) | |
| goto ex; | |
| fd_out_pwm_gazebo = create_udp_socket(NULL, 0); | |
| if(fd_out_pwm_gazebo < 0) | |
| goto ex; | |
| fd_out_fdm_betaflight = create_udp_socket(NULL, 0); | |
| if(fd_out_fdm_betaflight < 0) | |
| goto ex; | |
| pthread_spin_init(&lock, 0); | |
| pthread_create(&th_in_pwm_betaflight_rf9, NULL, proc_in_pwm_betaflight_rf9, NULL); | |
| pthread_create(&th_in_pwm_betaflight_gazebo, NULL, proc_in_pwm_betaflight_gazebo, NULL); | |
| pthread_create(&th_in_fdm_gazebo, NULL, proc_in_fdm_gazebo, NULL); | |
| pthread_create(&th_out_pwm_gazebo, NULL, proc_out_pwm_gazebo, NULL); | |
| while(!f_exit) | |
| { | |
| static int i = 0; | |
| static const char idle[] = "|/-\\"; | |
| usleep(250000); | |
| cnt_curr.ts_ns = get_clock_monotonic_raw_ns(); | |
| pthread_spin_lock(&lock); | |
| cnt_tmp = cnt_curr; | |
| pthread_spin_unlock(&lock); | |
| #define PKT_FREQ(V) pkt_freq(cnt_prev.ts_ns, cnt_prev.V, cnt_tmp.ts_ns, cnt_tmp.V) | |
| fprintf(stdout, "[%c]\n", idle[(i++) % (sizeof(idle) - 1)]); | |
| fprintf(stdout, " PWM PKT IN: %8" PRIu64 " / %8" PRIu64 " | PWM PKT OUT: %8" PRIu64 "\n", | |
| cnt_tmp.in_pwm_betaflight_gazebo, cnt_tmp.in_pwm_betaflight_rf9, cnt_tmp.out_pwm_gazebo); | |
| fprintf(stdout, " %7.2f Hz / %7.2f Hz | %7.2f Hz\n\n", | |
| PKT_FREQ(in_pwm_betaflight_gazebo), PKT_FREQ(in_pwm_betaflight_rf9), PKT_FREQ(out_pwm_gazebo)); | |
| fprintf(stdout, " IN PWM RAW: %10.3f %10.3f %10.3f %10.3f \n\n", | |
| last_servo_packet_raw.pwm_output_raw[0], last_servo_packet_raw.pwm_output_raw[1], | |
| last_servo_packet_raw.pwm_output_raw[2], last_servo_packet_raw.pwm_output_raw[3]); | |
| fprintf(stdout, " FDM PKT IN: %8" PRIu64 " | FDM PKT OUT: %8" PRIu64 "\n", | |
| cnt_tmp.in_fdm_gazebo, cnt_tmp.out_fdm_betaflight); | |
| fprintf(stdout, " %7.2f Hz | %7.2f Hz\n\n", | |
| PKT_FREQ(in_fdm_gazebo), PKT_FREQ(out_fdm_betaflight)); | |
| fprintf(stdout, " TIMESTAMP: %10.3f\n", last_fdm.timestamp); | |
| fprintf(stdout, " POS XYZ: [ %10.3f, %10.3f, %10.3f ]\n", | |
| last_fdm.position_xyz[0], last_fdm.position_xyz[1], last_fdm.position_xyz[2]); | |
| fprintf(stdout, " VEL XYZ: [ %10.3f, %10.3f, %10.3f ]\n", | |
| last_fdm.velocity_xyz[0], last_fdm.velocity_xyz[1], last_fdm.velocity_xyz[2]); | |
| fprintf(stdout, " QUAT WXYZ: [ %10.3f, %10.3f, %10.3f, %10.3f ]\n", | |
| last_fdm.imu_orientation_quat[0], last_fdm.imu_orientation_quat[1], last_fdm.imu_orientation_quat[2], last_fdm.imu_orientation_quat[3]); | |
| fprintf(stdout, " ACCEL XYZ: [ %10.3f, %10.3f, %10.3f ]\n", | |
| last_fdm.imu_linear_acceleration_xyz[0], last_fdm.imu_linear_acceleration_xyz[1], last_fdm.imu_linear_acceleration_xyz[2]); | |
| fprintf(stdout, " ANG VEL RPY: [ %10.3f, %10.3f, %10.3f ]\n", | |
| last_fdm.imu_angular_velocity_rpy[0], last_fdm.imu_angular_velocity_rpy[1], last_fdm.imu_angular_velocity_rpy[2]); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| fprintf(stdout, "\033[1A"); | |
| cnt_prev = cnt_tmp; | |
| }; | |
| shutdown(fd_in_pwm_betaflight_gazebo, SHUT_RDWR); | |
| shutdown(fd_in_pwm_betaflight_rf9, SHUT_RDWR); | |
| shutdown(fd_out_pwm_gazebo, SHUT_RDWR); | |
| shutdown(fd_out_fdm_betaflight, SHUT_RDWR); | |
| pthread_join(th_in_pwm_betaflight_rf9, NULL); | |
| pthread_join(th_in_pwm_betaflight_gazebo, NULL); | |
| pthread_join(th_out_pwm_gazebo, NULL); | |
| pthread_join(th_in_fdm_gazebo, NULL); | |
| pthread_spin_destroy(&lock); | |
| ex: | |
| if(fd_in_pwm_betaflight_gazebo > 0) close(fd_in_pwm_betaflight_gazebo); | |
| if(fd_in_pwm_betaflight_rf9 > 0) close(fd_in_pwm_betaflight_rf9); | |
| if(fd_out_pwm_gazebo > 0) close(fd_out_pwm_gazebo); | |
| if(fd_out_fdm_betaflight > 0) close(fd_out_fdm_betaflight); | |
| return 0; | |
| }; | |
| static void* proc_in_pwm_betaflight_rf9(void* p __attribute__((unused))) | |
| { | |
| int r; | |
| fd_set fds; | |
| char buf[2048]; | |
| struct timeval tv; | |
| while(!f_exit) | |
| { | |
| /* prepare to check if data present in buffer */ | |
| FD_ZERO (&fds); | |
| FD_SET (fd_in_pwm_betaflight_rf9, &fds); | |
| tv.tv_sec = 0; /* Timeout 'sec'. */ | |
| tv.tv_usec = 500000; /* Timeout 'usec'. */ | |
| /* check */ | |
| r = select(fd_in_pwm_betaflight_rf9 + 1, &fds, NULL, NULL, &tv); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: select() failed, r=%d (%s)", __FUNCTION__, r, strerror(r)); | |
| f_exit++; | |
| return NULL; | |
| }; | |
| if(!r) | |
| { | |
| in_pwm_timeout(0); | |
| continue; | |
| }; | |
| r = recv(fd_in_pwm_betaflight_rf9, buf, sizeof(buf), 0); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: recv() failed, r=%d (%s)", __FUNCTION__, r, strerror(r)); | |
| f_exit++; | |
| return NULL; | |
| }; | |
| if(sizeof(servo_packet_raw) != r) | |
| { | |
| fprintf(stderr, "%s: received %d bytes, sizeof(servo_packet_raw) == %lu", __FUNCTION__, r, sizeof(servo_packet_raw)); | |
| continue; | |
| }; | |
| // here we process a packets sent to RF9 from Betaflight | |
| in_pwm_packet((servo_packet_raw*)buf, NULL); | |
| cnt_curr.in_pwm_betaflight_rf9++; | |
| }; | |
| return NULL; | |
| }; | |
| static void* proc_in_pwm_betaflight_gazebo(void* p __attribute__((unused))) | |
| { | |
| int r; | |
| fd_set fds; | |
| char buf[2048]; | |
| struct timeval tv; | |
| while(!f_exit) | |
| { | |
| /* prepare to check if data present in buffer */ | |
| FD_ZERO (&fds); | |
| FD_SET (fd_in_pwm_betaflight_gazebo, &fds); | |
| tv.tv_sec = 0; /* Timeout 'sec'. */ | |
| tv.tv_usec = 500000; /* Timeout 'usec'. */ | |
| /* check */ | |
| r = select(fd_in_pwm_betaflight_gazebo + 1, &fds, NULL, NULL, &tv); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: select() failed, r=%d (%s)", __FUNCTION__, r, strerror(r)); | |
| f_exit++; | |
| return NULL; | |
| }; | |
| if(!r) | |
| { | |
| in_pwm_timeout(1); | |
| continue; | |
| }; | |
| r = recv(fd_in_pwm_betaflight_gazebo, buf, sizeof(buf), 0); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: recv() failed, r=%d (%s)", __FUNCTION__, r, strerror(r)); | |
| f_exit++; | |
| return NULL; | |
| }; | |
| if(sizeof(servo_packet) != r) | |
| { | |
| fprintf(stderr, "%s: received %d bytes, sizeof(servo_packet) == %lu", __FUNCTION__, r, sizeof(servo_packet)); | |
| continue; | |
| }; | |
| // here we process a packets sent to Gazebo from Betaflight | |
| in_pwm_packet(NULL, (servo_packet*)buf); | |
| cnt_curr.in_pwm_betaflight_gazebo++; | |
| }; | |
| return NULL; | |
| }; | |
| static void* proc_in_fdm_gazebo(void* p __attribute__((unused))) | |
| { | |
| int r, fd_in_fdm_gazebo = fd_out_pwm_gazebo; | |
| fd_set fds; | |
| char buf[2048]; | |
| struct timeval tv; | |
| while(!f_exit) | |
| { | |
| fdm_packet pkt = {0}; | |
| /* prepare to check if data present in buffer */ | |
| FD_ZERO (&fds); | |
| FD_SET (fd_in_fdm_gazebo, &fds); | |
| tv.tv_sec = 0; /* Timeout 'sec'. */ | |
| tv.tv_usec = 500000; /* Timeout 'usec'. */ | |
| /* check */ | |
| r = select(fd_in_fdm_gazebo + 1, &fds, NULL, NULL, &tv); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: select() failed, r=%d (%s)", __FUNCTION__, r, strerror(r)); | |
| f_exit++; | |
| return NULL; | |
| }; | |
| if(!r) | |
| continue; | |
| r = recv(fd_in_fdm_gazebo, buf, sizeof(buf), 0); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: recv() failed, r=%d (%s)", __FUNCTION__, r, strerror(r)); | |
| f_exit++; | |
| return NULL; | |
| }; | |
| buf[r] = 0; | |
| r = parse_json_fdm_packet(buf, &pkt); | |
| if(r < 0) | |
| continue; | |
| pthread_spin_lock(&lock); | |
| cnt_curr.in_fdm_gazebo++; | |
| last_fdm = pkt; | |
| pthread_spin_unlock(&lock); | |
| }; | |
| return NULL; | |
| }; | |
| static void send_fdm_to_betaflight() | |
| { | |
| fdm_packet pkt = {0}; | |
| pthread_spin_lock(&lock); | |
| pkt = last_fdm; | |
| cnt_curr.out_fdm_betaflight++; | |
| pthread_spin_unlock(&lock); | |
| sendto(fd_out_fdm_betaflight, &pkt, sizeof(pkt), 0, (struct sockaddr *)&s_addr_betaflight, sizeof(s_addr_betaflight)); | |
| }; | |
| static void in_pwm_timeout(int f_to_gazebo) | |
| { | |
| if(f_to_gazebo) | |
| return; | |
| send_fdm_to_betaflight(); | |
| }; | |
| static void in_pwm_packet(servo_packet_raw* rf9, servo_packet* gz) | |
| { | |
| /* | |
| if(rf9) | |
| fprintf(stderr, "\nservo_packet_raw: %f, %f, %f, %f\n", | |
| rf9->pwm_output_raw[0], rf9->pwm_output_raw[1], rf9->pwm_output_raw[2], rf9->pwm_output_raw[3]); | |
| else if(gz) | |
| fprintf(stderr, "\nservo_packet: %f, %f, %f, %f\n", | |
| gz->motor_speed[0], gz->motor_speed[1], gz->motor_speed[2], gz->motor_speed[3]); | |
| servo_packet: 0.000000, 0.000000, 0.000000, 0.000000 | |
| servo_packet_raw: 1000.000000, 1000.000000, 1000.000000, 1000.000000 | |
| */ | |
| pthread_spin_lock(&lock); | |
| if(rf9) | |
| last_servo_packet_raw = *rf9; | |
| if(gz) | |
| last_servo_packet = *gz; | |
| pthread_spin_unlock(&lock); | |
| if(rf9) | |
| send_fdm_to_betaflight(); | |
| }; | |
| static int parse_json_fdm_packet(char *json_buffer, fdm_packet *pkt) | |
| { | |
| /* | |
| { | |
| "timestamp":909.144, | |
| "imu":{ | |
| "gyro":[-0.043994875844701,-0.000572378842194773,-0.007185674781765455], | |
| "accel_body":[0.010770732557861375,0.5601342938212403,-9.739123093417417] | |
| }, | |
| "position":[100.00533744764076,0.0007287157985803391,-10.217000650458129], | |
| "quaternion":[0.999985203396347,-0.001301780858639356,0.00015294113943493633,-0.005279674608404283], | |
| "velocity":[-0.005383170717085117,0.015871287066207209,0.00602523647475713] | |
| } | |
| */ | |
| int r = -1, i; | |
| /* parse json */ | |
| cJSON *json_root = cJSON_Parse(json_buffer); | |
| if(!json_root) | |
| { | |
| const char *json_error_ptr = cJSON_GetErrorPtr(); | |
| if(json_error_ptr) | |
| fprintf(stderr, "%s: cJSON_Parse failed: %s\n", __FUNCTION__, json_error_ptr); | |
| return -1; | |
| }; | |
| // timestamp | |
| cJSON *json_timestamp = cJSON_GetObjectItemCaseSensitive(json_root, "timestamp"); | |
| if(!cJSON_IsNumber(json_timestamp)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsNumber(timestamp) is false\n", __FUNCTION__); | |
| goto ex1; | |
| } | |
| pkt->timestamp = cJSON_GetNumberValue(json_timestamp); | |
| // imu | |
| cJSON *json_imu = cJSON_GetObjectItemCaseSensitive(json_root, "imu"); | |
| if(!cJSON_IsObject(json_imu)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsObject(json_imu) is false\n", __FUNCTION__); | |
| goto ex1; | |
| } | |
| else | |
| { | |
| // gyro | |
| cJSON *json_imu_gyro = cJSON_GetObjectItemCaseSensitive(json_imu, "gyro"); | |
| if(!cJSON_IsArray(json_imu_gyro)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsArray(json_imu_gyro) is false\n", __FUNCTION__); | |
| goto ex1; | |
| } | |
| else | |
| { | |
| r = cJSON_GetArraySize(json_imu_gyro); | |
| if(3 != r) | |
| { | |
| fprintf(stderr, "%s: cJSON_GetArraySize(json_imu_gyro) == %d\n", __FUNCTION__, r); | |
| goto ex1; | |
| }; | |
| for(i = 0; i < 3; i++) | |
| { | |
| cJSON *json_imu_gyro_item; | |
| json_imu_gyro_item = cJSON_GetArrayItem(json_imu_gyro, i); | |
| if(!cJSON_IsNumber(json_imu_gyro_item)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsNumber(json_imu_gyro_item[%d]) is false\n", __FUNCTION__, i); | |
| goto ex1; | |
| }; | |
| pkt->imu_angular_velocity_rpy[i] = cJSON_GetNumberValue(json_imu_gyro_item); | |
| }; | |
| }; | |
| // accel_body | |
| cJSON *json_imu_accel = cJSON_GetObjectItemCaseSensitive(json_imu, "accel_body"); | |
| if(!cJSON_IsArray(json_imu_accel)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsArray(json_imu_accel) is false\n", __FUNCTION__); | |
| goto ex1; | |
| } | |
| else | |
| { | |
| r = cJSON_GetArraySize(json_imu_accel); | |
| if(3 != r) | |
| { | |
| fprintf(stderr, "%s: cJSON_GetArraySize(json_imu_accel) == %d\n", __FUNCTION__, r); | |
| goto ex1; | |
| }; | |
| for(i = 0; i < 3; i++) | |
| { | |
| cJSON *json_imu_accel_item; | |
| json_imu_accel_item = cJSON_GetArrayItem(json_imu_accel, i); | |
| if(!cJSON_IsNumber(json_imu_accel_item)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsNumber(json_imu_accel_item[%d]) is false\n", __FUNCTION__, i); | |
| goto ex1; | |
| }; | |
| pkt->imu_linear_acceleration_xyz[i] = cJSON_GetNumberValue(json_imu_accel_item); | |
| }; | |
| }; | |
| }; | |
| // quaternion | |
| cJSON *json_quaternion = cJSON_GetObjectItemCaseSensitive(json_root, "quaternion"); | |
| if(!cJSON_IsArray(json_quaternion)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsArray(quaternion) is false\n", __FUNCTION__); | |
| goto ex1; | |
| } | |
| else | |
| { | |
| r = cJSON_GetArraySize(json_quaternion); | |
| if(4 != r) | |
| { | |
| fprintf(stderr, "%s: cJSON_GetArraySize(quaternion) == %d\n", __FUNCTION__, r); | |
| goto ex1; | |
| }; | |
| for(i = 0; i < 4; i++) | |
| { | |
| cJSON *json_quaternion_item; | |
| json_quaternion_item = cJSON_GetArrayItem(json_quaternion, i); | |
| if(!cJSON_IsNumber(json_quaternion_item)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsNumber(json_quaternion_item[%d]) is false\n", __FUNCTION__, i); | |
| goto ex1; | |
| }; | |
| pkt->imu_orientation_quat[i] = cJSON_GetNumberValue(json_quaternion_item); | |
| }; | |
| }; | |
| // velocity | |
| cJSON *json_velocity = cJSON_GetObjectItemCaseSensitive(json_root, "velocity"); | |
| if(!cJSON_IsArray(json_velocity)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsArray(velocity) is false\n", __FUNCTION__); | |
| goto ex1; | |
| } | |
| else | |
| { | |
| r = cJSON_GetArraySize(json_velocity); | |
| if(3 != r) | |
| { | |
| fprintf(stderr, "%s: cJSON_GetArraySize(velocity) == %d\n", __FUNCTION__, r); | |
| goto ex1; | |
| }; | |
| for(i = 0; i < 3; i++) | |
| { | |
| cJSON *json_velocity_item; | |
| json_velocity_item = cJSON_GetArrayItem(json_velocity, i); | |
| if(!cJSON_IsNumber(json_velocity_item)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsNumber(json_position_item[%d]) is false\n", __FUNCTION__, i); | |
| goto ex1; | |
| }; | |
| pkt->velocity_xyz[i] = cJSON_GetNumberValue(json_velocity_item); | |
| }; | |
| }; | |
| // position | |
| cJSON *json_position = cJSON_GetObjectItemCaseSensitive(json_root, "position"); | |
| if(!cJSON_IsArray(json_position)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsArray(position) is false\n", __FUNCTION__); | |
| goto ex1; | |
| } | |
| else | |
| { | |
| r = cJSON_GetArraySize(json_position); | |
| if(3 != r) | |
| { | |
| fprintf(stderr, "%s: cJSON_GetArraySize(position) == %d\n", __FUNCTION__, r); | |
| goto ex1; | |
| }; | |
| for(i = 0; i < 3; i++) | |
| { | |
| cJSON *json_position_item; | |
| json_position_item = cJSON_GetArrayItem(json_position, i); | |
| if(!cJSON_IsNumber(json_position_item)) | |
| { | |
| fprintf(stderr, "%s: cJSON_IsNumber(json_position_item[%d]) is false\n", __FUNCTION__, i); | |
| goto ex1; | |
| }; | |
| pkt->position_xyz[i] = cJSON_GetNumberValue(json_position_item); | |
| }; | |
| }; | |
| // we will calculate pressure for further barameter using | |
| pkt->pressure = altitude_to_pressure(DEFAULT_ATTITUDE - pkt->position_xyz[2]); | |
| ex1: | |
| cJSON_Delete(json_root); | |
| return r; | |
| }; | |
| static void* proc_out_pwm_gazebo(void* p __attribute__((unused))) | |
| { | |
| int fd, r; | |
| struct itimerspec t; | |
| fd = timerfd_create(CLOCK_MONOTONIC, 0); | |
| if(fd < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: timerfd_create() failed: r=%d (%s)\n", __FUNCTION__, r, strerror(r)); | |
| goto ex0; | |
| }; | |
| t.it_value.tv_sec = | |
| t.it_interval.tv_sec = 0; | |
| t.it_value.tv_nsec = | |
| t.it_interval.tv_nsec = 1000000000LL / output_pwm_frame_rate; | |
| r = timerfd_settime(fd, 0, &t, NULL); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: timerfd_settime() failed, r=%d (%s)", __FUNCTION__, r, strerror(r)); | |
| goto ex1; | |
| }; | |
| while(!f_exit) | |
| { | |
| uint64_t v; | |
| struct servo_packet_16 pwm = {0}; | |
| r = read(fd, &v, sizeof(uint64_t)); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: timerfd_settime() failed, r=%d (%s)", __FUNCTION__, r, strerror(r)); | |
| goto ex1; | |
| }; | |
| pwm.magic = 18458; | |
| pwm.frame_rate = output_pwm_frame_rate + 1; | |
| pwm.frame_count = cnt_curr.out_pwm_gazebo++; | |
| // https://ardupilot.org/copter/docs/connect-escs-and-motors.html | |
| pthread_spin_lock(&lock); | |
| pwm.pwm[3] = last_servo_packet_raw.pwm_output_raw[0]; | |
| pwm.pwm[0] = last_servo_packet_raw.pwm_output_raw[1]; | |
| pwm.pwm[1] = last_servo_packet_raw.pwm_output_raw[2]; | |
| pwm.pwm[2] = last_servo_packet_raw.pwm_output_raw[3]; | |
| pthread_spin_unlock(&lock); | |
| r = sendto(fd_out_pwm_gazebo, &pwm, sizeof(pwm), 0, (struct sockaddr *)&s_addr_gazebo, sizeof(s_addr_gazebo)); | |
| if(r < 0) | |
| { | |
| r = errno; | |
| fprintf(stderr, "%s: sendto(%s:%d) failed: r=%d (%s)\n", | |
| __FUNCTION__, inet_ntoa(s_addr_gazebo.sin_addr), ntohs(s_addr_gazebo.sin_port), r, strerror(r)); | |
| goto ex1; | |
| }; | |
| }; | |
| ex1: | |
| close(fd); | |
| ex0: | |
| f_exit++; | |
| return NULL; | |
| }; | |
| int64_t get_timeval_timestamp_us(struct timeval *tv) | |
| { | |
| return ((int64_t)tv->tv_sec * 1000000LL) + (int64_t)tv->tv_usec; | |
| }; | |
| int64_t get_current_timestamp_us() | |
| { | |
| struct timeval tv; | |
| gettimeofday(&tv, NULL); | |
| return get_timeval_timestamp_us(&tv); | |
| }; | |
| int64_t get_clock_monotonic_raw_ns() | |
| { | |
| struct timespec tm; | |
| clock_gettime(CLOCK_MONOTONIC_RAW, &tm); | |
| return tm.tv_nsec + tm.tv_sec * 1000000000LL; | |
| }; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment