2 * This file is part of INAV Project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <arpa/inet.h>
41 #include <netinet/in.h>
46 #include "fc/runtime_config.h"
47 #include "common/utils.h"
48 #include "scheduler/scheduler.h"
49 #include "drivers/system.h"
50 #include "drivers/pwm_mapping.h"
51 #include "drivers/timer.h"
52 #include "drivers/serial.h"
53 #include "config/config_streamer.h"
54 #include "build/version.h"
56 #include "target/SITL/sim/realFlight.h"
57 #include "target/SITL/sim/xplane.h"
59 #include "target/SITL/serial_proxy.h"
62 const int timerHardwareCount
= 0;
63 timerHardware_t timerHardware
[1];
64 uint32_t SystemCoreClock
= 500 * 1e6
; // fake 500 MHz;
66 char _Min_Stack_Size
= 0;
68 static pthread_mutex_t mainLoopLock
;
69 static SitlSim_e sitlSim
= SITL_SIM_NONE
;
70 static struct timespec start_time
;
71 static uint8_t pwmMapping
[MAX_MOTORS
+ MAX_SERVOS
];
72 static uint8_t mappingCount
= 0;
73 static bool useImu
= false;
74 static char *simIp
= NULL
;
75 static int simPort
= 0;
79 static void printVersion(void) {
80 fprintf(stderr
, "INAV %d.%d.%d SITL (%s)\n", FC_VERSION_MAJOR
, FC_VERSION_MINOR
, FC_VERSION_PATCH_LEVEL
, shortGitRevision
);
83 void systemInit(void) {
85 clock_gettime(CLOCK_MONOTONIC
, &start_time
);
86 fprintf(stderr
, "[SYSTEM] Init...\n");
88 #if !defined(__FreeBSD__) && !defined(__APPLE__)
89 pthread_attr_t thAttr
;
92 pthread_attr_init(&thAttr
);
93 pthread_attr_getschedpolicy(&thAttr
, &policy
);
94 pthread_setschedprio(pthread_self(), sched_get_priority_min(policy
));
95 pthread_attr_destroy(&thAttr
);
98 if (pthread_mutex_init(&mainLoopLock
, NULL
) != 0) {
99 fprintf(stderr
, "[SYSTEM] Unable to create mainLoop lock.\n");
103 if (sitlSim
!= SITL_SIM_NONE
) {
104 fprintf(stderr
, "[SIM] Waiting for connection...\n");
108 case SITL_SIM_REALFLIGHT
:
109 if (mappingCount
> RF_MAX_PWM_OUTS
) {
110 fprintf(stderr
, "[SIM] Mapping error. RealFligt supports a maximum of %i PWM outputs.", RF_MAX_PWM_OUTS
);
111 sitlSim
= SITL_SIM_NONE
;
114 if (simRealFlightInit(simIp
, pwmMapping
, mappingCount
, useImu
)) {
115 fprintf(stderr
, "[SIM] Connection with RealFlight successfully established.\n");
117 fprintf(stderr
, "[SIM] Connection with RealFlight NOT established.\n");
120 case SITL_SIM_XPLANE
:
121 if (mappingCount
> XP_MAX_PWM_OUTS
) {
122 fprintf(stderr
, "[SIM] Mapping error. RealFligt supports a maximum of %i PWM outputs.", XP_MAX_PWM_OUTS
);
123 sitlSim
= SITL_SIM_NONE
;
126 if (simXPlaneInit(simIp
, simPort
, pwmMapping
, mappingCount
, useImu
)) {
127 fprintf(stderr
, "[SIM] Connection with X-Plane successfully established.\n");
129 fprintf(stderr
, "[SIM] Connection with X-PLane NOT established.\n");
133 fprintf(stderr
, "[SIM] No interface specified. Configurator only.\n");
137 rescheduleTask(TASK_SERIAL
, SITL_SERIAL_TASK_US
);
140 bool parseMapping(char* mapStr
)
142 char *split
= strtok(mapStr
, ",");
146 if (strlen(split
) != 6) {
150 if (split
[0] == 'M' || split
[0] == 'S') {
151 memcpy(numBuf
, &split
[1], 2);
152 int pwmOut
= atoi(numBuf
);
153 memcpy(numBuf
, &split
[4], 2);
154 int rOut
= atoi(numBuf
);
155 if (pwmOut
< 0 || rOut
< 1) {
158 if (split
[0] == 'M') {
159 pwmMapping
[rOut
- 1] = pwmOut
- 1;
160 pwmMapping
[rOut
- 1] |= 0x80;
162 } else if (split
[0] == 'S') {
163 pwmMapping
[rOut
- 1] = pwmOut
;
169 split
= strtok(NULL
, ",");
175 OptSerialStopBits_e
parseStopBits(const char* optarg
){
176 if ( strcmp(optarg
, "One") == 0 ) {
177 return OPT_SERIAL_STOP_BITS_ONE
;
178 } else if ( strcmp(optarg
, "Two") == 0 ) {
179 return OPT_SERIAL_STOP_BITS_TWO
;
181 return OPT_SERIAL_STOP_BITS_INVALID
;
185 OptSerialParity_e
parseParity(const char* optarg
){
186 if ( strcmp(optarg
, "Even") == 0 ) {
187 return OPT_SERIAL_PARITY_EVEN
;
188 } else if ( strcmp(optarg
, "None") == 0 ) {
189 return OPT_SERIAL_PARITY_NONE
;
190 } else if ( strcmp(optarg
, "Odd") == 0 ) {
191 return OPT_SERIAL_PARITY_ODD
;
193 return OPT_SERIAL_PARITY_INVALID
;
197 void printCmdLineOptions(void)
200 fprintf(stderr
, "Avaiable options:\n");
201 fprintf(stderr
, "--path=[path] Path and filename of eeprom.bin. If not specified 'eeprom.bin' in program directory is used.\n");
202 fprintf(stderr
, "--sim=[rf|xp] Simulator interface: rf = RealFligt, xp = XPlane. Example: --sim=rf\n");
203 fprintf(stderr
, "--simip=[ip] IP-Address oft the simulator host. If not specified localhost (127.0.0.1) is used.\n");
204 fprintf(stderr
, "--simport=[port] Port oft the simulator host.\n");
205 fprintf(stderr
, "--useimu Use IMU sensor data from the simulator instead of using attitude data from the simulator directly (experimental, not recommended).\n");
206 fprintf(stderr
, "--serialuart=[uart] UART number on which serial receiver is configured in SITL, f.e. 3 for UART3\n");
207 fprintf(stderr
, "--serialport=[serialport] Host's serial port to which serial receiver/proxy FC is connected, f.e. COM3, /dev/ttyACM3\n");
208 fprintf(stderr
, "--baudrate=[baudrate] Serial receiver baudrate (default: 115200).\n");
209 fprintf(stderr
, "--stopbits=[None|One|Two] Serial receiver stopbits (default: One).\n");
210 fprintf(stderr
, "--parity=[Even|None|Odd] Serial receiver parity (default: None).\n");
211 fprintf(stderr
, "--fcproxy Use inav/betaflight FC as a proxy for serial receiver.\n");
212 fprintf(stderr
, "--chanmap=[mapstring] Channel mapping. Maps INAVs motor and servo PWM outputs to the virtual receiver output in the simulator.\n");
213 fprintf(stderr
, " The mapstring has the following format: M(otor)|S(servo)<INAV-OUT>-<RECEIVER-OUT>,... All numbers must have two digits\n");
214 fprintf(stderr
, " For example: Map motor 1 to virtal receiver output 1, servo 1 to output 2 and servo 2 to output 3:\n");
215 fprintf(stderr
, " --chanmap=M01-01,S01-02,S02-03\n");
218 void parseArguments(int argc
, char *argv
[])
220 // Stash these so we can rexec on reboot, just like a FC does
221 c_argv
= calloc(argc
+1, sizeof(char *));
222 for (int i
= 0; i
< argc
; i
++) {
223 c_argv
[i
] = strdup(argv
[i
]);
227 static struct option longOpt
[] = {
228 {"sim", required_argument
, 0, 's'},
229 {"useimu", no_argument
, 0, 'u'},
230 {"chanmap", required_argument
, 0, 'c'},
231 {"simip", required_argument
, 0, 'i'},
232 {"simport", required_argument
, 0, 'p'},
233 {"help", no_argument
, 0, 'h'},
234 {"path", required_argument
, 0, 'e'},
235 {"version", no_argument
, 0, 'v'},
236 {"serialuart", required_argument
, 0, '0'},
237 {"serialport", required_argument
, 0, '1'},
238 {"baudrate", required_argument
, 0, '2'},
239 {"stopbits", required_argument
, 0, '3'},
240 {"parity", required_argument
, 0, '4'},
241 {"fcproxy", no_argument
, 0, '5'},
245 c
= getopt_long_only(argc
, argv
, "s:c:h", longOpt
, NULL
);
251 if (strcmp(optarg
, "rf") == 0) {
252 sitlSim
= SITL_SIM_REALFLIGHT
;
253 } else if (strcmp(optarg
, "xp") == 0){
254 sitlSim
= SITL_SIM_XPLANE
;
256 fprintf(stderr
, "[SIM] Unsupported simulator %s.\n", optarg
);
261 if (!parseMapping(optarg
) && sitlSim
!= SITL_SIM_NONE
) {
262 fprintf(stderr
, "[SIM] Invalid channel mapping string.\n");
263 printCmdLineOptions();
268 simPort
= atoi(optarg
);
277 if (!configFileSetPath(optarg
)){
278 fprintf(stderr
, "[EEPROM] Invalid path, using eeprom file in program directory\n.");
285 serialUartIndex
= atoi(optarg
);
286 if ( (serialUartIndex
<1) || (serialUartIndex
>8) ) {
287 fprintf(stderr
, "[serialuart] Invalid argument\n.");
292 if ( (strlen(optarg
)<1) || (strlen(optarg
)>63) ) {
293 fprintf(stderr
, "[serialport] Invalid argument\n.");
296 strcpy( serialPort
, optarg
);
300 serialBaudRate
= atoi(optarg
);
301 if ( serialBaudRate
< 1200 )
303 fprintf(stderr
, "[baudrate] Invalid argument\n.");
308 serialStopBits
= parseStopBits(optarg
);
309 if ( serialStopBits
== OPT_SERIAL_STOP_BITS_INVALID
)
311 fprintf(stderr
, "[stopbits] Invalid argument\n.");
317 serialParity
= parseParity(optarg
);
318 if ( serialParity
== OPT_SERIAL_PARITY_INVALID
)
320 fprintf(stderr
, "[parity] Invalid argument\n.");
325 serialFCProxy
= true;
329 printCmdLineOptions();
336 strcpy(simIp
, "127.0.0.1");
341 bool lockMainPID(void) {
342 return pthread_mutex_trylock(&mainLoopLock
) == 0;
345 void unlockMainPID(void)
347 pthread_mutex_unlock(&mainLoopLock
);
350 // Replacements for system functions
351 timeUs_t
micros(void) {
353 clock_gettime(CLOCK_MONOTONIC
, &now
);
355 return (now
.tv_sec
- start_time
.tv_sec
) * 1000000 + (now
.tv_nsec
- start_time
.tv_nsec
) / 1000;
358 uint64_t microsISR(void)
363 uint32_t millis(void) {
364 return (uint32_t)(micros() / 1000);
367 void delayMicroseconds(timeUs_t us
)
372 void delay(timeMs_t ms
)
374 delayMicroseconds(ms
* 1000UL);
377 void systemReset(void)
379 fprintf(stderr
, "[SYSTEM] Reset\n");
380 #if defined(__CYGWIN__) || defined(__APPLE__) || GCC_MAJOR < 12
381 for(int j
= 3; j
< 1024; j
++) {
388 execvp(c_argv
[0], c_argv
); // restart
391 void systemResetToBootloader(void)
393 fprintf(stderr
, "[SYSTEM] Reset to bootloader\n");
397 void failureMode(failureMode_e mode
) {
398 fprintf(stderr
, "[SYSTEM] Failure mode %d\n", mode
);
404 // Even more dummys and stubs
405 uint32_t getEscUpdateFrequency(void)
410 pwmInitError_e
getPwmInitError(void)
412 return PWM_INIT_ERROR_NONE
;
415 const char *getPwmInitErrorMessage(void)
420 void IOConfigGPIO(IO_t io
, ioConfig_t cfg
)
426 void timerInit(void) {
430 bool isMPUSoftReset(void)
435 // Not in linux libs, but in arm-none-eabi ?!?
436 // https://github.com/lattera/freebsd/blob/master/lib/libc/string/strnstr.c
437 char * strnstr(const char *s
, const char *find
, size_t slen
)
442 if ((c
= *find
++) != '\0') {
446 if (slen
-- < 1 || (sc
= *s
++) == '\0')
451 } while (strncmp(s
, find
, len
) != 0);
457 int lookupAddress (char *name
, int port
, int type
, struct sockaddr
*addr
, socklen_t
* len
)
459 struct addrinfo
*servinfo
, *p
;
460 struct addrinfo hints
= {.ai_family
= AF_UNSPEC
, .ai_socktype
= type
, .ai_flags
= AI_V4MAPPED
|AI_ADDRCONFIG
};
462 hints
.ai_flags
|= AI_PASSIVE
;
465 This nonsense is to uniformly deliver the same sa_family regardless of whether
466 name is NULL or non-NULL ** ON LINUX **
467 Otherwise, at least on Linux, we get
468 - V6,V4 for the non-null case and
469 - V4,V6 for the null case, regardless of gai.conf
470 Which may confuse consumers
471 FreeBSD and Windows behave consistently, giving V6 for Ipv6 enabled stacks
472 unless a quad dotted address is specified (or a name resolveds to V4,
473 or system policy enforces IPv4 over V6
475 struct addrinfo
*p4
= NULL
;
476 struct addrinfo
*p6
= NULL
;
480 snprintf(aport
, sizeof(aport
), "%d", port
);
482 if ((result
= getaddrinfo(name
, aport
, &hints
, &servinfo
)) != 0) {
483 fprintf(stderr
, "getaddrinfo: %s\n", gai_strerror(result
));
486 for(p
= servinfo
; p
!= NULL
; p
= p
->ai_next
) {
487 if(p
->ai_family
== AF_INET6
)
489 else if(p
->ai_family
== AF_INET
)
499 memcpy(addr
, p
->ai_addr
, p
->ai_addrlen
);
500 *len
= p
->ai_addrlen
;
501 freeaddrinfo(servinfo
);
506 char *prettyPrintAddress(struct sockaddr
* p
, char *outbuf
, size_t buflen
)
508 if (buflen
< IPADDRESS_PRINT_BUFLEN
) {
514 if (p
->sa_family
== AF_INET6
) {
515 struct sockaddr_in6
* ip
= (struct sockaddr_in6
*)p
;
516 addr
= &ip
->sin6_addr
;
517 port
= ntohs(ip
->sin6_port
);
519 struct sockaddr_in
* ip
= (struct sockaddr_in
*)p
;
520 port
= ntohs(ip
->sin_port
);
521 addr
= &ip
->sin_addr
;
523 const char *res
= inet_ntop(p
->sa_family
, addr
, outbuf
+1, buflen
-1);
525 char *ptr
= (char*)res
+strlen(res
);
526 if (p
->sa_family
== AF_INET6
) {
532 sprintf(ptr
, ":%d", port
);