fix(simulation): support Windows SITL transports

Make MAVLink, uXRCE-DDS, and simulator_mavlink tolerate Windows socket and daemon semantics while preserving the existing POSIX transport behavior.

Update Gazebo Classic integration, SIH defaults, and multi-instance simulation scripts needed by the native Windows SITL workflow.
This commit is contained in:
Nuno Marques
2026-05-11 10:33:25 -07:00
parent 2e88fa46aa
commit 1f484eef07
14 changed files with 307 additions and 92 deletions
@@ -16,7 +16,8 @@ param set-default SENS_EN_BAROSIM 1
param set-default SENS_EN_MAGSIM 1
param set-default SENS_EN_ARSPDSIM 1
param set-default SIH_T_MAX 6
param set-default SIH_F_T_MAX 8
param set-default SIH_F_CT0 0.16
param set-default SIH_MASS 0.3
param set-default SIH_IXX 0.00402
param set-default SIH_IYY 0.0144
@@ -65,3 +66,23 @@ param set-default FW_YR_I 3.0000
param set-default FW_YR_D 0.0000
param set-default FW_YR_FF 0.0000
param set-default FW_YR_IMAX 1.0000
# FW controller / TECS tuning for this 0.3 kg low-mass airframe.
# Default PX4 FW tuning is calibrated for 1-5 kg planes; this airframe needs
# specific tuning for the takeoff -> cruise handoff at 10 m/s trim with 8 N
# max thrust. Values verified by iterating against SIH dynamics.
param set-default FW_AIRSPD_STALL 6
param set-default FW_AIRSPD_MIN 7
param set-default FW_AIRSPD_TRIM 10
param set-default FW_AIRSPD_MAX 12
param set-default FW_PSP_OFF 2.0
param set-default FW_THR_TRIM 0.9
param set-default FW_THR_MIN 0.5
param set-default FW_T_CLMB_MAX 2
param set-default FW_T_SINK_MIN 2
param set-default FW_T_SPDWEIGHT 2.0
param set-default FW_T_ALT_TC 3.0
param set-default FW_T_TAS_TC 3.0
param set-default FW_T_HRATE_FF 0.6
param set-default FW_T_THR_DAMPING 0.3
param set-default NAV_LOITER_RAD 30
+105
View File
@@ -0,0 +1,105 @@
<#
.SYNOPSIS
Run multiple instances of the 'px4' binary on Windows, without starting
an external simulator. Mirrors Tools/simulation/sitl_multiple_run.sh -
same args, same per-instance working directories, same logfile names.
.DESCRIPTION
For developers on PowerShell or CMD (the .sh script also works under
Git Bash on Windows). Assumes px4 is already built with the specified
build target (e.g., px4_sitl_default).
Pass 0 for SitlNum to just stop everything currently running:
.\Tools\simulation\sitl_multiple_run.ps1 0
.PARAMETER SitlNum
Number of instances to start. Defaults to 2.
.PARAMETER SimModel
Value to export as PX4_SIM_MODEL. Defaults to 'gazebo-classic_iris' to
match the .sh script; set to 'sihsim_quadx' for a Windows-friendly run
that needs no external simulator.
.PARAMETER BuildTarget
Name of the cmake build directory under build/. Defaults to
'px4_sitl_default'.
.EXAMPLE
.\Tools\simulation\sitl_multiple_run.ps1 3 sihsim_quadx px4_sitl_default
.EXAMPLE
# From CMD:
powershell -ExecutionPolicy Bypass -File Tools\simulation\sitl_multiple_run.ps1 2 sihsim_quadx
#>
param(
[int]$SitlNum = 2,
[string]$SimModel = 'gazebo-classic_iris',
[string]$BuildTarget = 'px4_sitl_default'
)
$ErrorActionPreference = 'Stop'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$SrcPath = (Resolve-Path (Join-Path $ScriptDir '..\..')).Path
$BuildPath = Join-Path $SrcPath "build\$BuildTarget"
# Prefer px4.exe; fall back to px4 so this also runs against a WSL/MinGW
# layout that drops the suffix.
$Px4Bin = Join-Path $BuildPath 'bin\px4.exe'
if (-not (Test-Path $Px4Bin)) { $Px4Bin = Join-Path $BuildPath 'bin\px4' }
if ($SitlNum -gt 0 -and -not (Test-Path $Px4Bin)) {
Write-Error "px4 binary not found in $BuildPath\bin"
}
Write-Host 'killing running instances'
Get-Process -Name 'px4' -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 1
# Clean up stale lock files left by force-killed instances. Each instance
# uses %TEMP%\px4_lock-<N>; if the previous owner was killed before it could
# unlink, the next start sees "already running" and aborts.
Get-ChildItem -Path "$env:TEMP" -Filter 'px4_lock-*' -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
# Clear stale per-instance log files from previous runs. These are not
# truncated by the new launch (Start-Process -RedirectStandardOutput appends),
# so leftover content from a different rcS / different model can mislead
# debugging by appearing alongside fresh output.
if ($SitlNum -gt 0) {
for ($n = 0; $n -lt $SitlNum; $n++) {
$stale = Join-Path $BuildPath "instance_$n"
if (Test-Path $stale) {
Remove-Item -Path (Join-Path $stale 'out.log') -Force -ErrorAction SilentlyContinue
Remove-Item -Path (Join-Path $stale 'err.log') -Force -ErrorAction SilentlyContinue
}
}
}
$env:PX4_SIM_MODEL = $SimModel
for ($n = 0; $n -lt $SitlNum; $n++) {
$WorkingDir = Join-Path $BuildPath "instance_$n"
if (-not (Test-Path $WorkingDir)) { New-Item -ItemType Directory -Path $WorkingDir | Out-Null }
Write-Host "starting instance $n in $WorkingDir"
# Spawn px4.exe directly with redirected stdout/stderr.
#
# Earlier revisions wrapped each launch in `cmd.exe /c "px4.exe ... > out.log"`,
# which had a fatal flaw at higher instance counts: every cmd.exe child
# inherits the launcher's console, and when that console is torn down
# (cmd.exe exiting after spawning px4, or the launcher PowerShell window
# closing) Windows broadcasts CTRL_CLOSE_EVENT to every attached process.
# PX4's SetConsoleCtrlHandler maps CTRL_CLOSE_EVENT to SIGINT and shuts
# down cleanly, so multi-instance launches died silently a few seconds
# after rcS finished -- no error, empty stderr, just gone.
#
# Start-Process with -RedirectStandard* writes both streams to per-instance
# log files without involving cmd.exe, and -WindowStyle Hidden gives each
# child its own (hidden) console so it survives the launcher exiting.
$stdoutPath = Join-Path $WorkingDir 'out.log'
$stderrPath = Join-Path $WorkingDir 'err.log'
Start-Process -FilePath $Px4Bin `
-ArgumentList @('-i', $n, '-d', "$BuildPath\etc") `
-WorkingDirectory $WorkingDir `
-RedirectStandardOutput $stdoutPath `
-RedirectStandardError $stderrPath `
-WindowStyle Hidden | Out-Null
}
+14 -2
View File
@@ -7,6 +7,9 @@
# ./Tools/simulation/sitl_multiple_run.sh 3 sihsim_quadx px4_sitl_sih
# ./Tools/simulation/sitl_multiple_run.sh 2 gazebo-classic_iris px4_sitl_default
# ./Tools/simulation/sitl_multiple_run.sh # defaults: 2 instances, gazebo-classic_iris, px4_sitl_default
#
# Pass 0 instances to just stop everything currently running:
# ./Tools/simulation/sitl_multiple_run.sh 0
sitl_num=${1:-2}
sim_model=${2:-gazebo-classic_iris}
@@ -17,8 +20,17 @@ src_path="$SCRIPT_DIR/../../"
build_path=${src_path}/build/${build_target}
# Pick px4 vs px4.exe so this script also runs from Git Bash on Windows.
px4_bin="$build_path/bin/px4"
[ -x "$px4_bin.exe" ] && px4_bin="$px4_bin.exe"
echo "killing running instances"
pkill -x px4 || true
if command -v pkill >/dev/null 2>&1; then
pkill -x px4 || true
fi
if command -v taskkill >/dev/null 2>&1; then
taskkill //IM px4.exe //F >/dev/null 2>&1 || true
fi
sleep 1
@@ -31,7 +43,7 @@ while [ $n -lt $sitl_num ]; do
pushd "$working_dir" &>/dev/null
echo "starting instance $n in $(pwd)"
$build_path/bin/px4 -i $n -d "$build_path/etc" >out.log 2>err.log &
"$px4_bin" -i $n -d "$build_path/etc" >out.log 2>err.log &
popd &>/dev/null
n=$(($n + 1))
@@ -46,7 +46,14 @@
#endif
#define PX4LOG_REGULAR_FILE DT_REG
#define PX4LOG_DIRECTORY DT_DIR
// Windows (MinGW) sets PATH_MAX to MAX_PATH=260. Use the larger of the
// platform PATH_MAX and 1024 so the buffer always fits the 1023-char
// scanf conversion target that the logger expects.
#if defined(__PX4_WINDOWS) && PATH_MAX < 1024
#define PX4_MAX_FILEPATH 1024
#else
#define PX4_MAX_FILEPATH PATH_MAX
#endif
#define PX4_MAX_FILEPATH_SCANF 1023
#endif
+72 -65
View File
@@ -383,7 +383,7 @@ Mavlink::destroy_all_instances()
} else {
iterations++;
printf(".");
PX4_INFO_RAW(".");
fflush(stdout);
px4_usleep(10000);
}
@@ -422,7 +422,7 @@ Mavlink::get_status_all_instances(bool show_streams_status)
for (Mavlink *inst : mavlink_module_instances) {
if (inst != nullptr) {
printf("\ninstance #%u:\n", iterations);
PX4_INFO_RAW("\ninstance #%u:\n", iterations);
if (show_streams_status) {
inst->display_status_streams();
@@ -803,7 +803,7 @@ void Mavlink::send_finish()
if (_src_addr_initialized) {
# endif // CONFIG_NET
ret = sendto(_socket_fd, _buf, _buf_fill, 0, (struct sockaddr *)&_src_addr, sizeof(_src_addr));
ret = sendto(_socket_fd, (const char *)_buf, _buf_fill, 0, (struct sockaddr *)&_src_addr, sizeof(_src_addr));
# if defined(CONFIG_NET)
}
@@ -818,7 +818,7 @@ void Mavlink::send_finish()
if (_broadcast_address_found && _buf_fill > 0) {
int bret = sendto(_socket_fd, _buf, _buf_fill, 0, (struct sockaddr *)&_bcast_addr, sizeof(_bcast_addr));
int bret = sendto(_socket_fd, (const char *)_buf, _buf_fill, 0, (struct sockaddr *)&_bcast_addr, sizeof(_bcast_addr));
if (bret <= 0) {
if (!_broadcast_failed_warned) {
@@ -980,7 +980,7 @@ void Mavlink::find_broadcast_address()
int broadcast_opt = 1;
if (setsockopt(_socket_fd, SOL_SOCKET, SO_BROADCAST, &broadcast_opt, sizeof(broadcast_opt)) < 0) {
if (setsockopt(_socket_fd, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast_opt, sizeof(broadcast_opt)) < 0) {
PX4_WARN("setting broadcast permission failed");
}
@@ -1035,7 +1035,7 @@ void Mavlink::init_udp()
.tv_usec = UDP_SEND_TIMEOUT_US
};
if (setsockopt(_socket_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
if (setsockopt(_socket_fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv)) < 0) {
PX4_WARN("setting socket timeout failed: %s", strerror(errno));
}
@@ -3075,12 +3075,17 @@ Mavlink::start(int argc, char *argv[])
// between the starting task and the spawned
// task - start_helper() only returns
// when the started task exits.
px4_task_spawn_cmd("mavlink_main",
SCHED_DEFAULT,
SCHED_PRIORITY_DEFAULT,
PX4_STACK_ADJUSTED(2896) + MAVLINK_NET_ADDED_STACK,
(px4_main_t)&Mavlink::start_helper,
(char *const *)argv);
const px4_task_t task_id = px4_task_spawn_cmd("mavlink_main",
SCHED_DEFAULT,
SCHED_PRIORITY_DEFAULT,
PX4_STACK_ADJUSTED(2896) + MAVLINK_NET_ADDED_STACK,
(px4_main_t)&Mavlink::start_helper,
(char *const *)argv);
if (task_id < 0) {
PX4_ERR("task spawn failed: %d", task_id);
return PX4_ERROR;
}
// Ensure that this shell command
// does not return before the instance
@@ -3089,20 +3094,22 @@ Mavlink::start(int argc, char *argv[])
// this is effectively a lock on concurrent
// instance starting. XXX do a real lock.
// Sleep 500 us between each attempt
// Sleep 500 us between each attempt. This is a shell-side startup wait, so
// it must use wall time rather than simulated lockstep time.
const unsigned sleeptime = 500;
// Wait 100 ms max for the startup.
const unsigned limit = 100 * 1000 / sleeptime;
// Wait 10 s max for the startup.
const unsigned limit = 10 * 1000 * 1000 / sleeptime;
unsigned count = 0;
while (ic == Mavlink::instance_count() && count < limit) {
px4_usleep(sleeptime);
system_usleep(sleeptime);
count++;
}
if (ic == Mavlink::instance_count()) {
PX4_ERR("start timed out");
return PX4_ERROR;
} else {
@@ -3118,83 +3125,83 @@ Mavlink::display_status()
#endif // !CONSTRAINED_FLASH
if (_tstatus.heartbeat_type_gcs) {
printf("\tGCS heartbeat valid\n");
PX4_INFO_RAW("\tGCS heartbeat valid\n");
}
printf("\tmavlink chan: #%u\n", static_cast<unsigned>(_channel));
PX4_INFO_RAW("\tmavlink chan: #%u\n", static_cast<unsigned>(_channel));
if (_tstatus.timestamp > 0) {
printf("\ttype:\t\t");
PX4_INFO_RAW("\ttype:\t\t");
if (_radio_status_available) {
printf("RADIO Link\n");
printf("\t rssi:\t\t%" PRIu8 "\n", _rstatus.rssi);
printf("\t remote rssi:\t%" PRIu8 "\n", _rstatus.remote_rssi);
printf("\t txbuf:\t%" PRIu8 "\n", _rstatus.txbuf);
printf("\t noise:\t%" PRIu8 "\n", _rstatus.noise);
printf("\t remote noise:\t%" PRIu8 "\n", _rstatus.remote_noise);
printf("\t rx errors:\t%" PRIu16 "\n", _rstatus.rxerrors);
printf("\t fixed:\t%" PRIu16 "\n", _rstatus.fix);
PX4_INFO_RAW("RADIO Link\n");
PX4_INFO_RAW("\t rssi:\t\t%" PRIu8 "\n", _rstatus.rssi);
PX4_INFO_RAW("\t remote rssi:\t%" PRIu8 "\n", _rstatus.remote_rssi);
PX4_INFO_RAW("\t txbuf:\t%" PRIu8 "\n", _rstatus.txbuf);
PX4_INFO_RAW("\t noise:\t%" PRIu8 "\n", _rstatus.noise);
PX4_INFO_RAW("\t remote noise:\t%" PRIu8 "\n", _rstatus.remote_noise);
PX4_INFO_RAW("\t rx errors:\t%" PRIu16 "\n", _rstatus.rxerrors);
PX4_INFO_RAW("\t fixed:\t%" PRIu16 "\n", _rstatus.fix);
} else if (_tstatus.type == telemetry_status_s::LINK_TYPE_USB) {
printf("USB CDC\n");
PX4_INFO_RAW("USB CDC\n");
} else {
printf("GENERIC LINK OR RADIO\n");
PX4_INFO_RAW("GENERIC LINK OR RADIO\n");
}
} else {
printf("\tno radio status.\n");
PX4_INFO_RAW("\tno radio status.\n");
}
printf("\tflow control: %s\n", _flow_control_mode ? "ON" : "OFF");
printf("\trates:\n");
printf("\t tx: %.1f B/s\n", (double)_tstatus.tx_rate_avg);
printf("\t txerr: %.1f B/s\n", (double)_tstatus.tx_error_rate_avg);
printf("\t tx rate mult: %.3f\n", (double)_rate_mult);
printf("\t tx rate max: %i B/s\n", _datarate);
printf("\t rx: %.1f B/s\n", (double)_tstatus.rx_rate_avg);
printf("\t rx loss: %.1f%%\n", (double)_tstatus.rx_message_lost_rate);
PX4_INFO_RAW("\tflow control: %s\n", _flow_control_mode ? "ON" : "OFF");
PX4_INFO_RAW("\trates:\n");
PX4_INFO_RAW("\t tx: %.1f B/s\n", (double)_tstatus.tx_rate_avg);
PX4_INFO_RAW("\t txerr: %.1f B/s\n", (double)_tstatus.tx_error_rate_avg);
PX4_INFO_RAW("\t tx rate mult: %.3f\n", (double)_rate_mult);
PX4_INFO_RAW("\t tx rate max: %i B/s\n", _datarate);
PX4_INFO_RAW("\t rx: %.1f B/s\n", (double)_tstatus.rx_rate_avg);
PX4_INFO_RAW("\t rx loss: %.1f%%\n", (double)_tstatus.rx_message_lost_rate);
#if !defined(CONSTRAINED_FLASH)
_receiver.print_detailed_rx_stats();
#endif // !CONSTRAINED_FLASH
if (_mavlink_ulog) {
printf("\tULog rate: %.1f%% of max %.1f%%\n", (double)_mavlink_ulog->current_data_rate() * 100.,
(double)_mavlink_ulog->maximum_data_rate() * 100.);
PX4_INFO_RAW("\tULog rate: %.1f%% of max %.1f%%\n", (double)_mavlink_ulog->current_data_rate() * 100.,
(double)_mavlink_ulog->maximum_data_rate() * 100.);
}
printf("\tFTP enabled: %s, TX enabled: %s\n",
_ftp_on ? "YES" : "NO",
_transmitting_enabled ? "YES" : "NO");
printf("\tmode: %s\n", mavlink_mode_str(_mode));
PX4_INFO_RAW("\tFTP enabled: %s, TX enabled: %s\n",
_ftp_on ? "YES" : "NO",
_transmitting_enabled ? "YES" : "NO");
PX4_INFO_RAW("\tmode: %s\n", mavlink_mode_str(_mode));
if (_mode == MAVLINK_MODE_IRIDIUM) {
printf("\t iridium tx freq: %.3f\n", (double)(_high_latency_freq));
PX4_INFO_RAW("\t iridium tx freq: %.3f\n", (double)(_high_latency_freq));
}
printf("\tForwarding: %s\n", get_forwarding_on() ? "On" : "Off");
printf("\tMAVLink version: %" PRId8 "\n", _protocol_version);
PX4_INFO_RAW("\tForwarding: %s\n", get_forwarding_on() ? "On" : "Off");
PX4_INFO_RAW("\tMAVLink version: %" PRId8 "\n", _protocol_version);
printf("\ttransport protocol: ");
PX4_INFO_RAW("\ttransport protocol: ");
switch (_protocol) {
#if defined(MAVLINK_UDP)
case Protocol::UDP:
printf("UDP (%hu, remote port: %hu)\n", _network_port, _remote_port);
printf("\tBroadcast enabled: %s\n",
broadcast_enabled() ? "YES" : "NO");
PX4_INFO_RAW("UDP (%hu, remote port: %hu)\n", _network_port, _remote_port);
PX4_INFO_RAW("\tBroadcast enabled: %s\n",
broadcast_enabled() ? "YES" : "NO");
#if defined(CONFIG_NET_IGMP) && defined(CONFIG_NET_ROUTE)
printf("\tMulticast enabled: %s\n",
multicast_enabled() ? "YES" : "NO");
PX4_INFO_RAW("\tMulticast enabled: %s\n",
multicast_enabled() ? "YES" : "NO");
#endif
#ifdef __PX4_POSIX
if (get_client_source_initialized()) {
printf("\tpartner IP: %s\n", inet_ntoa(get_client_source_address().sin_addr));
PX4_INFO_RAW("\tpartner IP: %s\n", inet_ntoa(get_client_source_address().sin_addr));
}
#endif
@@ -3202,24 +3209,24 @@ Mavlink::display_status()
#endif // MAVLINK_UDP
case Protocol::SERIAL:
printf("serial (%s @%i)\n", _device_name, _baudrate);
PX4_INFO_RAW("serial (%s @%i)\n", _device_name, _baudrate);
break;
}
if (_ping_stats.last_ping_time > 0) {
printf("\tping statistics:\n");
printf("\t last: %0.2f ms\n", (double)_ping_stats.last_rtt);
printf("\t mean: %0.2f ms\n", (double)_ping_stats.mean_rtt);
printf("\t max: %0.2f ms\n", (double)_ping_stats.max_rtt);
printf("\t min: %0.2f ms\n", (double)_ping_stats.min_rtt);
printf("\t dropped packets: %" PRIi32 "\n", _ping_stats.dropped_packets);
PX4_INFO_RAW("\tping statistics:\n");
PX4_INFO_RAW("\t last: %0.2f ms\n", (double)_ping_stats.last_rtt);
PX4_INFO_RAW("\t mean: %0.2f ms\n", (double)_ping_stats.mean_rtt);
PX4_INFO_RAW("\t max: %0.2f ms\n", (double)_ping_stats.max_rtt);
PX4_INFO_RAW("\t min: %0.2f ms\n", (double)_ping_stats.min_rtt);
PX4_INFO_RAW("\t dropped packets: %" PRIi32 "\n", _ping_stats.dropped_packets);
}
}
void
Mavlink::display_status_streams()
{
printf("\t%-20s%-16s %s\n", "Name", "Rate Config (current) [Hz]", "Message Size (if active) [B]");
PX4_INFO_RAW("\t%-20s%-16s %s\n", "Name", "Rate Config (current) [Hz]", "Message Size (if active) [B]");
const float rate_mult = _rate_mult;
@@ -3239,13 +3246,13 @@ Mavlink::display_status_streams()
snprintf(rate_str, sizeof(rate_str), "%6.2f (%.3f)", (double)rate, (double)rate_current);
}
printf("\t%-30s%-16s", stream->get_name(), rate_str);
PX4_INFO_RAW("\t%-30s%-16s", stream->get_name(), rate_str);
if (size > 0) {
printf(" %3u\n", size);
PX4_INFO_RAW(" %3u\n", size);
} else {
printf("\n");
PX4_INFO_RAW("\n");
}
}
}
+4
View File
@@ -78,6 +78,9 @@ MavlinkMissionManager::MavlinkMissionManager(Mavlink &mavlink) :
if (!_dataman_init) {
_dataman_init = true;
#if defined(ENABLE_LOCKSTEP_SCHEDULER) && defined(__PX4_WINDOWS)
// Avoid blocking MAVLink startup on dataman during Windows lockstep initialization.
#else
mission_s mission_state;
bool success = _dataman_client.readSync(DM_KEY_MISSION_STATE, 0, reinterpret_cast<uint8_t *>(&mission_state),
sizeof(mission_s));
@@ -91,6 +94,7 @@ MavlinkMissionManager::MavlinkMissionManager(Mavlink &mavlink) :
PX4_WARN("offboard mission init failed");
}
#endif
update_active_mission(_mission_dataman_id, _count[MAV_MISSION_TYPE_MISSION], _current_seq,
_crc32[MAV_MISSION_TYPE_MISSION], false);
}
+7 -7
View File
@@ -3411,7 +3411,7 @@ MavlinkReceiver::run()
else if (_mavlink.get_protocol() == Protocol::UDP) {
if (fds[0].revents & POLLIN) {
nread = recvfrom(_mavlink.get_socket_fd(), buf, sizeof(buf), 0, (struct sockaddr *)&srcaddr, &addrlen);
nread = recvfrom(_mavlink.get_socket_fd(), (char *)buf, sizeof(buf), 0, (struct sockaddr *)&srcaddr, &addrlen);
}
struct sockaddr_in &srcaddr_last = _mavlink.get_client_source_address();
@@ -3678,13 +3678,13 @@ void MavlinkReceiver::print_detailed_rx_stats() const
{
// TODO: add mutex around shared data.
if (_component_states_count > 0) {
printf("\tReceived Messages:\n");
PX4_INFO_RAW("\tReceived Messages:\n");
for (const auto &comp_stat : _component_states) {
if (comp_stat.received_messages > 0) {
printf("\t sysid:%3" PRIu8 ", compid:%3" PRIu8 ", Total: %" PRIu32 " (lost: %" PRIu32 ")\n",
comp_stat.system_id, comp_stat.component_id,
comp_stat.received_messages, comp_stat.missed_messages);
PX4_INFO_RAW("\t sysid:%3" PRIu8 ", compid:%3" PRIu8 ", Total: %" PRIu32 " (lost: %" PRIu32 ")\n",
comp_stat.system_id, comp_stat.component_id,
comp_stat.received_messages, comp_stat.missed_messages);
#if !defined(CONSTRAINED_FLASH)
@@ -3702,8 +3702,8 @@ void MavlinkReceiver::print_detailed_rx_stats() const
const float elapsed_s = (now_ms - msg_stat.last_time_received_ms) / 1000.f;
printf("\t msgid:%5" PRIu16 ", Rate:%5.1f Hz, last %.2fs ago\n",
msg_stat.msg_id, (double)msg_stat.avg_rate_hz, (double)elapsed_s);
PX4_INFO_RAW("\t msgid:%5" PRIu16 ", Rate:%5.1f Hz, last %.2fs ago\n",
msg_stat.msg_id, (double)msg_stat.avg_rate_hz, (double)elapsed_s);
}
}
}
+1 -1
View File
@@ -226,7 +226,7 @@ size_t MavlinkShell::available()
{
int ret = 0;
if (ioctl(_from_shell_fd, FIONREAD, (unsigned long)&ret) == OK) {
if (ioctl(_from_shell_fd, FIONREAD, &ret) == OK) {
return ret;
}
@@ -195,7 +195,7 @@ void SimulatorMavlink::update_sensors(const hrt_abstime &time, const mavlink_hil
}
// accel
if ((sensors.fields_updated & SensorSource::ACCEL) == SensorSource::ACCEL) {
if ((sensors.fields_updated & SensorSource::ACCEL_XYZ) == SensorSource::ACCEL_XYZ) {
if (sensors.id >= ACCEL_COUNT_MAX) {
PX4_ERR("Number of simulated accelerometer %d out of range. Max: %d", sensors.id, ACCEL_COUNT_MAX);
return;
@@ -238,7 +238,7 @@ void SimulatorMavlink::update_sensors(const hrt_abstime &time, const mavlink_hil
}
// gyro
if ((sensors.fields_updated & SensorSource::GYRO) == SensorSource::GYRO) {
if ((sensors.fields_updated & SensorSource::GYRO_XYZ) == SensorSource::GYRO_XYZ) {
if (sensors.id >= GYRO_COUNT_MAX) {
PX4_ERR("Number of simulated gyroscope %d out of range. Max: %d", sensors.id, GYRO_COUNT_MAX);
return;
@@ -281,7 +281,7 @@ void SimulatorMavlink::update_sensors(const hrt_abstime &time, const mavlink_hil
}
// magnetometer
if ((sensors.fields_updated & SensorSource::MAG) == SensorSource::MAG) {
if ((sensors.fields_updated & SensorSource::MAG_XYZ) == SensorSource::MAG_XYZ) {
if (sensors.id >= MAG_COUNT_MAX) {
PX4_ERR("Number of simulated magnetometer %d out of range. Max: %d", sensors.id, MAG_COUNT_MAX);
return;
@@ -1003,10 +1003,10 @@ void SimulatorMavlink::send_mavlink_message(const mavlink_message_t &aMsg)
ssize_t len;
if (_ip == InternetProtocol::UDP) {
len = ::sendto(_fd, buf, bufLen, 0, (struct sockaddr *)&_srcaddr, sizeof(_srcaddr));
len = ::sendto(_fd, (const char *)buf, bufLen, 0, (struct sockaddr *)&_srcaddr, sizeof(_srcaddr));
} else {
len = ::send(_fd, buf, bufLen, 0);
len = ::send(_fd, (const char *)buf, bufLen, 0);
}
if (len <= 0) {
@@ -1134,7 +1134,18 @@ void SimulatorMavlink::run()
if (bind(_fd, (struct sockaddr *)&_myaddr, sizeof(_myaddr)) < 0) {
PX4_ERR("bind for UDP port %i failed (%i)", _port, errno);
#ifdef __PX4_WINDOWS
// Winsock SOCKETs are kernel handles, not CRT fds — using ::close()
// trips the UCRT _close.cpp assertion `(fh >= 0 && (unsigned)fh < (unsigned)_nhandle)`.
closesocket(_fd);
#else
::close(_fd);
#endif
#if defined(ENABLE_LOCKSTEP_SCHEDULER)
// Unblock the lockstep wait in simulator_mavlink_main so the
// rest of the startup script (including shutdown) can run.
_has_initialized.store(true);
#endif
return;
}
@@ -1142,7 +1153,7 @@ void SimulatorMavlink::run()
while (true) {
// Once we receive something, we're most probably good and can carry on.
int len = ::recvfrom(_fd, _buf, sizeof(_buf), 0,
int len = ::recvfrom(_fd, (char *)_buf, sizeof(_buf), 0,
(struct sockaddr *)&_srcaddr, (socklen_t *)&_addrlen);
if (len > 0) {
@@ -1181,7 +1192,12 @@ void SimulatorMavlink::run()
break;
} else {
#ifdef __PX4_WINDOWS
// See note above: Winsock SOCKETs need closesocket(), not ::close().
closesocket(_fd);
#else
::close(_fd);
#endif
system_usleep(500);
}
}
@@ -1237,7 +1253,7 @@ void SimulatorMavlink::run()
if (fds[0].revents & POLLIN) {
int len = ::recvfrom(_fd, _buf, sizeof(_buf), 0, (struct sockaddr *)&_srcaddr, (socklen_t *)&_addrlen);
int len = ::recvfrom(_fd, (char *)_buf, sizeof(_buf), 0, (struct sockaddr *)&_srcaddr, (socklen_t *)&_addrlen);
if (len > 0) {
mavlink_message_t msg;
@@ -87,9 +87,9 @@ using namespace time_literals;
//! Enumeration to use on the bitmask in HIL_SENSOR
enum class SensorSource {
ACCEL = 0b111,
GYRO = 0b111000,
MAG = 0b111000000,
ACCEL_XYZ = 0b111,
GYRO_XYZ = 0b111000,
MAG_XYZ = 0b111000000,
BARO = 0b1101000000000,
DIFF_PRESS = 0b10000000000
};
@@ -65,7 +65,7 @@ static void configure_udp_socket_nonblocking(int fd)
// Prevent deadlocks in send() under RX buffer starvation by making the socket non-blocking.
int nonblock = 1;
if (ioctl(fd, FIONBIO, (unsigned long)&nonblock) != OK) {
if (ioctl(fd, FIONBIO, &nonblock) != OK) {
PX4_WARN("failed to set non-blocking (%i)", errno);
}
}
@@ -133,6 +133,7 @@ bool UxrceddsClient::init()
deinit();
if (_transport == Transport::Serial) {
#if defined(UXRCE_DDS_CLIENT_SERIAL)
int fd = open(_device, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0) {
@@ -165,6 +166,10 @@ bool UxrceddsClient::init()
_transport_serial = nullptr;
return false;
#else
PX4_ERR("serial transport not supported on this platform");
return false;
#endif // UXRCE_DDS_CLIENT_SERIAL
}
#if defined(UXRCE_DDS_CLIENT_UDP)
@@ -185,6 +190,14 @@ bool UxrceddsClient::init()
} else {
PX4_ERR("init UDP agent IP:%s, port:%s failed", _agent_ip, _port);
// Symmetric with the serial path above: free the half-initialised
// transport here rather than leaving it dangling for the next
// init() call to clean up. uxr_init_udp_platform() now releases
// its socket / WSAStartup ref on its own failure paths, so
// dropping the C++ wrapper here does not leak Winsock state.
delete _transport_udp;
_transport_udp = nullptr;
}
}
@@ -195,12 +208,16 @@ bool UxrceddsClient::init()
void UxrceddsClient::deinit()
{
#if defined(UXRCE_DDS_CLIENT_SERIAL)
if (_transport_serial) {
uxr_close_serial_transport(_transport_serial);
delete _transport_serial;
_transport_serial = nullptr;
}
#endif // UXRCE_DDS_CLIENT_SERIAL
#if defined(UXRCE_DDS_CLIENT_UDP)
if (_transport_udp) {
@@ -417,11 +434,15 @@ UxrceddsClient::~UxrceddsClient()
delete_repliers();
#if defined(UXRCE_DDS_CLIENT_SERIAL)
if (_transport_serial) {
uxr_close_serial_transport(_transport_serial);
delete _transport_serial;
}
#endif // UXRCE_DDS_CLIENT_SERIAL
perf_free(_loop_perf);
perf_free(_loop_interval_perf);
@@ -710,7 +731,7 @@ void UxrceddsClient::run()
int fd_bytes_available = 0;
if ((_fd < 0)
|| (ioctl(_fd, FIONREAD, (unsigned long)&fd_bytes_available) != OK)
|| (ioctl(_fd, FIONREAD, &fd_bytes_available) != OK)
|| (fd_bytes_available <= 0)) {
break;
}
@@ -1000,10 +1021,14 @@ int UxrceddsClient::print_status()
#endif
#if defined(UXRCE_DDS_CLIENT_SERIAL)
if (_transport_serial != nullptr) {
PX4_INFO("Using transport: serial");
}
#endif // UXRCE_DDS_CLIENT_SERIAL
if (_connected) {
PX4_INFO("Payload tx: %i B/s", _last_payload_tx_rate);
PX4_INFO("Payload rx: %i B/s", _last_payload_rx_rate);
@@ -50,11 +50,27 @@
# define UXRCE_DDS_CLIENT_UDP 1
#endif
// Micro-XRCE-DDS-Client's Windows platform has no serial transport — its
// CMakeLists forces UCLIENT_PROFILE_SERIAL OFF under UCLIENT_PLATFORM_WINDOWS,
// so the `uxrSerialTransport` type simply isn't emitted. Gate PX4's
// serial wiring on the same build-time knob so the whole module keeps
// compiling on Windows while serial stays available on NuttX/Linux.
#if !defined(__PX4_WINDOWS)
# define UXRCE_DDS_CLIENT_SERIAL 1
#endif
#include "srv_base.h"
#define MAX_NUM_REPLIERS 5
#define STREAM_HISTORY 4
#define BUFFER_SIZE (UXR_CONFIG_SERIAL_TRANSPORT_MTU * STREAM_HISTORY) // MTU==512 by default
// Pick the largest MTU across the profiles enabled for this build. The
// serial transport's MTU is the historical cap, so on platforms where
// serial is compiled out (Windows) we fall back to the UDP MTU.
#if defined(UXRCE_DDS_CLIENT_SERIAL)
# define BUFFER_SIZE (UXR_CONFIG_SERIAL_TRANSPORT_MTU * STREAM_HISTORY)
#else
# define BUFFER_SIZE (UXR_CONFIG_UDP_TRANSPORT_MTU * STREAM_HISTORY)
#endif
class UxrceddsClient : public ModuleBase, public ModuleParams
{
@@ -144,7 +160,9 @@ private:
Transport _transport{};
uxrSerialTransport *_transport_serial{nullptr};
#if defined(UXRCE_DDS_CLIENT_SERIAL)
uxrSerialTransport *_transport_serial {nullptr};
#endif
char _device[32] {};
int _baudrate{};