Skip to content

Commit 7595391

Browse files
Scripts to Patch ArduPilot for UAV Toolbox : v1.0
0 parents  commit 7595391

12 files changed

+634
-0
lines changed

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Scripts to Patch ArduPilot® for UAV Toolbox
2+
3+
This repository provides an automation script that applies specific modifications to the ArduPilot® codebase in order to integrate a Simulink®-specific library (AC_Simulink).
4+
5+
This repository is designed to use alongside the UAV Toolbox Support Package for ArduPilot® Autopilots
6+
7+
8+
## Prerequisites
9+
- A local checkout of the ArduCopter-4.6.2 or ArduPlane-4.6.2 repository (GPL v3).
10+
11+
### MathWorks Products (https://www.mathworks.com)
12+
13+
Requires MATLAB® release R2025b
14+
- [MATLAB®](https://www.mathworks.com/products/matlab.html)
15+
- [Simulink®](https://www.mathworks.com/products/simulink.html)
16+
- UAV Toolbox Support Package for ArduPilot® Autopilots
17+
18+
## Installation
19+
Clone this repository:
20+
21+
```
22+
git clone https://github.com/mathworks-robotics/scripts-patch-ardupilot-uav-toolbox
23+
cd scripts-patch-ardupilot-uav-toolbox
24+
git checkout v1.0
25+
```
26+
27+
28+
## Usage
29+
30+
31+
Run the automation script in MATLAB, providing the path to your local ArduPilot repository and the intended vehicle type.
32+
Currently, only **`Copter`** and **`Plane`** are supported as valid vehicle types.
33+
34+
`runArduPilotPatch(<path-to-ardupilot-repo>, 'Copter')
35+
`
36+
37+
## License
38+
39+
The license is available in the License.txt file in this GitHub repository.
40+
41+
## Community Support
42+
You can post your queries on the [MATLAB Central]() page for the support package.<!--- Link to the UAV Toolbox Support package for ArduPilot Autopilots file exchange will be added when it is created-->
43+
You can also add your questions at [MATLAB Answers](https://www.mathworks.com/matlabcentral/answers/index).
44+
45+
Copyright 2025 The MathWorks, Inc.
46+

SECURITY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Reporting Security Vulnerabilities
2+
3+
If you believe you have discovered a security vulnerability, please report it to
4+
[[email protected]](mailto:[email protected]). Please see
5+
[MathWorks Vulnerability Disclosure Policy for Security Researchers](https://www.mathworks.com/company/aboutus/policies_statements/vulnerability-disclosure-policy.html)
6+
for additional information.

applyCodeInsertions.m

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
function applyCodeInsertions(rootPath, jsonPath)
2+
% APPLYCODEINSERTIONS Apply code insertions/replacements described in JSON.
3+
% Skips any insertion if the same TAG is already present in the target file.
4+
%
5+
% applyCodeInsertions(rootPath, jsonPath)
6+
%
7+
% Inputs
8+
% rootPath - repository root (string)
9+
% jsonPath - path to JSON config (string)
10+
%
11+
% Behavior
12+
% - For each file entry in JSON, reads the file into memory and processes
13+
% insertions in ascending line order.
14+
% - Before applying an insertion/replacement that would add a TAG block,
15+
% searches the file for the start tag "<comment_style> TAG: <tag>".
16+
% If found, the insertion/replacement is skipped to avoid duplicate inserts.
17+
% - Writes updated file content with LF line endings.
18+
19+
% Load and decode the JSON configuration file
20+
jsonText = fileread(jsonPath);
21+
config = jsondecode(jsonText);
22+
23+
% Iterate over each file entry in the JSON
24+
for f = 1:length(config.files)
25+
fileEntry = config.files(f);
26+
27+
% Convert relative path to platform-specific format
28+
relParts = strsplit(fileEntry.path, '/'); % Split path by '/'
29+
relPath = fullfile(relParts{:}); % Reconstruct using platform-specific separator
30+
filePath = fullfile(rootPath, relPath); % Combine with root path
31+
32+
% Check if the file exists before attempting to modify
33+
if ~isfile(filePath)
34+
error('File not found: %s', filePath);
35+
end
36+
37+
% Determine tag prefix from JSON or fallback to default
38+
if isfield(fileEntry, 'comment_style')
39+
tagPrefix = strtrim(fileEntry.comment_style); % Use specified comment style
40+
else
41+
tagPrefix = '//'; % Default to C/C++ style
42+
end
43+
44+
% Read the file content into a string array (one line per element)
45+
fileContent = readlines(filePath);
46+
47+
% Sort insertions by line number ascending
48+
% This ensures we apply edits top-to-bottom and can track line shifts correctly
49+
insertions = fileEntry.insertions;
50+
[~, idx] = sort([insertions.line]);
51+
insertions = insertions(idx);
52+
53+
% Initialize line shift tracker
54+
% This variable tracks how many lines have been added or removed
55+
% so that subsequent operations can adjust their target line numbers
56+
lineShift = 0;
57+
58+
% Process each insertion or replacement in order
59+
for i = 1:length(insertions)
60+
ins = insertions(i);
61+
originalLine = ins.line; % Line number from JSON
62+
adjustedLine = originalLine + lineShift; % Adjusted for prior edits
63+
64+
codeBlock = string(ins.code); % Convert code block to string array
65+
opType = lower(strtrim(ins.type)); % Normalize operation type
66+
tag = strtrim(ins.tag); % Clean up tag string
67+
68+
%Wrap code block with tag markers using appropriate comment style
69+
startTagLine = strcat(tagPrefix , " TAG: " , tag);
70+
endTagLine = strcat(tagPrefix , " END TAG: " , tag);
71+
taggedBlock = [startTagLine; string(codeBlock); endTagLine];
72+
73+
% If the tag already exists anywhere in the file, skip this insertion
74+
% We search for an exact line match of the start tag.
75+
tagExistsIdx = find(fileContent == startTagLine, 1, 'first');
76+
if ~isempty(tagExistsIdx)
77+
fprintf('Skipping insertion for tag "%s" in %s: tag already present at line %d.\n', tag, filePath, tagExistsIdx);
78+
% Do not modify fileContent or lineShift; continue to next insertion
79+
continue;
80+
end
81+
82+
% Apply the operation
83+
switch opType
84+
case "insert"
85+
% Insert the tagged block before the adjusted line
86+
fileContent = [fileContent(1:adjustedLine-1); taggedBlock; fileContent(adjustedLine:end)];
87+
lineShift = lineShift + length(taggedBlock); % Update shift by number of inserted lines
88+
fprintf('Inserted tag "%s" into %s at line %d.\n', tag, filePath, adjustedLine);
89+
90+
case "replace"
91+
% Replace the adjusted line with the tagged block
92+
fileContent = [fileContent(1:adjustedLine-1); taggedBlock; fileContent(adjustedLine+1:end)];
93+
lineShift = lineShift + length(taggedBlock) - 1; % Update shift by net line change
94+
fprintf('Replaced line %d in %s with tag "%s".\n', adjustedLine, filePath, tag);
95+
96+
otherwise
97+
error('Unknown operation type "%s" in file %s.', opType, filePath);
98+
end
99+
end
100+
101+
% Write the modified content back to the original file
102+
writeLinesWithLF(fileContent, filePath);
103+
end
104+
end
105+
106+
function writeLinesWithLF(lines, filePath)
107+
% WRITELINESWITHLF Writes lines to a file using LF-only line endings.
108+
% This avoids platform-dependent CRLF endings (e.g., on Windows).
109+
fid = fopen(filePath, 'w');
110+
if fid == -1
111+
error('Failed to open file: %s', filePath);
112+
end
113+
114+
for i = 1:length(lines)-1
115+
fprintf(fid, '%s\n', lines(i)); % Explicitly use \n for LF
116+
end
117+
% LF not needed for last line
118+
fprintf(fid, '%s', lines(length(lines)));
119+
fclose(fid);
120+
end

common/AC_Simulink_Base.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
3+
#include <AP_Common/AP_Common.h>
4+
5+
class AC_Simulink_Base {
6+
public:
7+
virtual ~AC_Simulink_Base() {}
8+
virtual void init() = 0;
9+
virtual void update() = 0;
10+
virtual void reset() = 0;
11+
};

common/AC_Simulink_Empty.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
3+
#include "AC_Simulink_Empty.h"
4+
AC_Simulink_Empty::AC_Simulink_Empty() {}
5+
6+
void AC_Simulink_Empty::init() {
7+
}
8+
9+
void AC_Simulink_Empty::update() {
10+
}
11+
12+
void AC_Simulink_Empty::reset() {
13+
14+
}

common/AC_Simulink_Empty.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
/// @file AC_Simulink_Empty.h
4+
/// @brief External mode implementation of Simulink integration
5+
6+
#include "AC_Simulink_Base.h"
7+
8+
class AC_Simulink_Empty : public AC_Simulink_Base {
9+
public:
10+
AC_Simulink_Empty();
11+
~AC_Simulink_Empty() override {}
12+
13+
void init() override;
14+
void update() override;
15+
void reset() override;
16+
17+
CLASS_NO_COPY(AC_Simulink_Empty);
18+
};

common/AC_Simulink_Factory.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "AC_Simulink_Factory.h"
2+
3+
AC_Simulink_Base* AC_Simulink_Factory::createSimulinkInstance() {
4+
#ifdef MW_EXTERNAL_MODE
5+
return new AC_Simulink_ExtMode();
6+
#elif defined(MW_NORMAL_MODE)
7+
return new AC_Simulink_Normal();
8+
#elif defined(MW_CONNECTEDIO_MODE)
9+
return new AC_Simulink_ConnectedIO();
10+
#else
11+
return new AC_Simulink_Empty();
12+
#endif
13+
}

common/AC_Simulink_Factory.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
3+
#include "AC_Simulink_Base.h"
4+
#ifdef MW_EXTERNAL_MODE
5+
#include "AC_Simulink_ExtMode.h"
6+
#elif defined(MW_NORMAL_MODE)
7+
#include "AC_Simulink_Normal.h"
8+
#elif defined(MW_CONNECTEDIO_MODE)
9+
#include "AC_Simulink_ConnectedIO.h"
10+
#else
11+
#include "AC_Simulink_Empty.h"
12+
#endif
13+
class AC_Simulink_Factory {
14+
public:
15+
static AC_Simulink_Base* createSimulinkInstance();
16+
};

0 commit comments

Comments
 (0)