OSCR

Cortical motor activity modulates respiration and reduces apnoea in neonates.

Code ↔ Paper

15 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 15 matches · 7 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
  1. [1] § Materials and methods › Breath and apnoea identification ↔ ip_to_interbreath_intervals.m, lines 2–140 · score 0.79 · shallow breathing, identify breaths, standard deviation, IP signals, adaptive, machine
  2. [2] § Materials and methods › Data recordings ↔ functions/align_using_xcorr.m, the whole file · a weak match · score 0.79 · PiNe, ECG epochs, cross correlations, vital signs, temporal, box
  3. [3] § Materials and methods › Statistical analysis of relationships between cortico-respiratory coupling and apnoea ↔ functions/plot_coherence.m, lines 79–121 · score 0.73 · linear models, confounding factors, linear mixed, apnoea rate, coefficient, predictors
  4. [4] § Materials and methods › Phase-amplitude coupling ↔ functions/data_to_wavelet_time_coh.m, the whole file · a weak match · score 0.72 · Hanning window, cross spectrum, standard deviation, wavelet, epochs, median
  5. [5] § Materials and methods › Phase-amplitude coupling ↔ functions/data_to_wavelet_coh.m, the whole file · a weak match · score 0.71 · Hanning window, cross spectrum, standard deviation, wavelet, epochs, median
  6. [6] § Materials and methods › Statistical analysis of relationships between cortico-respiratory coupling and apnoea ↔ run_coherence_age_and_apnoea_rate.m, lines 13–95 · score 0.65 · linear mixed, apnoea rate, Rstudio, analyse, ID, ventilation
  7. [7] § Materials and methods › Surrogate analysis ↔ functions/permutation_testing_cfc.m, the whole file · a weak match · score 0.63 · surrogate spectra, FieldTrip, permutation, FDR, alpha, cross
  8. [8] § Materials and methods › Directionality of PAC ↔ functions/data_to_pac_and_psi.m, the whole file · a weak match · score 0.61 · frequency resolution, phase slope, cross spectrum, coherency, PAC
  9. [9] § Materials and methods › Changes in PAC throughout the respiratory cycle ↔ compute_time_pac.m, lines 23–147 · score 0.59 · surrogate PAC, EEG channels, cycle, theta, delta, modulation
  10. [10] § Materials and methods › Data recordings ↔ ip_to_interbreath_intervals.m, lines 2–140 · score 0.57 · oxygen saturation, heart rate, Breathing, infants, respiration
  11. [11] § Materials and methods › Data recordings ↔ subfunctions/remove_artifactual_longpauses.m, the whole file · a weak match · score 0.56 · oxygen saturation, heart rate, Breathing, infants, respiration
  12. [12] § Materials and methods › Breath and apnoea identification ↔ subfunctions/remove_artifactual_longpauses.m, the whole file · a weak match · score 0.56 · shallow breathing, IP signals, machine, vector, intervals, model
  13. [13] § Materials and methods › Surrogate analysis ↔ compute_time_pac.m, lines 23–147 · score 0.56 · surrogate PAC, EEG channels, permutation, alpha, coherence, amplitude
  14. [14] § Results and discussion › Respiratory and cortical activity exhibit PAC in infants ↔ compute_cortical_amplitude_modulation.m, lines 154–220 · score 0.52 · amplitude modulation, EEG amplitude, locked, spectral, 13 Hz, alpha
  15. [15] § Results and discussion › Cortico-respiratory coupling is strongest during inspiration and may be driven by cortical activity ↔ compute_cortical_amplitude_modulation.m, lines 154–220 · score 0.52 · amplitude modulation, cortical activity, spectral, cycle, EEG, respiratory

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

