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 end
MOTIF_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