ADI ADC Simulink Model 在MatlabR2025a上适配修改,解决警告和报错的问题(以AD9268为例,其他的也可使用)
Matlab R2025a对语法的要求更高,以前版本的无法直接工作,需要进行修改:


主要是修改2个文件:
AD9268_sysobj.m
% Copyright (c) 2014, Analog Devices Inc.
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% 1. Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
%
% 2.Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% 3. Neither the name of the copyright holder nor the names of its
% contributors may be used to endorse or promote products derived from this
% software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
% IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
% THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
% PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
% CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
% EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
% PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
% LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
classdef AD9268_sysobj < matlab.System
% System Object behavioral model for Analog Devices' High-Speed ADCs
% Notes:
% - The Resolution dropdown filters the ADC options
% - In the name of the ADC is the maximum sampling clock, be sure
% to adjust your sampling clock accordingly.
properties
% Specific Options
SpecificOptions = '125 MSPS';
% Sampling Clock Frequency (Hz)
Fclk = 125e6;
% Mean Frequency (Hz)
Tessitura = 2.3e6;
% RMS Clock Jitter (sec)
ExtJitter = 6e-014;
% Input Configuration
InputConfig = 'Normalized';
end
properties (Access = private)
pm; % MOTIF Object
poffset; % ADC Offset
prange; % ADC Range
pgeneric; % Generic name
pspecificOptionsMap; % Specific Options map
end
properties(Constant, Hidden)
SpecificOptionsSet = matlab.system.StringSet({'80 MSPS', '105 MSPS', '125 MSPS'});
InputConfigSet = matlab.system.StringSet({'Normalized', 'Absolute'});
end
properties (DiscreteState)
end
methods
% Constructor
function obj = AD9268_sysobj(varargin)
% Support name-value pair arguments when constructing the
% object.
% Assign generic
obj.pgeneric = 'AD9268';
% Build options maps (note: indexing SpecificOptionsSet is
% impossible)
obj.pspecificOptionsMap = containers.Map;
obj.pspecificOptionsMap('80 MSPS') = '80';
obj.pspecificOptionsMap('105 MSPS') = '105';
obj.pspecificOptionsMap('125 MSPS') = '125';
% Add MOTIF path
modelPath = get_param(gcs,'FileName');
modelFolder = fileparts(modelPath);
resourcesFolder = fullfile(modelFolder, 'MOTIF');
addpath(resourcesFolder);
setProperties(obj,nargin,varargin{:});
end
end
methods (Static, Access = protected)
function header = getHeaderImpl
header = matlab.system.display.Header(mfilename('class'),...
'Title','System Object for an ADC',...
'Text','This is a behavioral model of an ADC.',...
'ShowSourceLink',false);
end
end
methods (Access = protected)
%% Common functions
function setupImpl(obj)
% Implement tasks that need to be performed only once,
% such as pre-computed constants.
modelPath = obj.determineModelName();
obj.pm = MOTIF_if(['MOTIF' filesep modelPath]);
if (~obj.pm.isLoaded())
error('Error: Could not open file! Check to see if you have the model file downloaded in your path.');
end
% Get maximum sampling rate, and coerce if necessary
clkmax = str2double(obj.pm.getProp('settings', 'clkmax'));
if obj.Fclk > clkmax
fclk = clkmax;
warning('Sampling Rate was too high, coerced to maximum for this device');
else
fclk = obj.Fclk;
end
% Push simulation properties to MOTIF
obj.pm.setProp('GLOBAL', 'fclk', num2str(fclk));
obj.pm.setProp('GLOBAL', 'tessitura', num2str(obj.Tessitura));
obj.pm.setProp('settings', 'extjitter', num2str(obj.ExtJitter));
if strcmp(obj.InputConfig, 'Normalized')
obj.poffset = str2double(obj.pm.getProp('settings', 'offset'));
obj.prange = str2double(obj.pm.getProp('settings', 'range'));
else
obj.poffset = 0;
obj.prange = 2;
end
end
function modelPath = determineModelName(obj)
% Determines the currently selected model name and saves to
% pmodelPath
modelPath = obj.pgeneric;
if ~strcmp(obj.SpecificOptions, '[empty]')
specificOptions = obj.pspecificOptionsMap(obj.SpecificOptions);
modelPath = [modelPath '_' specificOptions];
end
modelPath = [modelPath '.adc'];
end
function y = stepImpl(obj, u)
% Implement algorithm. Calculate y as a function of
% input u and discrete states.
y = obj.pm.runSamples(u * obj.prange / 2 + obj.poffset);
end
function releaseImpl(obj)
% Initialize discrete-state properties.
obj.pm.destroy();
end
% This method controls visibility of the object's properties
function flag = isInactivePropertyImpl(obj, propertyName)
flag = false;
if strcmp(propertyName, 'SpecificOptions')
if strcmp(obj.SpecificOptions, '[empty]')
flag = true;
end
end
end
function icon = getIconImpl(obj)
icon = obj.pgeneric;
end
function dataout = getOutputDataTypeImpl(~)
dataout = 'double';
end
function sizeout = getOutputSizeImpl(~)
sizeout = [1 1];
end
function cplxout = isOutputComplexImpl(~)
cplxout = false;
end
function fixedout = isOutputFixedSizeImpl(~)
fixedout = true;
end
function num = getNumInputsImpl(~)
num = 1;
end
function varargout = getInputNamesImpl(obj)
numInputs = getNumInputs(obj);
varargout = cell(1,numInputs);
varargout{1} = 'in (V)';
end
function varargout = getOutputNamesImpl(~)
varargout = cell(1,1);
varargout{1} = 'out (Code)';
end
end
endMOTIF_if.m
% Copyright (c) 2014, Analog Devices Inc.
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% 1. Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
%
% 2.Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% 3. Neither the name of the copyright holder nor the names of its
% contributors may be used to endorse or promote products derived from this
% software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
% IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
% THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
% PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
% CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
% EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
% PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
% LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
classdef MOTIF_if < handle
%UNTITLED Summary of this class goes here
% Detailed explanation goes here
properties (Access = protected)
key = 0;
end
methods
function obj = MOTIF_if(filename)
% OS specific code
if ispc()
prefix = '';
suffix = '.dll';
else
prefix = 'lib';
suffix = '.so';
end
library_name = [prefix 'MOTIF' suffix];
% Check to see if the library is loaded already
if (~libisloaded('MOTIF'))
loadlibrary(library_name, 'MOTIF.h', 'alias', 'MOTIF');
end
% Try and resolve absolute path
if ~exist(filename, 'file')
tf = ['MOTIF' filesep filename];
if exist(tf, 'file')
filename = tf;
else
error_msg = sprintf('Cannot find model: %s', filename);
error(error_msg);
end
end
% Load the PMF into memory
obj.key = calllib('MOTIF', 'ImportPMF', filename);
end
function key = getKey(obj)
key = obj.key;
end
function isLoaded = isLoaded(obj)
isLoaded = ~(obj.key == 0);
end
function interface = queryInterface(obj)
direction = 0; % Input
in_count = obj.getPortCount(direction);
% Added check for in_count to prevent negative indexing if in_count is 0.
if in_count > 0
interface.in = obj.queryPort(direction, in_count - 1);
else
% Handle case where no input port is found, e.g., assign default or throw error
interface.in.f = 1;
interface.in.is_complex = 0;
interface.in.domain = '[unspecified]';
interface.in.unit = '[unspecified]';
end
direction = 1; % Output
out_count = obj.getPortCount(direction);
% Added check for out_count.
if out_count > 0
interface.out = obj.queryPort(direction, out_count - 1);
else
interface.out.f = 1;
interface.out.is_complex = 0;
interface.out.domain = '[unspecified]';
interface.out.unit = '[unspecified]';
end
% Ensure interface.in.f is not zero to prevent division by zero
if interface.in.f ~= 0
interface.r = interface.out.f / interface.in.f;
else
interface.r = 1; % Default or error handling
end
end
function [modeName, modeDisplayName] = getMode(obj)
response = obj.processMessage('<getmode />');
modeElements = MOTIF_if.getChildren(response);
% Added check for empty modeElements
if isempty(modeElements)
modeName = '';
modeDisplayName = '';
return;
end
attributes = MOTIF_if.getAttributes(modeElements(1));
% Added checks for key existence
modeName = '';
if isKey(attributes, 'mn')
modeName = attributes('mn');
end
modeDisplayName = '';
if isKey(attributes, 'mdn')
modeDisplayName = attributes('mdn');
end
end
function modes = queryModes(obj)
% Returns a Maps that contain the mode display names
response = obj.processMessage('<querymodes />');
queryModesElements = MOTIF_if.getChildren(response);
modes = containers.Map; % Initialize modes map
% Added check for empty queryModesElements
if isempty(queryModesElements)
return; % Return empty map if no modes are found
end
modeElements = MOTIF_if.getChildren(queryModesElements(1));
for i = 1:length(modeElements)
attributes = MOTIF_if.getAttributes(modeElements(i));
% Added check if 'mn' and 'mdn' attribute exists
if isKey(attributes, 'mn') && isKey(attributes, 'mdn')
modes(attributes('mn')) = attributes('mdn');
end
end
end
function printModes(obj)
modes = obj.queryModes();
ks = keys(modes);
for k_idx = 1:length(ks)
k = ks(k_idx);
mode = modes(k{1});
disp([k{1} ' : ' mode]);
end
end
function setMode(obj, mode)
msg = ['<setmode>' mode '</setmode>'];
obj.processMessage(msg);
end
function [value, displayName, permission, limits, type, unit, toolTip] = getProp(obj, blockName, propName)
response = obj.processMessage(['<getprop bn="' blockName '" pn="' propName '"/>']);
getPropElements = MOTIF_if.getChildren(response);
% Added check for empty getPropElements
if isempty(getPropElements)
value = ''; displayName = ''; permission = ''; limits = ''; type = ''; unit = ''; toolTip = '';
return;
end
value = MOTIF_if.getText(getPropElements(1));
attributes = MOTIF_if.getAttributes(getPropElements(1));
% Added checks for key existence
displayName = ''; if isKey(attributes, 'pdn'), displayName = attributes('pdn'); end
permission = ''; if isKey(attributes, 'p'), permission = attributes('p'); end
limits = ''; if isKey(attributes, 'l'), limits = attributes('l'); end
type = ''; if isKey(attributes, 't'), type = attributes('t'); end
unit = ''; if isKey(attributes, 'u'), unit = attributes('u'); end
toolTip = ''; if isKey(attributes, 'tt'), toolTip = attributes('tt'); end
end
function props = queryProps(obj)
% Returns a Map of Maps that contain the properties
% Explanantion of keys -
% pn = property name
% pdn = property display name
% bn = block name
% bdn = block display name
% p = permissions
% l = limits
% t = type
% u = unit
% tt = tool tip
response = obj.processMessage('<queryprops />');
queryPropsElements = MOTIF_if.getChildren(response);
props = containers.Map; % Initialize props map
% Added check for empty queryPropsElements
if isempty(queryPropsElements)
return;
end
propElements = MOTIF_if.getChildren(queryPropsElements{1});
for i = 1:length(propElements)
value = MOTIF_if.getText(propElements{i});
attributes = MOTIF_if.getAttributes(propElements{i});
attributes('value') = value;
% Added check if 'bn' and 'pn' attributes exist
if isKey(attributes, 'bn') && isKey(attributes, 'pn')
k = [attributes('bn') '.' attributes('pn')];
props(k) = attributes;
end
end
end
function props = queryPropValues(obj)
% Returns a Map that values
response = obj.processMessage('<queryprops />');
queryPropsElements = MOTIF_if.getChildren(response);
props = containers.Map; % Initialize props map
% Added check for empty queryPropsElements
if isempty(queryPropsElements)
return;
end
propElements = MOTIF_if.getChildren(queryPropsElements{1});
for i = 1:length(propElements)
value = MOTIF_if.getText(propElements{i});
attributes = MOTIF_if.getAttributes(propElements{i});
% Added check if 'bn' and 'pn' attributes exist
if isKey(attributes, 'bn') && isKey(attributes, 'pn')
k = [attributes('bn') '.' attributes('pn')];
props(k) = value;
end
end
end
function printProps(obj)
props = obj.queryProps();
k0s = keys(props);
for k0_idx = 1:length(k0s)
k0 = k0s(k0_idx);
prop = props(k0{1});
disp([k0{1} ' :']);
k1s = keys(prop);
for k1_idx = 1:length(k1s)
k1 = k1s(k1_idx);
attribute = prop(k1{1});
disp([' ' k1{1} ' : ' attribute]);
end
end
end
function setProp(obj, blockName, propName, value)
msg = ['<setprop bn="' blockName '" pn="' propName '">' value '</setprop>'];
obj.processMessage(msg);
end
function versionInfo = queryVersion(obj)
% Returns a Map that contain the version information
% Explanantion of keys -
% dllversion = version of MOTIF simulator
% pmfversion = version of Product Model File
response = obj.processMessage('<queryversion />');
queryVersionElements = MOTIF_if.getChildren(response);
% Added check for empty queryVersionElements
if isempty(queryVersionElements)
versionInfo = containers.Map; % Return empty map
return;
end
versionInfo = MOTIF_if.getAttributes(queryVersionElements(1));
end
function printVersion(obj)
versionInfo = queryVersion(obj);
if ~isempty(versionInfo) && isKey(versionInfo, 'dllversion') && isKey(versionInfo, 'pmfversion')
disp(['Library Version: ', versionInfo('dllversion')]);
disp(['PMF Version: ', versionInfo('pmfversion')]);
else
disp('Version information not available.');
end
end
function [out, interface] = runSamples(obj, in)
% Save input length
len = length(in);
interface = obj.queryInterface();
% Format input
if (interface.in.is_complex)
tin = zeros(1, len*2); % Pre-allocate for efficiency
tin(1:2:len*2) = real(in);
tin(2:2:len*2) = imag(in);
else
tin = real(in);
end
% Initialize the output code array
if (interface.out.is_complex)
out_len = ceil(len*2*interface.r);
else
out_len = ceil(len*interface.r);
end
% Ensure out_len is at least 0 and an integer
out_len = max(0, floor(out_len));
out = zeros(out_len, 1);
% Handle case where obj.key might be 0 or if runSamples fails
if obj.key == 0
warning('MOTIF_if object not loaded, cannot run samples. Returning zeros.');
out = zeros(size(in)); % Return zero-filled output of same size as input
return;
end
% Check if tin is empty, calllib might error with empty arrays
if isempty(tin) && len > 0
warning('Input array for RunSamples is empty but expected length > 0. Returning zeros.');
out = zeros(out_len, 1);
return;
end
% If out_len is 0, no need to calllib, just return empty or zeros based on requirement
if out_len == 0
out = zeros(0,1); % Return empty column vector if no output samples expected
return;
end
% calllib can sometimes modify tin in place even if not explicitly defined as output.
% Be aware if there are issues, a copy might be needed: tin_copy = tin;
[~, ~, out] = calllib('MOTIF', 'RunSamples', tin, out, len, obj.key);
if (interface.out.is_complex)
% Ensure idx+1 does not exceed out_len
% Adjusted to prevent out of bounds access if out_len is odd or too small
idx = 1:2:(out_len - 1);
if ~isempty(idx) % Only proceed if idx is not empty
out = out(idx) + 1i*out(idx+1);
else
out = zeros(0,1); % If no valid complex pairs, return empty column vector
end
end
end
function destroy(obj)
if obj.key ~= 0 && libisloaded('MOTIF')
calllib('MOTIF', 'Destroy', obj.key);
obj.key = 0; % Clear key after destroying
end
end
end
methods (Access = protected)
function count = getPortCount(obj, direction)
count = 0;
if obj.key ~= 0 && libisloaded('MOTIF')
[~, count] = calllib('MOTIF', 'GetPortCount', count, direction, obj.key);
else
warning('MOTIF library not loaded or object key is invalid. Returning 0 port count.');
end
end
function port = queryPort(obj, direction, index)
% Initialize return variable with safe defaults
port.f = 1;
port.is_complex = 0;
port.domain = '[unspecified]';
port.unit = '[unspecified]';
% Only proceed if index is valid and MOTIF is loaded
if index < 0
warning('Invalid port index (negative). Returning default port info.');
return;
end
if (obj.key == 0) || (~libisloaded('MOTIF'))
warning('MOTIF library not loaded or object key is invalid. Returning default port info.');
return
end
f_val = 1;
is_complex_val = 0;
domain_val = 0;
unitPtr_for_calllib = libpointer('voidPtrPtr'); % 创建 libpointer 对象用于 calllib
% 调用 calllib,并捕获第五个输出参数,它将是 C 函数写入到 unitPtr_for_calllib 中的实际指针地址。
[~, f, is_complex, domain, actual_unit_ptr_value] = calllib('MOTIF', 'QueryPort', f_val, is_complex_val, domain_val, unitPtr_for_calllib, direction, index, obj.key);
port.f = f;
port.is_complex = is_complex;
if domain == 1
port.domain = 'analog';
elseif domain == 2
port.domain = 'digital';
else
port.domain = '[unspecified]';
end
% 检查返回的指针地址是否为 NULL (0)
if actual_unit_ptr_value ~= 0
% 如果不是 NULL,则使用该地址创建一个新的 libpointer,并指定其为字符串指针
unit_str_ptr = libpointer('stringPtr', actual_unit_ptr_value);
port.unit = unit_str_ptr.Value; % 安全地检索字符串值
else
port.unit = '[unspecified]'; % 如果是 NULL 指针,使用默认值
end
end
function response = processMessage(obj, msg)
response = ''; % Initialize response as empty string
msg = ['<motif>' msg '</motif>'];
if (obj.key == 0) || (~libisloaded('MOTIF'))
warning('MOTIF library not loaded or object key is invalid. Cannot process message.');
return;
end
responsePtr = libpointer('voidPtrPtr'); % 创建 libpointer 对象
% 调用 calllib,并捕获第三个输出参数,它将是 C 函数写入到 responsePtr 中的实际指针地址。
[~, ~, actual_response_ptr_value] = calllib('MOTIF', 'ProcessMessage', msg, responsePtr, obj.key);
% 检查返回的指针地址是否为 NULL (0)
if actual_response_ptr_value ~= 0
% 如果不是 NULL,则使用该地址创建一个新的 libpointer,并指定其为字符串指针
string_ptr = libpointer('stringPtr', actual_response_ptr_value);
response = string_ptr.Value; % 安全地检索字符串值
else
warning('Received NULL response pointer from MOTIF ProcessMessage.');
% response 已经初始化为空字符串,无需额外处理
end
end
end
methods (Static, Access = public)
function destroyAll()
if (libisloaded('MOTIF')) % only call if loaded
calllib('MOTIF', 'DestroyAll');
unloadlibrary('MOTIF')
end
end
function str = bool2str(boolVal)
if (boolVal)
str = 'true';
else
str = 'false';
end
end
end
methods (Static, Access = protected)
function childrenStrings = getChildren(xmlString)
xmlString = char(xmlString);
childrenStrings = {}; % 初始值为空,以便在解析失败时直接返回
% 1. 检查输入的 XML 字符串是否为空
if isempty(xmlString)
return;
end
% 查找当前元素的起始标签 '>'
gt_indices = strfind(xmlString, '>');
if isempty(gt_indices)
% 如果没有找到 '>', 说明 XML 格式不完整或错误
return;
end
firstChildIdx = gt_indices(1) + 1; % 第一个 '>' 字符的下一个位置
% 2. 检查提取元素名称的索引范围是否有效
elementEndIdx = firstChildIdx - 2;
if elementEndIdx < 1 || elementEndIdx > length(xmlString)
% 尝试处理 <element/> 这种情况
if ~isempty(strfind(xmlString, '/>')) && (firstChildIdx - 2 == strfind(xmlString, '/>'))
return; % 这是一个自闭合标签,没有子节点
end
return; % 否则,无法解析
end
element = xmlString(2:elementEndIdx); % 提取元素名
idx_space = strfind(element, ' ');
if ~isempty(idx_space)
element = element(1:idx_space(1)-1);
end
% 如果 element 是空字符串,也无法继续解析
if isempty(element)
return;
end
% 查找对应的结束标签
lastChildIdx_candidate = strfind(xmlString, ['</' element '>']);
% 3. 处理自闭合标签(如 <tag />)和常规标签
if isempty(lastChildIdx_candidate)
% 如果没有找到显式的结束标签,检查是否为自闭合标签
selfClosingIdx = strfind(xmlString, '/>');
if ~isempty(selfClosingIdx) && selfClosingIdx(1) < firstChildIdx
return; % 自闭合元素,没有子节点
end
return; % 否则,既不是常规结束标签也不是自闭合,无法解析子节点
end
lastChildIdx = lastChildIdx_candidate(1) - 1; % 结束标签开始前的位置
% 4. 确保冒号操作符的范围有效 (lastChildIdx 必须大于或等于 firstChildIdx)
if lastChildIdx < firstChildIdx
return; % 范围无效(例如,结束标签在内容之前),或者没有内容
end
rest = xmlString(firstChildIdx:lastChildIdx);
% 分割子字符串
while ~isempty(rest)
% 查找当前内部元素的起始标签 '>'
iStart_candidates = strfind(rest, '>');
if isempty(iStart_candidates)
break; % 'rest' 字符串格式错误,没有更多元素
end
iStart = iStart_candidates(1) + 1;
% 5. 对内部元素重复索引范围检查
innerElementEndIdx = iStart - 2;
if innerElementEndIdx < 1 || innerElementEndIdx > length(rest)
% 尝试处理 <element/> 这种情况
if ~isempty(strfind(rest, '/>'))
currentElementSelfClosingEnd = strfind(rest(1:min(length(rest), iStart-1)), '/>');
if ~isempty(currentElementSelfClosingEnd)
childrenStrings = [childrenStrings; {rest(1:currentElementSelfClosingEnd(1)+1)}];
rest = rest(currentElementSelfClosingEnd(1)+2:end);
continue;
end
end
break; % 内部元素起始位置错误,停止解析
end
element = rest(2:innerElementEndIdx); % 提取内部元素名
idx_space_inner = strfind(element, ' ');
if ~isempty(idx_space_inner)
element = element(1:idx_space_inner(1)-1);
end
% 如果 element 是空字符串,也无法继续解析
if isempty(element)
break;
end
% 查找内部元素的结束标签
iStop_candidates = strfind(rest, ['</' element '>']);
if ~isempty(iStop_candidates)
iStop_val = iStop_candidates(1) + 3 + length(element) - 1; % 结束标签的结束位置
if iStop_val > length(rest)
break; % 结束索引超出范围
end
childrenStrings = [childrenStrings; {rest(1:iStop_val)}];
rest = rest(iStop_val+1:end);
else
% 检查是否为内部的自闭合标签
iStop_selfClosing_candidates = strfind(rest, '/>');
if ~isempty(iStop_selfClosing_candidates) && iStop_selfClosing_candidates(1) < iStart
iStop_val = iStop_selfClosing_candidates(1) + 1; % '/>' 的结束位置
if iStop_val > length(rest)
break; % 结束索引超出范围
end
childrenStrings = [childrenStrings; {rest(1:iStop_val)}];
rest = rest(iStop_val+1:end);
else
break; % 没有找到有效的结束或自闭合标签,停止解析
end
end
end
end
function attributes = getAttributes(elementString)
elementString = char(elementString);
attributes = containers.Map;
idx_gt = strfind(elementString, '>');
if isempty(idx_gt)
return; % Invalid XML fragment, no opening tag found
end
end_of_opening_tag = idx_gt(1);
idx_first_space = strfind(elementString(1:end_of_opening_tag), ' ');
if isempty(idx_first_space)
return; % No attributes
end
attr_start_idx = idx_first_space(1) + 1;
attr_end_idx = end_of_opening_tag - 1;
idx_self_closing = strfind(elementString(1:end_of_opening_tag), '/>');
if ~isempty(idx_self_closing)
attr_end_idx = idx_self_closing(1) - 1;
if attr_end_idx < attr_start_idx
return; % No attributes
end
end
if attr_end_idx < attr_start_idx
return; % No attributes found or malformed
end
attr_string = elementString(attr_start_idx : attr_end_idx);
current_pos = 1;
while current_pos <= length(attr_string)
% Skip leading spaces
while current_pos <= length(attr_string) && isspace(attr_string(current_pos))
current_pos = current_pos + 1;
end
if current_pos > length(attr_string)
break; % Reached end of string
end
% Find the '=' for the attribute name
idx_eq_relative = strfind(attr_string(current_pos:end), '=');
if isempty(idx_eq_relative)
break; % Malformed attribute (no = sign)
end
idx_eq_abs = current_pos + idx_eq_relative(1) - 1;
% Extract attribute name (key)
k = strtrim(attr_string(current_pos : idx_eq_abs - 1));
% Move past '='
current_pos = idx_eq_abs + 1;
% Check for opening quote
if current_pos > length(attr_string) || attr_string(current_pos) ~= '"'
break; % Malformed attribute (no opening quote or empty string)
end
% Move past opening quote
current_pos = current_pos + 1;
% Find closing quote
idx_quote_relative = strfind(attr_string(current_pos:end), '"');
if isempty(idx_quote_relative)
break; % Malformed attribute (no closing quote)
end
idx_quote_abs = current_pos + idx_quote_relative(1) - 1;
% Extract value
if (idx_quote_abs - 1) < current_pos
value = ''; % Handle empty value like attr=""
else
value = attr_string(current_pos : idx_quote_abs - 1);
end
attributes(k) = value;
% Move past closing quote
current_pos = idx_quote_abs + 1;
end
end
function text = getText(elementString)
elementString = char(elementString);
text = '';
idx_self_closing = strfind(elementString, '/>');
if ~isempty(idx_self_closing)
return
end
iStart_candidates = strfind(elementString, '>');
iStop_candidates = strfind(elementString, '<');
if isempty(iStart_candidates) || isempty(iStop_candidates)
return; % Invalid XML fragment
end
iStart = iStart_candidates(1) + 1;
iStop_after_start = iStop_candidates(iStop_candidates > iStart_candidates(1));
if isempty(iStop_after_start)
return; % No closing tag found after opening tag
end
if iStart <= length(elementString) && iStop_after_start(1) >= iStart
text = elementString(iStart:iStop_after_start(1)-1);
else
text = ''; % No valid text found or invalid range
end
end
end
end