MATLAB · 1,678 lines · 54 KB · no license · 2 matches

  1. function [ibi,ibi_time,filtered_ip,filtered_ip_time,filtered_ip_fs] = ip_to_interbreath_intervals(ecg,ecg_time,ecg_fs,ip,ip_time,ip_fs,hr,sats,hr_time,sats_time,model_input)
  2. %% This function takes an impedance pneumograph signal as input, and outputs a series of interbreath-intervals, and the cleaned impedance pneumograph signal, with ECG artefact removed.
  3. %
  4. %*** ALL INPUTS SHOULD BE LONGER THAN 5 MINUTES, AND BE IN SECONDS***
  5. %The following toolboxes are required to run this function:
  6. % *Signal Processing Toolbox
  7. % *Statistics and Machine Learning Toolbox
  8. % *DSP System Toolbox
  9. %
  10. %Inputs:
  11. % ecg________________ecg signal. Use [], if there is no ecg input.
  12. % ecg_time___________times corresponding to the ecg signal. Use [], if there is no ecg_time input.
  13. % ecg_fs_____________sampling frequency of the ecg signal. Use [], if there is no ecg_fs input.
  14. % ip_________________impedance pneumograph signal
  15. % ip_time____________times corresponding to the impedance pneumograph signal
  16. % ip_fs______________sampling frequency of the impedance pneumograph signal
  17. % hr_________________heart rate signal
  18. % sats_______________oxygen saturation values
  19. % sats_time__________times corresponding to the sats values
  20. % hr_time____________times corresponding to the hr values
  21. % model_input________enter 1 if you want to the run the machine
  22. % learning model to remove ibi values greater than 20 which are
  23. % artifactual due to shallow breathing or poor electrode
  24. % placement. If this is not wanted, enter 0.
  25. %
  26. %Outputs: ibi__________________________interbreath-interval series consisting of the times between each breath
  27. % ibi_time_____________________times corresponding to the end breath of each interval
  28. % filtered_ip__________________cleaned impedance pneumograph signal
  29. % filtered_ip_time_____________time vector corresponding to the cleaned impedance pneumograph signal
  30. % filtered_ip_fs_______________sampling frequency of the cleaned impedance pneumograph signal
  31. %
  32. %
  33. % The function is made up of the following sections;
  34. % 1a) Check if the ECG signal is inverted (ie upside-down): r-peaks should be the highest peaks
  35. % b) NaNs in the ECG must be found, such that the ip at these times can be kept as the original ip signal, without filtering out the r-peaks
  36. % c) Find any segments of ECG where the ECG value does not change
  37. % d) Find any segments of ECG where samples were missed
  38. % e) Using c) and d), the times at which the ECG values did not change or were missed are concatenated such that the original ip can be inserted into the ip at these times (see 9a)
  39. % f) Insert NaNs into the ECG where there are gaps in time
  40. %
  41. % 2) Detrend the ECG and identify the ECG R-peaks to derive RR intervals
  42. %
  43. % 3) Clean RR intervals
  44. %
  45. % 4) The rr interval (rri) series is be stretched or compressed so that there are an equal number of steps between each rri
  46. %
  47. % 5) Start of ip signal preprocessing: Remove NaNs from the ip signal
  48. %
  49. % 6) Resample the ip signal in accordance to the rri
  50. %
  51. % 7a) Filter ip signals
  52. % b)filter out frequencies between 0.9-1.1 Hz to take out 1 Hz data, as this will represent the r-peak artefact
  53. % c)filter out frequencies between 1.9-2.1 Hz to take out 2 Hz data, as this will represent the r-peak artefact
  54. %
  55. % 8) Resample filtered_ip2 to be back in time
  56. %
  57. % 9a) Reinsert the original ip signal where the ECG was NaN
  58. % b) Filter out low frequencies below 0.5 Hz to tidy up signal, and remove outliers
  59. %
  60. % 10a) Make all data points in filtered_ip which correspond to missing ip data NaNs
  61. % b) NaNs in the original ip must be found, such that the filtered_ip at these times can be made NaNs
  62. %
  63. % 11a) This part of the script analyses the ip signal to identify breaths using an adaptive threshold
  64. % b) Identify breath peaks and compute the interbreath-interval (IBI)
  65. % c) The interbreath interval series must be altered to reflect NaNs, where the ip signal contains NaNs
  66. % d) This part of the code joins pauses which occur within 2 seconds of each other
  67. %
  68. %12) If requested, run the machine learning model using the remove_artifactual_longpauses function
  69. %
  70. % Tricia Adjei March 2021
  71. % If you use this code please cite it as
  72. % Adjei T, Purdy R, Jorge J, et al
  73. % New method to measure interbreath intervals in infants for the assessment of apnoea and respiration
  74. % BMJ Open Respiratory Research 2021;8:e001042. doi: 10.1136/bmjresp-2021-001042
  75. %
  76. % For further help please email Associate Prof. Caroline Hartley ([email hidden]).
  77. %If the inputs do not include an ecg signal, an adaptive threshold of 0.5
  78. %multiplied by the standard deviation of the cleaned ip signal is used. If
  79. %the inputs include an ecg signal, an adaptive threshold of 0.4
  80. %multiplied by the standard deviation of the cleaned ip signal is used.
  81. %First reshape all input vectors
  82. if isempty(ip)
  83. disp('No IP signal')
  84. ibi=[]; ibi_time=[]; filtered_ip=[]; filtered_ip_time=[]; filtered_ip_fs=[];
  85. return
  86. end
  87. if exist('ecg')
  88. if ~isempty(ecg)
  89. ecg=reshape(ecg,length(ecg),1);
  90. ecg_time=reshape(ecg_time,length(ecg_time),1);
  91. [ecg_time,iecg,~]=unique(ecg_time);
  92. ecg=ecg(iecg);
  93. end
  94. end
  95. if exist('ip')
  96. if ~isempty(ip)
  97. ip=reshape(ip,length(ip),1);
  98. ip_time=reshape(ip_time,length(ip_time),1);
  99. [ip_time,iip,~]=unique(ip_time);
  100. ip=ip(iip);
  101. end
  102. end
  103. if exist('hr')
  104. if ~isempty(hr)
  105. hr=reshape(hr,length(hr),1);
  106. hr_time=reshape(hr_time,length(hr_time),1);
  107. [hr_time,ihr,~]=unique(hr_time);
  108. hr=hr(ihr);
  109. end
  110. end
  111. if exist('sats')
  112. if ~isempty(sats)
  113. sats=reshape(sats,length(sats),1);
  114. sats_time=reshape(sats_time,length(sats_time),1);
  115. [sats_time,isats,~]=unique(sats_time);
  116. sats=sats(isats);
  117. end
  118. end
  119. if isempty(ecg)
  120. thresh=0.5;
  121. final_ip_fs=50;
  122. final_ip=spline(ip_time,ip,[ip_time(1):1/final_ip_fs:ip_time(end)]);
  123. final_ip_time=[ip_time(1):1/final_ip_fs:ip_time(end)];
  124. original_ip=ip;
  125. original_ip_time=ip_time;
  126. else
  127. %% 1a) Check if the ECG signal is inverted (ie upside-down): r-peaks should be the highest peaks, and append NaNs to the end of the ECG if it is shorter than the ip signal
  128. % Take a sample of the ecg signal to check if the signal could be inverted:
  129. if (ecg_time(end)-ecg_time(1))<=(15*60)
  130. test_window=length(ecg);
  131. else
  132. test_window_sec=300;
  133. test_window=test_window_sec*ecg_fs;
  134. end
  135. ecg_sample=ecg(1:test_window);
  136. ecg_sample(isnan(ecg_sample))=[];
  137. if length(ecg_sample)>(12*ecg_fs)
  138. %find the envelopes of the sample signal. The magnitude of the upper and
  139. %lower envelopes will indicate which way 'up' the signal should be.
  140. [upper,lower]=envelope(ecg_sample,(4*ecg_fs),'peak');
  141. upper_avg=abs(mode(upper));
  142. lower_avg=abs(mode(lower));
  143. if upper_avg>lower_avg
  144. ecg=ecg;
  145. elseif lower_avg>upper_avg
  146. ecg=-1*ecg;
  147. end
  148. else ecg=ecg;
  149. end
  150. %Append NaNs to the ECG if it is more than 1 minute shorter than the ip signal
  151. if (ip_time(end)-ecg_time(end))>=60
  152. shorter_by=ip_time(end)-ecg_time(end);
  153. shorter_by_time=[ecg_time(end)+(1/ecg_fs):(1/ecg_fs):ecg_time(end)+shorter_by]';
  154. shorter_by_length=length(shorter_by_time);
  155. shorter_by_nans=nan(shorter_by_length,1);
  156. ecg=[ecg;shorter_by_nans];
  157. ecg_time=[ecg_time;shorter_by_time];
  158. end
  159. %% b) NaNs in the ECG must be found, such that the ip at these times can be kept as the original ip signal, without filtering out the r-peaks
  160. ecgNaNs=find(isnan(ecg));
  161. if ~isempty(ecgNaNs)
  162. ecgNaNtime=ecg_time(find(isnan(ecg)));
  163. ecgNaN_loc_st=zeros(1,length(ecgNaNs));
  164. ecgNaN_loc_en=zeros(1,length(ecgNaNs));
  165. for i=1:length(ecgNaNs)
  166. if i==1
  167. ecgNaN_loc_st(i)=ecgNaNs(i);
  168. elseif ecgNaNs(i-1)~=ecgNaNs(i)-1
  169. ecgNaN_loc_st(i)=ecgNaNs(i);
  170. end
  171. if i==length(ecgNaNs)
  172. ecgNaN_loc_en(i)=ecgNaNs(i);
  173. elseif ecgNaNs(i+1)~=ecgNaNs(i)+1
  174. ecgNaN_loc_en(i)=ecgNaNs(i);
  175. end
  176. end
  177. clear i
  178. ecgNaN_loc_st(ecgNaN_loc_st==0)=[];
  179. ecgNaN_loc_en(ecgNaN_loc_en==0)=[];
  180. ecgNaN_time_st=ecg_time(ecgNaN_loc_st);
  181. ecgNaN_time_en=ecg_time(ecgNaN_loc_en);
  182. end
  183. %% c) Find any segments of ECG where the ECG value does not change
  184. nonconstantECG_loc=find(diff(ecg)~=0);
  185. if ~isempty(nonconstantECG_loc)
  186. nonconstantECG_loc=nonconstantECG_loc+1;
  187. nonconstantECG_loc(find(diff(nonconstantECG_loc)~=1))=[];
  188. nonconstantECG_loc(end)=[];
  189. if nonconstantECG_loc(1)==2
  190. nonconstantECG_loc=[1;nonconstantECG_loc];
  191. end
  192. constantECG_loc=1:length(ecg);
  193. constantECG_loc(nonconstantECG_loc)=[];
  194. ecg_repeats=ecg(constantECG_loc);
  195. ecg_repeats_time=ecg_time(constantECG_loc);
  196. constantECG_st=nan(length(ecg_repeats),1);
  197. constantECG_en=nan(length(ecg_repeats),1);
  198. for i=1:length(ecg_repeats)
  199. if i==1
  200. constantECG_st(i)=ecg_repeats_time(i);
  201. elseif i~=1 & ecg_repeats(i-1)~=ecg_repeats(i)
  202. constantECG_st(i)=ecg_repeats_time(i);
  203. end
  204. if i==length(constantECG_loc)
  205. constantECG_en(i)=ecg_repeats_time(i);
  206. elseif i~=length(constantECG_loc) & ecg_repeats(i+1)~=ecg_repeats(i)
  207. constantECG_en(i)=ecg_repeats_time(i);
  208. end
  209. end
  210. clear i
  211. constantECG_st(isnan(constantECG_st))=[];
  212. constantECG_en(isnan(constantECG_en))=[];
  213. end
  214. %% d) Find any segments of ECG where samples were missed
  215. miss_data_loc=find(diff(ecg_time)>=(2/ecg_fs));
  216. if ~isempty(miss_data_loc)
  217. new_miss_data_loc=miss_data_loc;
  218. miss_start_time=ecg_time(miss_data_loc); %Find start time of missing segments
  219. miss_end_time=ecg_time(miss_data_loc+1);
  220. miss_start_time=miss_start_time;
  221. miss_end_time=miss_end_time;
  222. end
  223. %% e) constantECG_st and ecgNaN_time_st and miss_start_time need to be concatenated so that they
  224. % can be used to reinsert the original ip signal at step 9a.
  225. if exist('ecgNaN_time_st')==1 & exist('constantECG_st')==1 & exist('miss_start_time')==1
  226. [insert_time_st,original_order,~]=unique([ecgNaN_time_st;constantECG_st;miss_start_time]);
  227. insert_time_en=[ecgNaN_time_en;constantECG_en;miss_end_time];
  228. insert_time_en=insert_time_en(original_order);
  229. elseif exist('ecgNaN_time_st')==1 & exist('constantECG_st')==1 & exist('miss_start_time')==0
  230. [insert_time_st,original_order,~]=unique([ecgNaN_time_st;constantECG_st]);
  231. insert_time_en=[ecgNaN_time_en;constantECG_en];
  232. insert_time_en=insert_time_en(original_order);
  233. elseif exist('ecgNaN_time_st')==1 & exist('constantECG_st')==0 & exist('miss_start_time')==0
  234. insert_time_st=ecgNaN_time_st;
  235. insert_time_en=ecgNaN_time_en;
  236. elseif exist('ecgNaN_time_st')==1 & exist('constantECG_st')==0 & exist('miss_start_time')==1
  237. [insert_time_st,original_order,~]=unique([ecgNaN_time_st;miss_start_time]);
  238. insert_time_en=[ecgNaN_time_en;miss_end_time];
  239. insert_time_en=insert_time_en(original_order);
  240. elseif exist('ecgNaN_time_st')==0 & exist('constantECG_st')==0 & exist('miss_start_time')==1
  241. insert_time_st=miss_start_time;
  242. insert_time_en=miss_end_time;
  243. elseif exist('ecgNaN_time_st')==0 & exist('constantECG_st')==1 & exist('miss_start_time')==1
  244. [insert_time_st,original_order,~]=unique([constantECG_st;miss_start_time]);
  245. insert_time_en=[constantECG_en;miss_end_time];
  246. insert_time_en=insert_time_en(original_order);
  247. elseif exist('ecgNaN_time_st')==0 & exist('constantECG_st')==1 & exist('miss_start_time')==0
  248. insert_time_st=constantECG_st;
  249. insert_time_en=constantECG_en;
  250. end
  251. %% f) Insert NaNs into the ECG where there are gaps in time
  252. %define gaps in the ECG as 2 missing data values, and find the gaps
  253. if ~isempty(miss_data_loc)
  254. if length(miss_data_loc)==1
  255. miss_start_time=ecg_time(miss_data_loc); %Find start time of missing segments
  256. miss_end_time=ecg_time(miss_data_loc+1);
  257. else
  258. % Only keep the last index of a missing segment
  259. for md=1:length(miss_data_loc)-1
  260. if md~=1 && miss_data_loc(md)+1==(miss_data_loc(md+1))
  261. new_miss_data_loc(md)=NaN;
  262. else
  263. new_miss_data_loc(md)=miss_data_loc(md);
  264. end
  265. end
  266. new_miss_data_loc(find(isnan(new_miss_data_loc)))=[];
  267. miss_data_loc=new_miss_data_loc;
  268. miss_start_time=ecg_time(miss_data_loc);
  269. miss_end_time=ecg_time(miss_data_loc+1);
  270. end
  271. insert_times1=cell(1,length(miss_start_time));
  272. %Find the missing times which correspond to the missing data
  273. for m=1:length(miss_start_time)
  274. insert_time=[(miss_start_time(m)+(1/ecg_fs)):(1/ecg_fs):(miss_end_time(m))];
  275. if insert_time(end)==miss_end_time(m)
  276. insert_times1{m}=insert_time(1:(length(insert_time)-1));
  277. else
  278. insert_times1{m}=insert_time;
  279. end
  280. end
  281. %Insert NaNs into ecg and insert the missing times into ecg_time
  282. insert_times=cat(2,insert_times1{:});
  283. new_ecg_time=[ecg_time;insert_times'];
  284. [sorted_ecg_time,order]=sort(new_ecg_time);
  285. new_ecg=[ecg;NaN(length(insert_times),1)];
  286. ecg_time=sorted_ecg_time;
  287. ecg=new_ecg(order);
  288. end
  289. clear miss_data_loc new_miss_data_loc miss_start_time miss_end_time
  290. %% 2) Detrend the ECG and identify the ECG R-peaks to derive RR intervals
  291. %The ecg signal should be a row vector.
  292. dim=size(ecg);
  293. [M,I]=max(dim);
  294. if I==1
  295. ecg=reshape(ecg,1,[]);
  296. ecg_time=reshape(ecg_time,1,[]);
  297. end
  298. window_sec=5; %detrend the ecg in a window, 5 seconds in length.
  299. window=window_sec*ecg_fs;
  300. lower=zeros(1,floor((length(ecg))/window)-1);
  301. upper=zeros(1,floor((length(ecg))/window)-1);
  302. nan_start=zeros(1,floor((length(ecg))/window)-1);
  303. nan_end=zeros(1,floor((length(ecg))/window)-1);
  304. detrend_ecg1=cell(1,floor((length(ecg))/window)-1);
  305. detrend_ecg_time1=cell(1,floor((length(ecg))/window)-1);
  306. time_of_r_peaks1=cell(1,floor((length(ecg))/window)-1);
  307. time_of_r_peaks2=cell(1,floor((length(ecg))/window)-1);
  308. r_peaks1=cell(1,floor((length(ecg))/window)-1);
  309. r_peak_loc1=cell(1,floor((length(ecg))/window)-1);
  310. time_of_volt_peaks1=cell(1,floor((length(ecg))/window)-1);
  311. for j=1:floor((length(ecg))/window)-1
  312. lower(j)=floor(1+((j-1)*window)); %lower window limit
  313. upper(j)=floor(j*window); %upper window limit
  314. windowed_signal1=ecg(lower(j):upper(j));
  315. %tell matlab to move to the next loop if there are NaNs in the
  316. %windowed segment, otherwise, carry on. The NaNs will be re-inserted
  317. %into the rr series later.
  318. if sum(isnan(windowed_signal1))~=0
  319. nan_start(j)=ecg_time(lower(j));
  320. nan_end(j)=ecg_time(upper(j));
  321. detrend_ecg1{j}=windowed_signal1;
  322. detrend_ecg_time1{j}=ecg_time(lower(j):upper(j));
  323. time_of_r_peaks1{j}=NaN;
  324. time_of_r_peaks2{j}=ecg_time(lower(j));
  325. r_peak_loc1{j}=NaN;
  326. r_peaks1{j}=NaN;
  327. continue
  328. end
  329. windowed_signal=[0 0 0 windowed_signal1 0 0 0]; %add zeros before and after the windowed signal segment to help the detrending process
  330. %find the polynomial coefficients which describe the wander in the
  331. %windowed signal.
  332. [p,s,mu]=polyfit((1:numel(windowed_signal)),windowed_signal,20);
  333. trend=polyval(p,(1:numel(windowed_signal)),[],mu); %compute the polynomial which describes the wander
  334. %subtract the wander polynomial from the windowed signal
  335. detrend_ecg_windowed=windowed_signal-trend;
  336. %fit an envelope to the detrended signal segment and find the
  337. %indices where the envelope and signal intersect.
  338. [upper_env,lower_env]=envelope(detrend_ecg_windowed,round(ecg_fs/(250/65)),'peak');
  339. detrend_ecg_windowed=detrend_ecg_windowed(4:(length(detrend_ecg_windowed)-3));
  340. detrend_ecg_time_windowed=ecg_time(lower(j):upper(j));
  341. [touch,touchloc]=intersect(detrend_ecg_windowed,upper_env,'stable');
  342. touchloc=touchloc';
  343. %delete touches which are definitely not r_peaks
  344. touchloc(touch<(0.9*std(touch)))=[];
  345. touch(touch<(0.9*std(touch)))=[];
  346. %move to the next segment if there are too few touches, or if the
  347. %touches are too noisy.
  348. if length(touch)<3 | std(touch)>0.7
  349. detrend_ecg1{j}=windowed_signal1;
  350. detrend_ecg_time1{j}=ecg_time(lower(j):upper(j));
  351. time_of_r_peaks1{j}=NaN;
  352. time_of_r_peaks2{j}=ecg_time(lower(j));
  353. r_peak_loc1{j}=NaN;
  354. r_peaks1{j}=NaN;
  355. continue
  356. end
  357. %if the last touched peak is short, delete that touch
  358. if touch(end)<0.5*touch(length(touch)-1)
  359. touch(end)=[];
  360. touchloc(end)=[];
  361. end
  362. touch_loc=touchloc;
  363. %search to see if there is a potential r-peak which follows the last touch
  364. remainder=detrend_ecg_windowed(max(touch_loc)+3:end);
  365. remainder_pk=max(remainder);
  366. if remainder_pk>0.7*detrend_ecg_windowed(max(touch_loc))
  367. touch_loc=[touch_loc find(detrend_ecg_windowed==remainder_pk)];
  368. end
  369. %search the gaps between the touches, and define good touches as
  370. %those which are of a consistent size.
  371. gaps=diff(touch_loc);
  372. gap_length=1:length(gaps);
  373. good_touches=[touch_loc(1) touch_loc(gap_length(gaps>2*std(gaps))+1)];
  374. check_gap=gap_length;
  375. touch_locs=good_touches;
  376. %if a gap is large, search for missing peaks in the gap
  377. for bb=1:length(check_gap)-1
  378. num_miss_val=round(gaps(check_gap(bb)+1)/gaps(check_gap(bb)))-1; %This denotes the number of missing peaks
  379. if num_miss_val+1<2
  380. continue
  381. end
  382. approx_ind=round((touch_loc(check_gap(bb)+2)-touch_loc(check_gap(bb)+1))/(num_miss_val+1)); %approx_ind approximates the jump to the next r peak
  383. %for the number of num_miss_vals, the location and
  384. %value of r-peaks missed by the envelope are found.
  385. for mi=1:length(num_miss_val)
  386. miss_ind(mi)=[touch_loc(check_gap(bb)+1)+(mi*approx_ind)];
  387. approx_range=[miss_ind(mi)-5:miss_ind(mi)+5]; %this is the approximate range of indices in which to search for a missed r-peak
  388. miss_dat=detrend_ecg_windowed(approx_range);
  389. [highest,highest_loc]=sort(miss_dat,'descend');
  390. miss_val(mi)=highest(mi);
  391. new_touch_loc1(mi)=(approx_range(mi)-1)+highest_loc(mi);
  392. end
  393. touch_locs=[touch_locs new_touch_loc1];
  394. end
  395. r_peaks_locs_windowed=sort(touch_locs);
  396. additional=lower(j)-1; %this is the signal index immediately before the start of the data window.
  397. %If the number of data points between additional and the first
  398. %value of r_peaks_locs_windowed is too small to be an rr interval, delete the first value of r_peaks_locs_windowed.
  399. if j~=1 & ~isempty(r_peak_loc1{j-1}) & ((r_peaks_locs_windowed(1)+additional)-(r_peak_loc1{j-1}(end)))<(mean(diff(r_peak_loc1{j-1}))*0.5)
  400. r_peaks_locs_windowed(1)=[];
  401. end
  402. %% Identify r-peaks
  403. detrend_ecg_time1{j}=detrend_ecg_time_windowed;
  404. detrend_ecg1{j}=detrend_ecg_windowed;
  405. r_peak_loc1{j}=r_peaks_locs_windowed+additional; %location of r-peaks
  406. r_peaks1{j}=detrend_ecg_windowed(r_peaks_locs_windowed); %r-peaks
  407. time_of_r_peaks1{j}=detrend_ecg_time_windowed(r_peaks_locs_windowed); %Time of r-peaks
  408. time_of_r_peaks2{j}=detrend_ecg_time_windowed(r_peaks_locs_windowed); %Time of r-peaks
  409. %If the std of the r-peaks is too high, the window is too noisy
  410. if std(r_peaks1{j})>=1
  411. time_of_r_peaks1{j}=NaN(1,length(time_of_r_peaks2{j}));
  412. r_peak_loc1{j}=NaN(1,length(r_peak_loc1{j}));
  413. r_peaks1{j}=NaN(1,length(r_peaks1{j}));
  414. continue
  415. end
  416. r_peak_loc1{j}=r_peaks_locs_windowed+additional; %location of r-peaks
  417. r_peaks1{j}=detrend_ecg_windowed(r_peaks_locs_windowed); %r-peaks
  418. time_of_volt_peaks1{j}=detrend_ecg_time_windowed(r_peaks_locs_windowed); %Time of all peaks
  419. end
  420. clear windowed_signal
  421. detrended_ecg=cat(2,detrend_ecg1{:}); %concatenate the windowed segments
  422. detrended_ecg_time=cat(2,detrend_ecg_time1{:});
  423. detrended_ecg_fs=ecg_fs;
  424. r_peak_loc=cat(2,r_peak_loc1{:});
  425. r_peaks=cat(2,r_peaks1{:});
  426. time_of_r_peaks_nan=cat(2,time_of_r_peaks1{:});
  427. time_of_r_peaks=cat(2,time_of_r_peaks2{:});
  428. rri=abs(diff(time_of_r_peaks_nan)); %time intervals between r-peaks
  429. rri_time=(time_of_r_peaks(2:end)); %timestamps of rr intervals
  430. %% 3) Clean rr intervals
  431. rri(rri<=0.25| rri>=1.5)=NaN;
  432. %% 4) The rr interval (rri) series is to be stretched or compressed so that there are an equal number of steps between each rri
  433. stretched_rri_time=cell(1,length(rri_time));
  434. division=50; %this defines the number of steps between each rri
  435. division1=division-1;
  436. for i=1:(length(rri_time)-1)
  437. increment_steps=(rri_time(i+1)-rri_time(i))/division1;
  438. for ii=1:(division1)
  439. stretched_rri_time{i}(ii)=rri_time(i)+((ii-1)*increment_steps);
  440. end
  441. end
  442. clear i ii
  443. stretched_rri_time=cat(2,stretched_rri_time{:});
  444. %% 5) Start of ip signal preprocessing: Remove NaNs from the ip signal
  445. % First, get rid of NaNs in the ip signal and remove any square edges in the ip signal
  446. original_ip=ip;
  447. original_ip_time=ip_time;
  448. ip_nan_indices=find(isnan(ip));
  449. ip(ip_nan_indices)=[];
  450. ip_time(ip_nan_indices)=[];
  451. ip_copy=ip;
  452. ip_copy_time=ip_time;
  453. gapps=find(diff(ip)==0);
  454. gap_st_loc=zeros(1,length(gapps));
  455. gap_en_loc=zeros(1,length(gapps));
  456. for g=1:length(gapps)
  457. if g==1
  458. gap_st_loc(g)=gapps(g);
  459. elseif gapps(g-1)~=gapps(g)-1
  460. gap_st_loc(g)=gapps(g);
  461. end
  462. if g==length(gapps)
  463. gap_en_loc(g)=gapps(g);
  464. elseif gapps(g+1)~=gapps(g)+1
  465. gap_en_loc(g)=gapps(g);
  466. end
  467. end
  468. gap_st_loc(gap_st_loc==0)=[];
  469. gap_en_loc(gap_en_loc==0)=[];
  470. gap_en_loc=gap_en_loc+1;
  471. for h=1:length(gap_st_loc)
  472. ip_copy([gap_st_loc(h):gap_en_loc(h)])=NaN;
  473. ip_copy_time([gap_st_loc(h):gap_en_loc(h)])=NaN;
  474. end
  475. new_ip=ip_copy;
  476. new_ip_time=ip_copy_time;
  477. new_ip(isnan(ip_copy))=[];
  478. new_ip_time(isnan(ip_copy))=[];
  479. %% 6) Resample the ip signal in accordance to the rri
  480. % (Lee, H., Rusin, C.G., Lake, D.E., Clark, M.T., Guin, L., Smoot, T.J., Paget-Brown, A.O., Vergales, B.D., Kattwinkel, J., Moorman, J.R. and Delos, J.B., 2011.
  481. % A new algorithm for detecting central apnea in neonates. Physiological measurement, 33(1), p.1).
  482. rri_sampled_ip=spline(new_ip_time,new_ip,stretched_rri_time); %Resample rri according to stretched_rri_time
  483. %% 7a) Filter ip signals
  484. % filter out frequencies less than 0.1 Hz to take out noise
  485. fltr=designfilt('highpassfir','FilterOrder',floor(length(rri_sampled_ip)/1000),'CutoffFrequency',0.1,'DesignMethod','window','Window','Hamming','ScalePassband',false,'SampleRate',division);
  486. filt_ip=filtfilt(fltr,rri_sampled_ip);
  487. delay=mean(grpdelay(fltr,length(filt_ip),division));
  488. filt_ip(1:delay)=[];
  489. filt_ip_time=stretched_rri_time;
  490. filt_ip_time(1:delay)=[];
  491. %% b)filter out frequencies between 0.9-1.1 Hz to take out 1 Hz data, as this will represent the r-peak artefact
  492. nyquist=division/2;
  493. wo=1/nyquist;
  494. bw=wo/4;
  495. [SOS1,G1]=iirnotch(wo,bw,10);
  496. filtered_ip1=filtfilt(SOS1,G1,filt_ip);
  497. delay=mean(grpdelay(SOS1,length(filt_ip),division));
  498. filtered_ip1(1:delay)=[];
  499. filtered_ip_time1=filt_ip_time;
  500. filtered_ip_time1(1:delay)=[];
  501. %% c)filter out frequencies between 1.9-2.1 Hz to take out 2 Hz data, as this will represent the r-peak artefact
  502. wo=2/nyquist;
  503. bw=wo/4;
  504. [SOS2,G2]=iirnotch(wo,bw,10);
  505. filtered_ip2=filtfilt(SOS2,G2,filtered_ip1);
  506. delay=mean(grpdelay(SOS2,length(filtered_ip1),division));
  507. filtered_ip2(1:delay)=[];
  508. filtered_ip_time2=filtered_ip_time1;
  509. filtered_ip_time2(1:delay)=[];
  510. %% 8) Resample filtered_ip2 to be back in time
  511. final_ip_fs=50; %Resample the ip signal at a frequency of 50 Hz
  512. X=filtered_ip_time2;
  513. Y=filtered_ip2;
  514. final_ip_time=filtered_ip_time2(1):1/final_ip_fs:filtered_ip_time2(end);
  515. final_ip=spline(X,Y,final_ip_time);
  516. %% 9a) Reinsert the original ip signal where the ECG was NaN or did not change, or missed samples
  517. if exist('insert_time_st')==1
  518. insertip=spline(original_ip_time,original_ip,final_ip_time);
  519. diff_insert=insert_time_en-insert_time_st;
  520. f=find(diff_insert>=60); %The original ip signal should only be inserted if the segment to be inserted is longer than 60s
  521. for i=1:length(f)
  522. insertloc1=find(final_ip_time<=insert_time_st(f(i)),1,'last');
  523. insertloc2=find(final_ip_time>=insert_time_en(f(i)),1,'first');
  524. if ~isempty(insertloc1) && ~isempty(insertloc2)
  525. final_ip(insertloc1:insertloc2)=insertip(insertloc1:insertloc2);
  526. end
  527. end
  528. end
  529. end
  530. clear i
  531. %% b) Filter out low frequencies below 0.5 Hz to tidy up signal, and remove outliers
  532. high_pass_filt=designfilt('highpassfir', 'FilterOrder', 300, 'CutoffFrequency', 0.5, 'SampleRate', final_ip_fs);
  533. filtered_final_ip=filter(high_pass_filt,final_ip);
  534. delay=mean(grpdelay(high_pass_filt));
  535. delay_time=delay*(1/final_ip_fs);
  536. filtered_final_ip(1:delay)=[];
  537. filtered_final_ip_time=final_ip_time-delay_time;
  538. filtered_final_ip_time(1:delay)=[];
  539. filtered_final_ip_fs=final_ip_fs;
  540. %Outliers will be identified and removed from every 12 hours of the ip signal.
  541. window_pts=12*3600*filtered_final_ip_fs;
  542. windowed_ip1=cell(1,ceil(length(filtered_final_ip)/window_pts));
  543. windowed_ip_time1=cell(1,ceil(length(filtered_final_ip_time)/window_pts));
  544. for j=1:ceil(length(filtered_final_ip)/window_pts)
  545. if j<ceil(length(filtered_final_ip)/window_pts)
  546. lower=1+((j-1)*window_pts); %lower window limit
  547. upper=j*window_pts; %upper window limit
  548. elseif (length(filtered_final_ip)-((j-1)*window_pts))>=(60*filtered_final_ip_fs)
  549. lower=1+((j-1)*window_pts);
  550. upper=length(filtered_final_ip);
  551. else
  552. break
  553. end
  554. windowed_ip=filtered_final_ip(lower:upper);
  555. windowed_ip_time=filtered_final_ip_time(lower:upper);
  556. temp_windowed_ip1=windowed_ip(windowed_ip>0);
  557. high_outlier=6*prctile(temp_windowed_ip1,90);
  558. outliers1=find(windowed_ip>high_outlier);
  559. windowed_ip(outliers1)=high_outlier;
  560. temp_windowed_ip2=windowed_ip(windowed_ip<0);
  561. low_outlier=6*prctile(temp_windowed_ip2,10);
  562. outliers2=find(windowed_ip<low_outlier);
  563. windowed_ip(outliers2)=low_outlier;
  564. windowed_ip1{j}=windowed_ip;
  565. windowed_ip_time1{j}=windowed_ip_time;
  566. end
  567. filtered_ip=cat(2,windowed_ip1{:});
  568. filtered_ip_time=cat(2,windowed_ip_time1{:});
  569. filtered_ip_fs=filtered_final_ip_fs;
  570. %% 10a) Make all data points in filtered_ip which correspond to missing ip data NaNs
  571. %define gaps in the original ip signal as 2 missing data values,
  572. %and find the gaps
  573. miss_data_loc=find(abs(diff(original_ip_time))>=(2/ip_fs));
  574. if ~isempty(miss_data_loc)
  575. new_miss_data_loc=miss_data_loc;
  576. for md=1:length(miss_data_loc)-1
  577. if md~=1 & miss_data_loc(md)+1==(miss_data_loc(md+1))
  578. new_miss_data_loc(md)=NaN;
  579. end
  580. end
  581. new_miss_data_loc(find(isnan(new_miss_data_loc)))=[];
  582. miss_data_loc=new_miss_data_loc;
  583. miss_start_time=original_ip_time(miss_data_loc);
  584. miss_end_time=original_ip_time(miss_data_loc+1);
  585. nearest_start_loc=zeros(1,length(miss_start_time));
  586. nearest_end_loc=zeros(1,length(miss_start_time));
  587. check_start_loc=zeros(1,length(miss_start_time));
  588. check_end_loc=zeros(1,length(miss_start_time));
  589. missing_periods=cell(1,length(miss_start_time));
  590. check_start_periods=cell(1,length(miss_start_time));
  591. check_end_periods=cell(1,length(miss_start_time));
  592. %find nearest times in filtered_ip_time which correspond to the
  593. %missing times.
  594. for m=1:length(miss_start_time)
  595. [~,nearest_start_loc(m)]=min(abs(miss_start_time(m)-filtered_ip_time));
  596. [~,nearest_end_loc(m)]=min(abs(miss_end_time(m)-filtered_ip_time));
  597. if (nearest_start_loc(m)-(60*filtered_ip_fs))>=1
  598. check_start_loc(m)=nearest_start_loc(m)-(60*filtered_ip_fs);
  599. else check_start_loc(m)=1;
  600. end
  601. if (nearest_end_loc(m)+(60*filtered_ip_fs))<=length(filtered_ip)
  602. check_end_loc(m)=nearest_end_loc(m)+(60*filtered_ip_fs);
  603. else check_end_loc(m)=length(filtered_ip);
  604. end
  605. %Define indices of periods of missing data
  606. missing_periods{m}=[nearest_start_loc(m):nearest_end_loc(m)];
  607. check_start_periods{m}=[check_start_loc(m):nearest_start_loc(m)-1];
  608. check_end_periods{m}=[nearest_end_loc(m)+1:check_end_loc(m)];
  609. %remove outliers occurring immediatley before a segment of missing
  610. %data.
  611. to_check=filtered_ip(check_start_periods{m});
  612. take_out1=find(to_check>=high_outlier | to_check<=low_outlier,1,'first');
  613. take_out2=find(to_check(1:take_out1)<0,1,'last');
  614. take_out3=check_start_periods{m}(take_out2:end);
  615. filtered_ip(take_out3)=NaN;
  616. clear take_out1 take_out2 take_out3
  617. %remove outliers occurring immediatley after a segment of missing
  618. %data.
  619. to_check1=filtered_ip(check_end_periods{m});
  620. take_out1=find(to_check1>=high_outlier | to_check1<=low_outlier,1,'last');
  621. take_out2=find(to_check1(take_out1:end)<0,1,'first');
  622. if (take_out1+take_out2) <= length(to_check1)
  623. take_out3=check_end_periods{m}(1:(take_out1+take_out2));
  624. else
  625. take_out3=check_end_periods{m}(1:take_out1);
  626. end
  627. filtered_ip(take_out3)=NaN;
  628. %filtered_ip currently contains interpolated values where data
  629. %should be missing, so replace data during missing periods with NaNs
  630. filtered_ip(missing_periods{m})=NaN;
  631. end
  632. data_restart_loc=nearest_end_loc+1; %Time at which data restarts after a period of missing data
  633. end
  634. %% b) NaNs in the original ip must be found, such that the filtered_ip at these times can be made NaNs
  635. ipNaNs=find(isnan(original_ip));
  636. if ~isempty(ipNaNs)
  637. ipNaNtime=original_ip_time(find(isnan(ip)));
  638. ipNaN_loc_st=zeros(1,length(ipNaNs));
  639. ipNaN_loc_en=zeros(1,length(ipNaNs));
  640. for i=1:length(ipNaNs)
  641. if i==1
  642. ipNaN_loc_st(i)=ipNaNs(i);
  643. elseif ipNaNs(i-1)~=ipNaNs(i)-1
  644. ipNaN_loc_st(i)=ipNaNs(i);
  645. end
  646. if i==length(ipNaNs)
  647. ipNaN_loc_en(i)=ipNaNs(i);
  648. elseif ipNaNs(i+1)~=ipNaNs(i)+1
  649. ipNaN_loc_en(i)=ipNaNs(i);
  650. end
  651. end
  652. clear i
  653. ipNaN_loc_st(ipNaN_loc_st==0)=[];
  654. ipNaN_loc_en(ipNaN_loc_en==0)=[];
  655. ipNaN_time_st=original_ip_time(ipNaN_loc_st);
  656. ipNaN_time_en=original_ip_time(ipNaN_loc_en);
  657. ip_nan_times2=cell(1,length(ipNaN_time_st));
  658. for i=1:length(ipNaN_time_st)
  659. ip_nan_times2{i}=find(filtered_ip_time>=ipNaN_time_st(i) & filtered_ip_time<=ipNaN_time_en(i));
  660. end
  661. clear i
  662. nan_time_loc=cat(2,ip_nan_times2{:});
  663. filtered_ip(nan_time_loc)=NaN;
  664. end
  665. %% 11a) This part of the script analyses the ip signal
  666. clearvars -except ecgNaN_time_st ecgNaN_time_en thresh ip ip_time ip_fs original_ip original_ip_time filtered_ip_time filtered_ip filtered_ip_fs data_restart_times hr sats sats_time hr_time model_input
  667. signal=filtered_ip;
  668. time=filtered_ip_time;
  669. %a) Set signal to NaN if the hr signal has NaNs.
  670. checkHR=find(isnan(hr));
  671. %but not if there are large chunks of missing HR data
  672. diffcheckHR=diff(checkHR);
  673. fdhr=find(diffcheckHR>1);
  674. if isempty(fdhr)
  675. checkHR=[];
  676. else
  677. todelete=[];
  678. for ind=1:length(fdhr)
  679. if ind==1
  680. len=checkHR(fdhr(ind))-checkHR(1);
  681. if len>60
  682. todelete=[1:fdhr(ind)];
  683. end
  684. else
  685. len=checkHR(fdhr(ind))-checkHR(fdhr(ind-1)+1);
  686. if len>60
  687. todelete=[todelete,fdhr(ind-1)+1:fdhr(ind)];
  688. end
  689. end
  690. end
  691. checkHR(todelete)=[];
  692. end
  693. if ~isempty(checkHR)
  694. time_nanHR=hr_time(checkHR);
  695. for i=1:length(checkHR) %for each NaN change the ip signal to NaN for 5 seconds around it
  696. st_nan=time_nanHR(i)-2.5;
  697. en_nan=time_nanHR(i)+2.5;
  698. ind=find(time>=st_nan & time<=en_nan);
  699. signal(ind)=NaN;
  700. end
  701. end
  702. clear i;
  703. diff_ip=diff(original_ip);
  704. ind=find(diff_ip==0);
  705. diff_ind=diff(ind);
  706. i=1;
  707. while i<length(diff_ind)
  708. if diff_ind(i)==1
  709. f=find(diff_ind(i:end)>1,1,'first');
  710. if f>1*ip_fs %second is fs
  711. %Find other segments of data which reach the cap within 5
  712. %seconds of the first segment.
  713. jump=1;
  714. while jump==1
  715. g=find(diff_ind(i+f:end)>1,1,'first');
  716. if ~isempty(g)
  717. if g>1*ip_fs && diff_ind(i+f-1)<5*ip_fs
  718. f=f+g;
  719. else
  720. jump=2;
  721. end
  722. else
  723. jump=2;
  724. end
  725. end
  726. ind_nan=ind(i:i+f-1);
  727. st_time=original_ip_time(ind_nan(1))-2.5; %take 2.5 seconds either side
  728. en_time=original_ip_time(ind_nan(end))+2.5;
  729. t_ind=find(time>=st_time & time<=en_time);
  730. signal(t_ind)=NaN;
  731. end
  732. else
  733. f=1;
  734. end
  735. i=i+f;
  736. end
  737. clear i f;
  738. %Find short segments of signal left over and set them to NaN
  739. f=find(isnan(signal));
  740. d=diff(f);
  741. ff=find(d>1 & d<2*ip_fs);
  742. for i=1:length(ff)
  743. signal(f(ff(i)):f(ff(i)+1))=NaN;
  744. end
  745. clear i f d ff;
  746. f=find(isnan(signal));
  747. if ~isempty(f)
  748. d=diff(f);
  749. ff=find(d>1);
  750. data_restart_times=time(f(ff)+1);
  751. data_end=time(f(ff+1)-1);
  752. if f(1)==1
  753. data_end=[time(1),data_end];
  754. else
  755. data_end=[time(f(1)-1),data_end];
  756. end
  757. data_end(end)=[];
  758. duration_stop=data_restart_times-data_end;
  759. clear f d ff;
  760. end
  761. %% b) Identify breath peaks and compute the interbreath-interval (IBI)
  762. if (time(end)-time(1))<=15*60
  763. initial_thresh_window=30; % window length in which to compute ip threshold
  764. else
  765. initial_thresh_window=600;
  766. end
  767. window_pts=initial_thresh_window*filtered_ip_fs;
  768. sig_start=find(~isnan(signal),1,'first');
  769. temp_sig=signal(sig_start:end);
  770. temp_tim=time(sig_start:end);
  771. all_thresholds=cell(1,ceil(length(signal)));
  772. crossings_windowed=zeros(1,ceil(length(signal)));
  773. x=cell(1,ceil(length(signal)/window_pts));
  774. %Detect breaths and interbreath intervals in nonoverlapping windows
  775. window_pts_sum=window_pts;
  776. j=1;
  777. extra=0;
  778. n_breaths=15;
  779. while window_pts_sum<=length(temp_sig)
  780. lower=window_pts_sum-window_pts+1;
  781. upper=window_pts_sum;
  782. windowed_ip=temp_sig(lower:upper);
  783. windowed_ip_time=temp_tim(lower:upper);
  784. if exist('ecgNaN_time_st')==1
  785. for i=1:length(ecgNaN_time_st)
  786. if windowed_ip_time(1)>=ecgNaN_time_st(i) & windowed_ip_time(end)<=ecgNaN_time_en(i)
  787. thresh=0.5;
  788. break
  789. else
  790. thresh=0.4;
  791. end
  792. end
  793. else thresh=0.4;
  794. end
  795. %If the windowed signal only contains NaNs,
  796. %skip to the next window
  797. if sum(isnan(windowed_ip))==length(windowed_ip) || sum(diff(windowed_ip))==0
  798. threh=all_thresholds{1:j};
  799. if isempty(threh)
  800. extra=1;
  801. end
  802. window_pts_sum=window_pts_sum+window_pts;
  803. continue
  804. end
  805. if lower==1 | extra==1
  806. threshold=thresh*std(windowed_ip,'omitnan');
  807. rel_thres=windowed_ip-threshold;
  808. pos=find(rel_thres>=0);
  809. diffposs=diff(pos);
  810. f=find(diffposs~=1);
  811. % length(pos) denotes the last threshold crossing.
  812. % If the signal in the window ends at a point above
  813. % the threshold, f is left as f. But if it ends at
  814. % another point, f must also include length(pos).
  815. if ~ismember(window_pts,pos)
  816. f=[f length(pos)];
  817. end
  818. breath_time_windowed=windowed_ip_time(pos(f));
  819. %Delete if only 1 crossing within 5 seconds either side
  820. f=find(diff(breath_time_windowed)>5);
  821. if ~isempty(f)
  822. g=find(diff(f)==1);
  823. if ~isempty(g)
  824. breath_time_windowed(f(g)+1)=[];
  825. end
  826. end
  827. x{j}=find(abs(diff(breath_time_windowed))<0.3); %Ignore breaths which occur less than 0.3 seconds after the previous peak.
  828. breath_time_windowed(x{j}+1)=[];
  829. crossings_windowed(1:length(breath_time_windowed))=breath_time_windowed; %Store times at which the ip signal crossed the threshold
  830. all_thresholds{j}=repmat(threshold,1,length(breath_time_windowed)); %Store the threshold used in each window
  831. num_breath=find(crossings_windowed~=0);
  832. num_breath=length(num_breath); %Number of breaths identified
  833. min_num_breaths=num_breath;
  834. %If the number of identified breaths is less than n_breath (ie the required number of previous breaths),
  835. %a fixed threshold, calculated over the same length as initial_thresh_window, is used.
  836. if min_num_breaths<n_breaths
  837. clear all_thresholds
  838. crossings_windowed1=cell(1,ceil(length(temp_sig)/window_pts));
  839. all_thresholds=cell(1,ceil(length(temp_sig)/window_pts));
  840. %Detect breaths in nonoverlapping windows if the number of
  841. %breaths computed in the first window is less than that
  842. %required to update the threshold
  843. for j=1:ceil(length(temp_sig)/window_pts)
  844. if j<ceil(length(temp_sig)/window_pts)
  845. lower=1+((j-1)*window_pts); %lower window limit
  846. upper=j*window_pts; %upper window limit
  847. elseif (length(temp_sig)-((j-1)*window_pts))>=(5*filtered_ip_fs)
  848. lower=1+((j-1)*window_pts);
  849. upper=length(temp_sig);
  850. else
  851. break
  852. end
  853. windowed_ip=temp_sig(lower:upper);
  854. windowed_ip_time=temp_tim(lower:upper);
  855. if sum(isnan(windowed_ip))==length(windowed_ip) || sum(diff(windowed_ip))==0
  856. continue
  857. end
  858. threshold=thresh*std(windowed_ip,'omitnan');
  859. rel_thres=windowed_ip-threshold;
  860. pos=find(rel_thres>=0);
  861. diffposs=diff(pos);
  862. f=find(diffposs~=1);
  863. if ~ismember(window_pts,pos)
  864. f=[f length(pos)];
  865. end
  866. breath_time_windowed=windowed_ip_time(pos(f));
  867. %Delete if only 1 crossing within 5 seconds either side
  868. f=find(diff(breath_time_windowed)>5);
  869. if ~isempty(f)
  870. g=find(diff(f)==1);
  871. if ~isempty(g)
  872. breath_time_windowed(f(g)+1)=[];
  873. end
  874. end
  875. crossings_windowed1{j}=breath_time_windowed; %Store times at which the ip signal crossed the threshold
  876. all_thresholds{j}=repmat(threshold,1,length(crossings_windowed1{j})); %Store the threshold used in each window
  877. end
  878. crossings_windowed=cat(2,crossings_windowed1{:});
  879. x=find(abs(diff(crossings_windowed))<0.3); %Ignore breaths which occur less than 0.3 seconds after the previous peak.
  880. crossings_windowed(x+1)=[];
  881. break
  882. end
  883. %This part of the loop finds the first breath, using an adaptive
  884. %threshold.
  885. extra=0;
  886. nbreaths=n_breaths-1; %n_breaths is the number of previous breaths over which to compute the adaptive threshold
  887. j=j+1;
  888. lastbreath=crossings_windowed(num_breath-nbreaths);
  889. lastbreath_loc1=find(temp_tim==lastbreath);
  890. lastbreath_loc2=find(temp_tim==crossings_windowed(num_breath));
  891. temp_thresh=thresh*std(temp_sig(lastbreath_loc1:lastbreath_loc2),'omitnan');
  892. sig_len=(lastbreath_loc2+1):length(temp_sig);
  893. pot_cross=sig_len(temp_sig((lastbreath_loc2+1):end)>=temp_thresh); %pot_cross contains the indices of all signal values which are above the threshold
  894. gd_point=find(diff(pot_cross)~=1); %find pot_cross values which are not preceded by another pot_cross value
  895. gd_point(gd_point==(pot_cross(gd_point)-lastbreath_loc2))=[];
  896. pot_cross=pot_cross(gd_point);
  897. pot_cross(pot_cross==length(temp_sig))=[];
  898. pot_cross=pot_cross((temp_tim(pot_cross)-temp_tim(lastbreath_loc2))>0.3);
  899. cross_loc=find(temp_sig(pot_cross+1)<temp_thresh,1,'first');
  900. if isempty(cross_loc)
  901. break
  902. end
  903. next_breath_loc=pot_cross(cross_loc);
  904. space=find(crossings_windowed==0,1,'first');
  905. crossings_windowed(space)=temp_tim(next_breath_loc);
  906. all_thresholds{j}=temp_thresh;
  907. previous_breath_loc=lastbreath_loc2;
  908. window_pts_sum=previous_breath_loc; %window_pts_sum must be defined so that the next window can be found
  909. window_pts=next_breath_loc-previous_breath_loc;
  910. window_pts_sum=window_pts_sum+window_pts;
  911. if window_pts_sum>length(temp_sig)
  912. break
  913. end
  914. else
  915. j=j+1;
  916. num_breath=find(crossings_windowed~=0);
  917. num_breath=length(num_breath);
  918. lastbreath=crossings_windowed(num_breath-nbreaths);
  919. lastbreath_loc1=find(temp_tim==lastbreath);
  920. lastbreath_loc2=find(temp_tim==crossings_windowed(num_breath));
  921. temp_thresh=thresh*std(temp_sig(lastbreath_loc1:lastbreath_loc2),'omitnan');
  922. sig_len=(lastbreath_loc2+1):length(temp_sig);
  923. pot_cross=sig_len(temp_sig((lastbreath_loc2+1):end)>=temp_thresh);
  924. gd_point=find(diff(pot_cross)~=1);
  925. gd_point(gd_point==(pot_cross(gd_point)-lastbreath_loc2))=[];
  926. pot_cross=pot_cross(gd_point);
  927. pot_cross(pot_cross==length(temp_sig))=[];
  928. pot_cross=pot_cross((temp_tim(pot_cross)-temp_tim(lastbreath_loc2))>0.3);
  929. cross_loc=find(temp_sig(pot_cross+1)<temp_thresh,1,'first');
  930. if isempty(cross_loc)
  931. break
  932. end
  933. next_breath_loc=pot_cross(cross_loc);
  934. space=find(crossings_windowed==0,1,'first');
  935. crossings_windowed(space)=temp_tim(next_breath_loc);
  936. all_thresholds{j}=temp_thresh;
  937. previous_breath_loc=lastbreath_loc2;
  938. window_pts=next_breath_loc-previous_breath_loc;
  939. window_pts_sum=window_pts_sum+window_pts;
  940. if window_pts_sum>length(temp_sig)
  941. break
  942. end
  943. end
  944. end
  945. all_thresholds=cat(2,all_thresholds{:});
  946. good=find(crossings_windowed~=0);
  947. crossings=crossings_windowed(good);
  948. threshold_crossings=crossings;
  949. %Compute the interbreath intervals
  950. ibi=abs(diff(crossings));
  951. ibi_time=crossings(2:end);
  952. %% c) The interbreath interval series must be altered to reflect NaNs, where the ip signal contains NaNs to identify breaths using an adaptive threshold
  953. if sum(isnan(signal))~=0
  954. time_aft_nan=zeros(1,length(signal));
  955. time_bef_nan=zeros(1,length(signal));
  956. getrid_ibi_loc=zeros(1,length(signal));
  957. %The times at which sequences of NaNs start or end are found as time_bef_nan and time_aft_nan
  958. for i=1:length(time)-1
  959. if i==1 && isnan(signal(i)) && ~isnan(signal(i+1))
  960. time_bef_nan(i)=time(i);
  961. time_aft_nan(i)=time(i);
  962. elseif i==1 && isnan(signal(i)) && isnan(signal(i+1))
  963. time_bef_nan(i)=time(i);
  964. elseif i~=1 && ~isnan(signal(i-1)) && isnan(signal(i))
  965. time_bef_nan(i)=time(i);
  966. elseif i~=1 && isnan(signal(i)) && ~isnan(signal(i+1))
  967. time_aft_nan(i)=time(i);
  968. if find(crossings>time_aft_nan(i),1,'first')~=1 & crossings(end)>time_aft_nan(i)
  969. getrid_ibi_loc(i)=find(ibi_time==(crossings(find(crossings>time_aft_nan(i),1,'first'))));
  970. end
  971. elseif i==length(time)-1 && isnan(signal(i+1))
  972. time_aft_nan(i)=time(i);
  973. end
  974. end
  975. clear i;
  976. getrid_ibi_loc(getrid_ibi_loc==0)=[];
  977. if ~isempty(getrid_ibi_loc)
  978. getrid_ibi_loc=unique(getrid_ibi_loc);
  979. ibi(getrid_ibi_loc)=[];
  980. ibi_time(getrid_ibi_loc)=[];
  981. end
  982. time_bef_nan(time_bef_nan==0)=[];
  983. time_aft_nan(time_aft_nan==0)=[];
  984. ibi_time=[ibi_time time_bef_nan];
  985. [nanibi_time,ibi_ord,new_ord]=unique(ibi_time);
  986. ibi=[ibi NaN(1,length(time_bef_nan))];
  987. nanibi=ibi(ibi_ord);
  988. %To account for breath pauses which are followed by
  989. %segments of NaNs, 'additional' ibi values must be found. In other words,
  990. %pauses followed by NaNs and not by threshold crossings must be found.
  991. additional_ibi_time=zeros(1,length(time_bef_nan));
  992. additional_ibi=zeros(1,length(time_bef_nan));
  993. for i=1:length(time_bef_nan)
  994. if ~isempty(find(crossings<time_bef_nan(i),1,'last'))
  995. last_cross_loc=find(time==crossings(find(crossings<time_bef_nan(i),1,'last')));
  996. if sum(isnan(signal(last_cross_loc:find(time<time_bef_nan(i),1,'last'))))==0
  997. if time(find(time<time_bef_nan(i),1,'last'))-crossings(find(crossings<time_bef_nan(i),1,'last')) >=3
  998. additional_ibi_time(i)=time(find(time<time_bef_nan(i),1,'last'));
  999. additional_ibi(i)=time(find(time<time_bef_nan(i),1,'last'))-crossings(find(crossings<time_bef_nan(i),1,'last'));
  1000. end
  1001. end
  1002. end
  1003. end
  1004. additional_ibi_time(additional_ibi_time==0)=[];
  1005. additional_ibi(additional_ibi==0)=[];
  1006. ibi_time1=[nanibi_time additional_ibi_time];
  1007. ibi1=[nanibi additional_ibi];
  1008. clear i
  1009. %To account for breath pauses which are preceded and followed by
  1010. %segments of NaNs, 'additional' ibi values must be found; pauses preceded
  1011. %and followed by NaNs and not by threshold crossings must be found.
  1012. additional_ibi_time=zeros(1,length(time_aft_nan));
  1013. additional_ibi=zeros(1,length(time_aft_nan));
  1014. if length(time_bef_nan)>=2
  1015. for i=2:length(time_bef_nan)
  1016. if isempty(find(crossings>time_aft_nan(i-1) & crossings<time_bef_nan(i)))
  1017. if time(find(time<time_bef_nan(i),1,'last'))-time(find(time>time_aft_nan(i-1),1,'first')) >=3
  1018. additional_ibi_time(i)=time(find(time<time_bef_nan(i),1,'last'));
  1019. additional_ibi(i)=time(find(time<time_bef_nan(i),1,'last'))-time(find(time>time_aft_nan(i-1),1,'first'));
  1020. end
  1021. end
  1022. end
  1023. additional_ibi_time(additional_ibi_time==0)=[];
  1024. additional_ibi(additional_ibi==0)=[];
  1025. ibi_time2=[ibi_time1 additional_ibi_time];
  1026. ibi2=[ibi1 additional_ibi];
  1027. clear i
  1028. else
  1029. additional_ibi_time(additional_ibi_time==0)=[];
  1030. additional_ibi(additional_ibi==0)=[];
  1031. ibi_time2=[ibi_time1 additional_ibi_time];
  1032. ibi2=[ibi1 additional_ibi];
  1033. end
  1034. %Pauses preceded by NaNs must be accounted for.
  1035. additional_ibi_time=zeros(1,length(time_aft_nan));
  1036. additional_ibi=zeros(1,length(time_aft_nan));
  1037. for i=1:length(time_aft_nan)
  1038. if crossings(end)>=time_aft_nan(i)
  1039. nxt_cross_loc=find(time==crossings(find(crossings>time_aft_nan(i),1,'first')));
  1040. if sum(isnan(signal(find(time>time_aft_nan(i),1,'first'):nxt_cross_loc)))==0
  1041. if crossings(find(crossings>time_aft_nan(i),1,'first'))-time(find(time>time_aft_nan(i),1,'first')) >=3
  1042. additional_ibi_time(i)=crossings(find(crossings>time_aft_nan(i),1,'first'));
  1043. additional_ibi(i)=crossings(find(crossings>time_aft_nan(i),1,'first'))-time(find(time>time_aft_nan(i),1,'first'));
  1044. end
  1045. end
  1046. end
  1047. end
  1048. additional_ibi_time(additional_ibi_time==0)=[];
  1049. additional_ibi(additional_ibi==0)=[];
  1050. ibi_time3=[ibi_time2 additional_ibi_time];
  1051. ibi3=[ibi2 additional_ibi];
  1052. [final_ibi_time,prev_ord]=unique(ibi_time3);
  1053. final_ibi=ibi3(prev_ord);
  1054. ibi=final_ibi;
  1055. ibi_time=final_ibi_time;
  1056. clear i
  1057. end
  1058. filtered_ip=signal;
  1059. filtered_ip_time=time;
  1060. %% d) This part of the code joins pauses which occur within 2 seconds of each other
  1061. pause_ibi_loc=find(ibi>=5); %Identify pauses by finding ibi greater than a specified value
  1062. if ~isempty(pause_ibi_loc)
  1063. pause_start=ibi_time(pause_ibi_loc)-ibi(pause_ibi_loc); %start of pause
  1064. pause_end=ibi_time(pause_ibi_loc); %end of pause
  1065. c=find((pause_start(2:end)-pause_end(1:end-1))<=2); %find pauses less than 2 seconds apart
  1066. if ~isempty(c)
  1067. %Find chains of pauses which are close; cindex1 contains the start indices of the chains, and index2 contains the end indices
  1068. for i=1:length(c)
  1069. if i==1 || c(i)-1~=c(i-1)
  1070. cindex1(i)=c(i);
  1071. end
  1072. if i==length(c) || c(i)+1~=c(i+1)
  1073. cindex2(i)=c(i)+1;
  1074. end
  1075. end
  1076. clear i
  1077. cindex1(cindex1==0)=[];
  1078. cindex2(cindex2==0)=[];
  1079. %alter the ibi and ibi_time values to join the close pauses
  1080. to_delete=cell(1,size(cindex1,2));
  1081. for i=1:size(cindex1,2)
  1082. index1=pause_ibi_loc(cindex1(i));
  1083. index2=pause_ibi_loc(cindex2(i));
  1084. ibi(index1)=sum(ibi(index1:index2));
  1085. ibi_time(index1)=ibi_time(index2);
  1086. to_delete{i}=[index1+1:index2];
  1087. end
  1088. to_delete=cat(2,to_delete{:});
  1089. ibi(to_delete)=[];
  1090. ibi_time(to_delete)=[];
  1091. clear pause_start pause_end
  1092. pause_ibi_loc2=find(ibi>=5);
  1093. if ~isempty(pause_ibi_loc2)
  1094. pause_start=ibi_time(pause_ibi_loc2)-ibi(pause_ibi_loc2);
  1095. pause_end=ibi_time(pause_ibi_loc2);
  1096. else
  1097. pause_start=[];
  1098. pause_end=[];
  1099. end
  1100. end
  1101. end
  1102. %% 12) If requested, run the machine learning model using the remove_artifactual_longpauses function
  1103. if model_input==1
  1104. current_folder=pwd;
  1105. addpath([current_folder '/subfunctions/']);
  1106. [ibi_corrected,response_predictions]=remove_artifactual_longpauses(ibi,ibi_time,...
  1107. sats_time,hr_time,sats,hr,filtered_ip_time,filtered_ip);
  1108. ibi=ibi_corrected;
  1109. end
  1110. end

ip_to_interbreath_intervals.m at commit 72a9ae4, no license · at the source

Overview

Authors: Coen S Zandvoort1, Fatima Usman1, Shellie Robinson1, Odunayo Fatunla1, Eleri Adams2, Kyle TS Pattinson3,4, Simon F Farmer5,6, Caroline Hartley1
  1. Department of Paediatrics, University of Oxford Oxford United Kingdom
  2. Newborn Care Unit, John Radcliffe Hospital, Oxford University Hospitals NHS Foundation Trust Oxford United Kingdom
  3. Nuffield Department of Clinical Neurosciences, University of Oxford Oxford United Kingdom
  4. Nuffield Department of Anaesthetics, Oxford University Hospitals NHS Foundation Trust Oxford United Kingdom
  5. Department of Neurology, National Hospital for Neurology and Neurosurgery London United Kingdom
  6. Department of Clinical and Human Neuroscience, UCL Institute of Neurology London United Kingdom
Journal: eLife, volume 14, article RP107081
Dates: published online 12 May 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.7554/elife.107081 · PMID 42118681 · PMCID PMC13167109 · OpenAlex W4413342978
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: EEG (modality), human (organism), developmental (subfield)
Methods: Statistics, Spectral & time-frequency, Connectivity, Machine learning, Preprocessing, Physiology & signal measures
Keywords: EEG, cortical-respiratory coupling, neonate, brain-body interaction, Human
MeSH: Apnea*, Motor Cortex*, Respiration*, Electroencephalography, Female, Humans, Infant, Newborn, Infant, Premature, Male (* major topic)
Journal subjects: Neuroscience
Topic: Neuroscience of respiration and sleep (Endocrine and Autonomic Systems, Neuroscience), according to OpenAlex
Funding: Wellcome Trust (Sir Henry Dale Fellowship 213486/Z/18/Z); Commonwealth Scholarship Commission; University of Oxford; Royal Society (Sir Henry Dale Fellowship 213486/Z/18/Z)
Citations: cited by 1 paper (Europe PMC); 39 references in the paper

Abstract

Respiration is governed by a widespread network of cortical and subcortical structures. This complex communication between the brain and lungs is altered in pathological conditions. Apnoea – the cessation of respiration – is a common condition in infants, particularly those born prematurely. Apnoea in infants is believed to relate to immaturity of brainstem respiratory centres; involvement of the cortex in respiration in infants has yet to be explored. We investigated if there was any evidence for cortical coupling with respiration in newborn humans and whether it relates to apnoea. Using simultaneous electroencephalography (EEG) and impedance pneumography, we investigated interactions between cortical and respiratory activity (known as cortico-respiratory coupling) using phase-amplitude coupling. We show that cortico-respiratory coupling is present in premature and term newborns (104 recordings from 68 infants; 34.5±2.6 weeks postmenstrual age), identifying an interplay between breathing phase and EEG amplitude. We further shed light on the biological meaning by revealing that the strongest coupling occurs during inspiration and that cortical activity precedes respiration, with coupling strongest over frontocentral regions. Whilst our study was limited in spatial resolution, and determining causality is challenging, we believe these findings support the notion that the cortico-respiratory coupling observed here constitutes communication between cortical motor areas and lung effectors. Moreover, we show that cortico-respiratory coupling is negatively correlated with the rate of apnoea, revealing novel insight into this common and potentially life-threatening neonatal pathology.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 15 matches between paragraphs and lines of code.

paediatric_neuroimaging/cortico_respiratory_coupling

License: GPL-3.0
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: d4370e2c53c38e523f1149d12e0b95f7b4572002, 11 May 2026
Languages: MATLAB (27)
Size: 41 files, 27 scripts
Software Heritage: archived
Found in: “Data availability”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Signal Processing Toolbox (5 files), FieldTrip (4 files), EEGLAB (2 files), Image Processing Toolbox (2 files), Statistics and Machine Learning Toolbox (2 files), Brainstorm (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
29 files

paediatric_neuroimaging/identify_ibi_from_ip

License: none: the authors keep all their rights
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 72a9ae4bc288257158bdbce4f382b36f1142a387, 1 June 2022
Languages: MATLAB (2)
Size: 6 files, 2 scripts
Software Heritage: archived
Found in: the references
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
3 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 29 scripts, each with its path and the digest of its content;
  • 15 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data availability

Ethical restrictions stipulate that data can be shared with other researchers in adherence with ethical constraints (e.g. time limits on data usage). Due to these restrictions, the raw data (raw EEG and vital signs recordings) that support the findings of this study are available on reasonable request. Requests can be made directly to the corresponding author or directed to the Paediatric Neuroimaging Group (University of Oxford) through the institutional will be required to agree to destroy the data within the time limits stipulated in the ethically approved study protocols. Upon agreement, data will be provided within 1 month. Access can only be provided to other researchers, owing to ethical constraints. Summary-level data used to produce figures, and channel- and recording-specific PAC spectra are provided at https://gitlab.com/paediatric_neuroimaging/cortico_respiratory_coupling. The codes for all analyses are available at: https://gitlab.com/paediatric_neuroimaging/cortico_respiratory_coupling (copy archived at Hartley, 2026). The algorithm for inter-breath interval detection is online at: https://gitlab.com/paediatric_neuroimaging/identify_ibi_from_ip (Hartley, 2022).

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 28 September 2026: the first record

Recorded: type, language, journal, volume, pages, dates, 8 authors, 5 keywords, 9 MeSH terms, 4 funders, 37 references.

Cite

This paper

Zandvoort, C. S., Usman, F., Robinson, S., Fatunla, O., Adams, E., Pattinson, K. T., Farmer, S. F., & Hartley, C. (2026). Cortical motor activity modulates respiration and reduces apnoea in neonates. eLife, 14, RP107081. https://doi.org/10.7554/elife.107081

BibTeX

@article{zandvoort2026cortical,
author = {Zandvoort, Coen S and Usman, Fatima and Robinson, Shellie and Fatunla, Odunayo and Adams, Eleri and Pattinson, Kyle TS and Farmer, Simon F and Hartley, Caroline},
title = {{Cortical motor activity modulates respiration and reduces apnoea in neonates}},
journal = {eLife},
year = {2026},
month = may,
volume = {14},
pages = {RP107081},
publisher = {eLife Sciences Publications, Ltd},
issn = {2050-084X},
doi = {10.7554/elife.107081},
url = {https://doi.org/10.7554/elife.107081},
pmid = {42118681},
pmcid = {PMC13167109}
}

RIS

TY - JOUR
AU - Zandvoort, Coen S
AU - Usman, Fatima
AU - Robinson, Shellie
AU - Fatunla, Odunayo
AU - Adams, Eleri
AU - Pattinson, Kyle TS
AU - Farmer, Simon F
AU - Hartley, Caroline
TI - Cortical motor activity modulates respiration and reduces apnoea in neonates
T2 - eLife
J2 - Elife
PY - 2026
DA - 2026/05/12
VL - 14
SP - RP107081
SN - 2050-084X
PB - eLife Sciences Publications, Ltd
DO - 10.7554/elife.107081
UR - https://doi.org/10.7554/elife.107081
LA - en
ER -

CSL-JSON

{
"id": "10.7554/elife.107081",
"type": "article-journal",
"title": "Cortical motor activity modulates respiration and reduces apnoea in neonates",
"container-title": "eLife",
"author": [
{
"family": "Zandvoort",
"given": "Coen S"
},
{
"family": "Usman",
"given": "Fatima"
},
{
"family": "Robinson",
"given": "Shellie"
},
{
"family": "Fatunla",
"given": "Odunayo"
},
{
"family": "Adams",
"given": "Eleri"
},
{
"family": "Pattinson",
"given": "Kyle TS"
},
{
"family": "Farmer",
"given": "Simon F"
},
{
"family": "Hartley",
"given": "Caroline"
}
],
"container-title-short": "Elife",
"volume": "14",
"page": "RP107081",
"DOI": "10.7554/elife.107081",
"PMID": "42118681",
"PMCID": "PMC13167109",
"ISSN": "2050-084X",
"publisher": "eLife Sciences Publications, Ltd",
"URL": "https://doi.org/10.7554/elife.107081",
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
12
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1371/journal.pbio.3003982 [code]
No evidence for modulation of the readiness potential by respiratory phase during natural breathing.
Journal: PLoS biology
In common: FieldTrip, Signal Processing Toolbox, Statistics and Machine Learning Toolbox, EEG, 7 references
[2] doi:10.1038/s41467-026-73828-0 [code]
Human forebrain neural synchronization and entrainment to breathing during wakefulness, sleep, and external mechanical ventilation.
Journal: Nature communications
In common: Signal Processing Toolbox, Statistics and Machine Learning Toolbox, 7 references
[3] doi:10.1038/s41467-026-71604-8 [code]
Respiration as a dynamic modulator of sensory sampling.
Journal: Nature communications
In common: FieldTrip, Signal Processing Toolbox, Statistics and Machine Learning Toolbox, 5 references
[4] doi:10.1111/psyp.70271 [code]
Disentangling Respiratory Phase-Dependent and Phase-Independent Components of Anticipatory Cardiac Deceleration.
Journal: Psychophysiology
In common: EEGLAB, FieldTrip, Image Processing Toolbox, 2 other tools, EEG, 3 references
[5] doi:10.1038/s42003-026-10164-5 [code]
The influence of nonlinear resonance on human cortical oscillations.
Journal: Communications biology
In common: Brainstorm, EEGLAB, FieldTrip, 2 other tools, EEG, 1 reference
[6] doi:10.1038/s41467-026-73106-z [code]
Respiratory pauses highlight sleep architecture in mice.
Journal: Nature communications
In common: EEGLAB, FieldTrip, Image Processing Toolbox, 2 other tools, EEG, 2 references
[7] doi:10.1038/s41598-026-49900-6 [code]
Global neural oscillations underlie performance variability and attentional state fluctuations in humans.
Journal: Scientific reports
In common: Brainstorm, EEGLAB, FieldTrip, 3 other tools
[8] doi:10.1162/imag.a.1201 [code]
All spectral frequencies of neural activity reveal semantic representation in the human anterior ventral temporal cortex.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Brainstorm, EEGLAB, FieldTrip, 3 other tools
[9] doi:10.1371/journal.pcbi.1014672 [code]
Robust circular cluster-based statistics for respiration-brain coupling.
Journal: PLoS computational biology
In common: Signal Processing Toolbox, Statistics and Machine Learning Toolbox, 5 references
[10] doi:10.1371/journal.pbio.3003938 [code]
Theta oscillations tag episodic memories for sleep-dependent consolidation.
Journal: PLoS biology
In common: EEGLAB, FieldTrip, Image Processing Toolbox, 2 other tools, EEG, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.