rpipe 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/.document +5 -0
  2. data/.gitignore +23 -0
  3. data/LICENSE +20 -0
  4. data/README +0 -0
  5. data/README.rdoc +33 -0
  6. data/Rakefile +78 -0
  7. data/VERSION +1 -0
  8. data/bin/create_driver.rb +79 -0
  9. data/bin/rpipe +131 -0
  10. data/bin/swallow_batch_run.rb +21 -0
  11. data/lib/core_additions.rb +5 -0
  12. data/lib/custom_methods/JohnsonMerit220Visit1Preproc.m +26 -0
  13. data/lib/custom_methods/JohnsonMerit220Visit1Preproc.rb +43 -0
  14. data/lib/custom_methods/JohnsonMerit220Visit1Preproc_job.m +80 -0
  15. data/lib/custom_methods/JohnsonMerit220Visit1Stats.m +74 -0
  16. data/lib/custom_methods/JohnsonMerit220Visit1Stats.rb +63 -0
  17. data/lib/custom_methods/JohnsonMerit220Visit1Stats_job.m +63 -0
  18. data/lib/custom_methods/JohnsonTbiLongitudinalSnodPreproc.m +26 -0
  19. data/lib/custom_methods/JohnsonTbiLongitudinalSnodPreproc.rb +41 -0
  20. data/lib/custom_methods/JohnsonTbiLongitudinalSnodPreproc_job.m +69 -0
  21. data/lib/custom_methods/JohnsonTbiLongitudinalSnodStats.m +76 -0
  22. data/lib/custom_methods/JohnsonTbiLongitudinalSnodStats.rb +67 -0
  23. data/lib/custom_methods/JohnsonTbiLongitudinalSnodStats_job.m +59 -0
  24. data/lib/custom_methods/ReconWithHello.rb +7 -0
  25. data/lib/default_logger.rb +13 -0
  26. data/lib/default_methods/default_preproc.rb +76 -0
  27. data/lib/default_methods/default_recon.rb +80 -0
  28. data/lib/default_methods/default_stats.rb +94 -0
  29. data/lib/default_methods/recon/physionoise_helper.rb +69 -0
  30. data/lib/default_methods/recon/raw_sequence.rb +109 -0
  31. data/lib/generators/job_generator.rb +36 -0
  32. data/lib/generators/preproc_job_generator.rb +31 -0
  33. data/lib/generators/recon_job_generator.rb +76 -0
  34. data/lib/generators/stats_job_generator.rb +70 -0
  35. data/lib/generators/workflow_generator.rb +128 -0
  36. data/lib/global_additions.rb +18 -0
  37. data/lib/logfile.rb +310 -0
  38. data/lib/matlab_helpers/CreateFunctionalVolumeStruct.m +6 -0
  39. data/lib/matlab_helpers/import_csv.m +32 -0
  40. data/lib/matlab_helpers/matlab_queue.rb +37 -0
  41. data/lib/matlab_helpers/prepare_onsets_xls.m +30 -0
  42. data/lib/rpipe.rb +254 -0
  43. data/rpipe.gemspec +177 -0
  44. data/spec/generators/preproc_job_generator_spec.rb +27 -0
  45. data/spec/generators/recon_job_generator_spec.rb +33 -0
  46. data/spec/generators/stats_job_generator_spec.rb +50 -0
  47. data/spec/generators/workflow_generator_spec.rb +97 -0
  48. data/spec/helper_spec.rb +40 -0
  49. data/spec/integration/johnson.merit220.visit1_spec.rb +47 -0
  50. data/spec/integration/johnson.tbi.longitudinal.snod_spec.rb +48 -0
  51. data/spec/logfile_spec.rb +96 -0
  52. data/spec/matlab_queue_spec.rb +40 -0
  53. data/spec/merit220_stats_spec.rb +81 -0
  54. data/spec/physio_spec.rb +98 -0
  55. data/test/drivers/merit220_workflow_sample.yml +15 -0
  56. data/test/drivers/mrt00000.yml +65 -0
  57. data/test/drivers/mrt00015.yml +62 -0
  58. data/test/drivers/mrt00015_hello.yml +41 -0
  59. data/test/drivers/mrt00015_withphys.yml +81 -0
  60. data/test/drivers/tbi000.yml +129 -0
  61. data/test/drivers/tbi000_separatevisits.yml +137 -0
  62. data/test/drivers/tmp.yml +58 -0
  63. data/test/fixtures/faces3_recognitionA.mat +0 -0
  64. data/test/fixtures/faces3_recognitionA.txt +86 -0
  65. data/test/fixtures/faces3_recognitionA_equal.csv +25 -0
  66. data/test/fixtures/faces3_recognitionA_unequal.csv +21 -0
  67. data/test/fixtures/faces3_recognitionB_incmisses.txt +86 -0
  68. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CPd3R_40.txt +13360 -0
  69. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CPd3_40.txt +13360 -0
  70. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CPttl_40.txt +13360 -0
  71. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CRTd3R_40.txt +13360 -0
  72. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CRTd3_40.txt +13360 -0
  73. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CRTttl_40.txt +13360 -0
  74. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_CRTd3R_40.txt +334 -0
  75. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_CRTd3_40.txt +334 -0
  76. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_CRTttl_40.txt +334 -0
  77. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_RRT_40.txt +334 -0
  78. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_RVT_40.txt +334 -0
  79. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_card_spline_40.txt +334 -0
  80. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_resp_spline_40.txt +334 -0
  81. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_RRT_40.txt +9106 -0
  82. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_RVT_40.txt +9106 -0
  83. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_CRTd3R_40.txt +167 -0
  84. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_CRTd3_40.txt +167 -0
  85. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_CRTttl_40.txt +167 -0
  86. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_RRT_40.txt +167 -0
  87. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_RVT_40.txt +167 -0
  88. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_card_spline_40.txt +167 -0
  89. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_resp_spline_40.txt +167 -0
  90. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_card_spline_40.txt +13360 -0
  91. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_resp_spline_40.txt +9106 -0
  92. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_resp_spline_downsampled_40.txt +9106 -0
  93. data/test/fixtures/ruport_summary.yml +123 -0
  94. data/test/fixtures/valid_scans.yaml +35 -0
  95. data/test/helper.rb +10 -0
  96. data/test/test_dynamic_method_inclusion.rb +10 -0
  97. data/test/test_includes.rb +11 -0
  98. data/test/test_integrative_johnson.merit220.visit1.rb +31 -0
  99. data/test/test_preproc.rb +11 -0
  100. data/test/test_recon.rb +11 -0
  101. data/test/test_rpipe.rb +19 -0
  102. data/vendor/output_catcher.rb +93 -0
  103. data/vendor/trollop.rb +781 -0
  104. metadata +260 -0
@@ -0,0 +1,63 @@
1
+ require 'logfile'
2
+ module JohnsonMerit220Visit1Stats
3
+
4
+ DEFAULT_CONDITIONS = [:new_correct_, :new_incorrect_, :old_correct_, :old_incorrect, {:misses => [:new_misses, :old_misses]} ]
5
+
6
+ # runs the complete set of tasks using data in a subject's "proc" directory and a preconfigured template spm job.
7
+ def run_first_level_stats
8
+ flash "Highway to the dangerzone..."
9
+ setup_directory(@statsdir, "STATS")
10
+
11
+ Dir.chdir(@statsdir) do
12
+ link_files_from_proc_directory(File.join(@procdir, "sw*.nii"), File.join(@procdir, "rp*.txt"))
13
+ setup_onsets
14
+ run_stats_spm_job
15
+ end
16
+ end
17
+
18
+ alias_method :perform, :run_first_level_stats
19
+
20
+ def setup_onsets
21
+ setup_conditions
22
+ create_or_link_onsets_files
23
+ end
24
+
25
+ def setup_conditions
26
+ @conditions = @conditions ? @conditions.collect! {|c| c.to_sym if c.respond_to? :to_sym } : DEFAULT_CONDITIONS
27
+ end
28
+
29
+ def create_or_link_onsets_files
30
+ if @onsetsfiles.nil?
31
+ if @responses.nil?
32
+ raise ScriptError, "Condition vectors cannot be created because neither log response files nor onsets mat files have been specified."
33
+ else
34
+ @onsetsfiles = create_onsets_files(@responses, conditions)
35
+ end
36
+ else
37
+ link_onsets_files
38
+ end
39
+ end
40
+
41
+ # Finally runs the stats job
42
+ def run_stats_spm_job
43
+ images = Dir.glob(File.join(@statsdir, "sw*#{@subid}*[Tt]ask*.nii"))
44
+ raise ScriptError, "Can't find any smoothed, warped images in #{@statsdir}" if images.empty?
45
+
46
+ queue = MatlabQueue.new
47
+ queue.paths << ['/Applications/spm/spm8/spm8_current',
48
+ '/apps/spm/spm8_current',
49
+ File.join(@libdir, 'custom_methods'),
50
+ File.join(@libdir, 'matlab_helpers') ]
51
+
52
+ queue << "#{@method}('#{@statsdir}/', \
53
+ { #{images.collect {|im| "'#{File.basename(im)}'"}.join(' ')} }, \
54
+ { #{@bold_reps.join(' ') } }, \
55
+ { #{@onsetsfiles.collect { |file| "'#{File.basename(file)}'"}.join(' ') } }, \
56
+ { #{@regressorsfiles.collect { |file| "'#{File.basename(file)}'"}.join(' ') } }, \
57
+ '#{@method}_job.m')"
58
+
59
+ # puts queue.to_s
60
+ queue.run!
61
+ end
62
+
63
+ end
@@ -0,0 +1,63 @@
1
+ %-----------------------------------------------------------------------
2
+ % Job configuration created by cfg_util (rev $Rev: 3599 $)
3
+ %-----------------------------------------------------------------------
4
+ %% Specification
5
+ matlabbatch{1}.spm.stats.fmri_spec.dir = '<UNDEFINED>';
6
+ matlabbatch{1}.spm.stats.fmri_spec.timing.units = 'scans';
7
+ matlabbatch{1}.spm.stats.fmri_spec.timing.RT = 2;
8
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t = 16;
9
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t0 = 1;
10
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).scans = '<UNDEFINED>';
11
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).cond = struct('name', {}, 'onset', {}, 'duration', {}, 'tmod', {}, 'pmod', {});
12
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).multi = '<UNDEFINED>';
13
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).regress = struct('name', {}, 'val', {});
14
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).multi_reg = '<UNDEFINED>';
15
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).hpf = 128;
16
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).scans = '<UNDEFINED>';
17
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).cond = struct('name', {}, 'onset', {}, 'duration', {}, 'tmod', {}, 'pmod', {});
18
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).multi = '<UNDEFINED>';
19
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).regress = struct('name', {}, 'val', {});
20
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).multi_reg = '<UNDEFINED>';
21
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).hpf = 128;
22
+ matlabbatch{1}.spm.stats.fmri_spec.fact = struct('name', {}, 'levels', {});
23
+ matlabbatch{1}.spm.stats.fmri_spec.bases.hrf.derivs = [0 0];
24
+ matlabbatch{1}.spm.stats.fmri_spec.volt = 1;
25
+ matlabbatch{1}.spm.stats.fmri_spec.global = 'None';
26
+ matlabbatch{1}.spm.stats.fmri_spec.mask = {''};
27
+ matlabbatch{1}.spm.stats.fmri_spec.cvi = 'AR(1)';
28
+ %% Estimation
29
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1) = cfg_dep;
30
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tname = 'Select SPM.mat';
31
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).name = 'filter';
32
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).value = 'mat';
33
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).name = 'strtype';
34
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).value = 'e';
35
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).sname = 'fMRI model specification: SPM.mat File';
36
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
37
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_output = substruct('.','spmmat');
38
+ matlabbatch{2}.spm.stats.fmri_est.method.Classical = 1;
39
+ %% Contrast Creation
40
+ matlabbatch{3}.spm.stats.con.spmmat(1) = cfg_dep;
41
+ matlabbatch{3}.spm.stats.con.spmmat(1).tname = 'Select SPM.mat';
42
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(1).name = 'filter';
43
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(1).value = 'mat';
44
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(2).name = 'strtype';
45
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(2).value = 'e';
46
+ matlabbatch{3}.spm.stats.con.spmmat(1).sname = 'Model estimation: SPM.mat File';
47
+ matlabbatch{3}.spm.stats.con.spmmat(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1});
48
+ matlabbatch{3}.spm.stats.con.spmmat(1).src_output = substruct('.','spmmat');
49
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.name = '<UNDEFINED>';
50
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.convec{1} = '<UNDEFINED>';
51
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.sessrep = 'repl';
52
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.name = '<UNDEFINED>';
53
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.convec = '<UNDEFINED>';
54
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.sessrep = 'repl';
55
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.name = '<UNDEFINED>';
56
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.convec = '<UNDEFINED>';
57
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.sessrep = 'repl';
58
+ matlabbatch{3}.spm.stats.con.delete = 0;
59
+
60
+ % NOTE! F-contrasts expect cells containing contrast arrays, while
61
+ % T-contrasts expect the arrays themslves (hence the cell reference in
62
+ % F-contrast definition (convec{1} = [];) but not in T-contrast definition
63
+ % (convec = [];)
@@ -0,0 +1,26 @@
1
+ function [] = JohnsonTbiLongitudinalSnodPreproc(studypath, images, image_boldreps, job_mfile)
2
+ % [] = Calls and runs TBI Longitudinal preproc job.
3
+ % merit_preproc('/private/tmp/mrt00015_orig/', ...
4
+ % {'amrt00015_task1.nii' 'amrt00015_task2.nii' 'amrt00015_rest.nii'}, ...
5
+ % {164 164 164}, ...
6
+ % '/private/tmp/mrt00015_orig/mrt00015_preproc_job.m')
7
+ %
8
+
9
+ spm('defaults', 'FMRI');
10
+
11
+ % List of open inputs
12
+ % Realign: Estimate: Session - cfg_files
13
+ % Realign: Estimate: Session - cfg_files
14
+ % Realign: Estimate: Session - cfg_files
15
+ nrun = 1; % enter the number of runs here
16
+ jobfile = {job_mfile};
17
+ jobs = repmat(jobfile, 1, nrun);
18
+ inputs = cell(3, nrun);
19
+
20
+ for crun = 1:nrun
21
+ for index = 1:length(images)
22
+ inputs{index, crun} = CreateFunctionalVolumeStruct(studypath, images{index}, image_boldreps{index}); % Realign: Estimate: Session - cfg_files
23
+ end
24
+ end
25
+
26
+ spm_jobman('serial', jobs, '', inputs{:});
@@ -0,0 +1,41 @@
1
+ require 'matlab_helpers/matlab_queue'
2
+ module JohnsonTbiLongitudinalSnodPreproc
3
+
4
+ # Runs the preprocessing job, including spm job customization, run spm job, and handling motion issues.
5
+ # This function assumes a destination directory is set up; it will overwrite preexisting data. Careful!
6
+ def preproc_visit
7
+ flash "Spatial Preprocessing Subject: #{@subid}"
8
+ setup_directory(@procdir, "PREPROC")
9
+
10
+ Dir.chdir(@procdir) do
11
+ link_files_into_proc
12
+ run_preproc_mfile
13
+ deal_with_motion
14
+ end
15
+ end
16
+
17
+ alias_method :perform, :preproc_visit
18
+
19
+ private
20
+
21
+ def run_preproc_mfile
22
+ images = @scan_labels ? @scan_labels.collect! { |label| Dir.glob("a*#{label}*.nii").to_s } : Dir.glob(File.join(@origdir, "a*#{@subid}*.nii"))
23
+
24
+ raise ScriptError, "Can't find any slice-time corrected images in #{@origdir}" if images.empty?
25
+ queue = MatlabQueue.new
26
+ queue.paths << ['/Applications/spm/spm8/spm8_current',
27
+ '/apps/spm/spm8_current',
28
+ File.join(@libdir, 'custom_methods'),
29
+ File.join(@libdir, 'matlab_helpers')
30
+ ]
31
+
32
+ queue << "JohnsonTbiLongitudinalSnodPreproc('#{@procdir}/', \
33
+ { #{images.collect {|im| "'#{File.basename(im)}'"}.join(' ')} }, \
34
+ { #{@bold_reps.join(' ') } }, \
35
+ 'JohnsonTbiLongitudinalSnodPreproc_job.m')"
36
+
37
+ puts queue.to_s
38
+ queue.run!
39
+ end
40
+
41
+ end
@@ -0,0 +1,69 @@
1
+ %-----------------------------------------------------------------------------
2
+ % Unfortunately, there is no easy way to pass in the number of images to this
3
+ % template job, short of writing a function to build one on the fly. Number of
4
+ % sessions is hardcoded.
5
+ nsessions = 6;
6
+ %-----------------------------------------------------------------------------
7
+
8
+ %-----------------------------------------------------------------------
9
+ % Job configuration created by cfg_util (rev $Rev: 3599 $)
10
+ %-----------------------------------------------------------------------
11
+ for i = 1:nsessions
12
+ matlabbatch{1}.spm.spatial.realign.estimate.data{i} = '<UNDEFINED>';
13
+ end
14
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.quality = 0.9;
15
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.sep = 4;
16
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.fwhm = 5;
17
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.rtm = 1;
18
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.interp = 2;
19
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.wrap = [0 0 0];
20
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.weight = {''};
21
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1) = cfg_dep;
22
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tname = 'Source Image';
23
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tgt_spec{1}(1).name = 'filter';
24
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tgt_spec{1}(1).value = 'image';
25
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tgt_spec{1}(2).name = 'strtype';
26
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tgt_spec{1}(2).value = 'e';
27
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).sname = 'Realign: Estimate: Realigned Images (Sess 1)';
28
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
29
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).src_output = substruct('.','sess', '()',{1}, '.','cfiles');
30
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.wtsrc = '';
31
+ for i = 1:nsessions
32
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i) = cfg_dep;
33
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tname = 'Images to Write';
34
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tgt_spec{1}(1).name = 'filter';
35
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tgt_spec{1}(1).value = 'image';
36
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tgt_spec{1}(2).name = 'strtype';
37
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tgt_spec{1}(2).value = 'e';
38
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).sname = ['Realign: Estimate: Realigned Images (Sess' i ')'];
39
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
40
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).src_output = substruct('.','sess', '()',{i}, '.','cfiles');
41
+ end
42
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.template = {[spm('Dir') '/templates/EPI.nii,1']};
43
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.weight = '';
44
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.smosrc = 8;
45
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.smoref = 0;
46
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.regtype = 'mni';
47
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.cutoff = 25;
48
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.nits = 16;
49
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.reg = 1;
50
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.preserve = 0;
51
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.bb = [-78 -112 -50
52
+ 78 76 85];
53
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.vox = [2 2 2];
54
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.interp = 1;
55
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.wrap = [0 0 0];
56
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.prefix = 'w';
57
+ matlabbatch{3}.spm.spatial.smooth.data(1) = cfg_dep;
58
+ matlabbatch{3}.spm.spatial.smooth.data(1).tname = 'Images to Smooth';
59
+ matlabbatch{3}.spm.spatial.smooth.data(1).tgt_spec{1}(1).name = 'filter';
60
+ matlabbatch{3}.spm.spatial.smooth.data(1).tgt_spec{1}(1).value = 'image';
61
+ matlabbatch{3}.spm.spatial.smooth.data(1).tgt_spec{1}(2).name = 'strtype';
62
+ matlabbatch{3}.spm.spatial.smooth.data(1).tgt_spec{1}(2).value = 'e';
63
+ matlabbatch{3}.spm.spatial.smooth.data(1).sname = 'Normalise: Estimate & Write: Normalised Images (Subj 1)';
64
+ matlabbatch{3}.spm.spatial.smooth.data(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
65
+ matlabbatch{3}.spm.spatial.smooth.data(1).src_output = substruct('()',{1}, '.','files');
66
+ matlabbatch{3}.spm.spatial.smooth.fwhm = [8 8 8];
67
+ matlabbatch{3}.spm.spatial.smooth.dtype = 0;
68
+ matlabbatch{3}.spm.spatial.smooth.im = 0;
69
+ matlabbatch{3}.spm.spatial.smooth.prefix = 's';
@@ -0,0 +1,76 @@
1
+ function [] = JohnsonTbiLongitudinalSnodStats(statsdir, images, image_boldreps, conditions, regressors, job_mfile)
2
+
3
+ % List of open inputs
4
+ % fMRI model specification: Directory - cfg_entry
5
+ % 6 Scans Each with:
6
+ % fMRI model specification: Scans
7
+ % fMRI model specification: Multiple Conditions File - cfg_entry
8
+ % fMRI model specification: Multiple Regressors File - cfg_entry
9
+
10
+ spm('defaults', 'FMRI');
11
+
12
+ % statsdir = '/private/tmp/mrt00015_stats/'
13
+ % images = { 'swamrt00015_task1.nii' 'swamrt00015_task2.nii' }
14
+ % image_boldreps = {164 164}
15
+ % regressors = { 'mrt00015_faces3_recognitionA.mat' 'mrt00015_faces3_recognitionB.mat' }
16
+ % job_mfile = '/private/tmp/mrt00015_stats/Merit220Stats_job.m'
17
+
18
+ nrun = 1; % enter the number of runs here
19
+ % jobfile = {'/private/tmp/mrt00015_proc/Merit220_Stats_job.m'};
20
+ jobfile = {job_mfile};
21
+ jobs = repmat(jobfile, 1, nrun);
22
+ inputs = cell(length(images) + 1, nrun);
23
+
24
+ % Load the first conditions file to get the length of the names.
25
+ eval(['load ' conditions{1}]);
26
+
27
+ switch numel(names)
28
+ case 2 % New, Old
29
+ fcontrast_vector = [1 0
30
+ 0 1];
31
+ tcontrast_1_vector = [-1 1];
32
+ tcontrast_2_vector = [1 -1];
33
+ % case 4 % New_correct, New_incorrect, Old_correct, Old_incorrect
34
+ % fcontrast_vector = [1 0 0 0
35
+ % 0 1 0 0
36
+ % 0 0 1 0
37
+ % 0 0 0 1 ];
38
+ % tcontrast_1_vector = [-1 0 1 0];
39
+ % tcontrast_2_vector = [ 1 0 -1 0];
40
+ % case 5 % New_correct, New_incorrect, Old_correct, Old_incorrect, Misses
41
+ % fcontrast_vector = [1 0 0 0
42
+ % 0 1 0 0
43
+ % 0 0 1 0
44
+ % 0 0 0 1 ];
45
+ % tcontrast_1_vector = [-1 0 1 0];
46
+ % tcontrast_2_vector = [ 1 0 -1 0];
47
+ otherwise
48
+ error('Incorrect number of conditions detected: %d', numel(conditions));
49
+ end
50
+
51
+ for crun = 1:nrun
52
+ inputs{1, crun} = { statsdir }; % fMRI model specification: Directory - cfg_entry
53
+
54
+ % fMRI model specification: Directory - cfg_entry
55
+ for index = 1:length(images)
56
+ offset = (index - 1) .* 3;
57
+ inputs{offset + 2, crun} = CreateFunctionalVolumeStruct(statsdir, images{index}, image_boldreps{index}); % fMRI model specification: Scans - cfg_entry
58
+ inputs{offset + 3, crun} = { strcat(statsdir, conditions{index} ) }; % fMRI model specification: Multiple Conditions File - cfg_entry
59
+ inputs{offset + 4, crun} = { strcat(statsdir, regressors{index} ) }; % fMRI model specification: Multiple Regressors File - cfg_entry
60
+ end
61
+
62
+ image_offset = (length(images) .* 3) + 1;
63
+ inputs{image_offset + 1, crun} = 'Omnibus F'; % fMRI model specification: Omnibus Title - cfg_entry
64
+ inputs{image_offset + 2, crun} = fcontrast_vector; % fMRI model specification: Omnibus Contrast - cfg_entry
65
+
66
+ inputs{image_offset + 3, crun} = 'NV > PV'; % fMRI model specification: T Contrast 1 Title - cfg_entry
67
+ inputs{image_offset + 4, crun} = tcontrast_1_vector; % fMRI model specification: T Contrast 1 Vector - cfg_entry
68
+
69
+ inputs{image_offset + 5, crun} = 'PV > NV'; % fMRI model specification: T Contrast 2 Title - cfg_entry
70
+ inputs{image_offset + 6, crun} = tcontrast_2_vector; % fMRI model specification: T Contrast 2 Vector - cfg_entry
71
+
72
+ end
73
+
74
+
75
+
76
+ spm_jobman('serial', jobs, '', inputs{:});
@@ -0,0 +1,67 @@
1
+ # require 'logfile'
2
+ module JohnsonTbiLongitudinalSnodStats
3
+
4
+ DEFAULT_CONDITIONS = [:new, :old]
5
+
6
+ # runs the complete set of tasks using data in a subject's "proc" directory and a preconfigured template spm job.
7
+ def run_first_level_stats
8
+ flash "Highway to the dangerzone..."
9
+ setup_directory(@statsdir, "STATS")
10
+
11
+ Dir.chdir(@statsdir) do
12
+ link_files_from_proc_directory(File.join(@procdir, "sw*.nii"), File.join(@procdir, "rp*.txt"))
13
+ setup_onsets
14
+ run_stats_spm_job
15
+ end
16
+ end
17
+
18
+ alias_method :perform, :run_first_level_stats
19
+
20
+ def setup_onsets
21
+ setup_conditions
22
+ create_or_link_onsets_files
23
+ end
24
+
25
+ def setup_conditions
26
+ @conditions = @conditions ? @conditions.collect {|c| c.to_sym if c.respond_to? :to_sym } : DEFAULT_CONDITIONS
27
+ end
28
+
29
+ def create_or_link_onsets_files
30
+ if @onsetsfiles.nil?
31
+ if @responses.nil?
32
+ raise ScriptError, "Condition vectors cannot be created because neither log response files nor onsets mat files have been specified."
33
+ else
34
+ @onsetsfiles = create_onsets_files(@responses, conditions)
35
+ end
36
+ else
37
+ link_onsets_files
38
+ end
39
+ end
40
+
41
+ # Finally runs the stats job
42
+ def run_stats_spm_job
43
+ images = @scan_labels ? @scan_labels.collect! { |label| Dir.glob("sw*#{label}*.nii").to_s } : Dir.glob(File.join(@origdir, "sw*#{@subid}*.nii"))
44
+ raise ScriptError, "Can't find any smoothed, warped images in #{@statsdir}" if images.empty?
45
+ unless @onsetsfiles.length == @regressorsfiles.length && @regressorsfiles.length == @bold_reps.length
46
+ raise ScriptError, "Mismatch between #{@bold_reps.length} reps, #{@onsetsfiles.length} onsets and #{@regressorsfiles.length} regressors files."
47
+ end
48
+
49
+ queue = MatlabQueue.new
50
+ queue.paths << [
51
+ '/Applications/spm/spm8/spm8_current',
52
+ '/apps/spm/spm8_current',
53
+ File.join(@libdir, 'custom_methods'),
54
+ File.join(@libdir, 'matlab_helpers') ]
55
+
56
+ queue << "JohnsonTbiLongitudinalSnodStats('#{@statsdir}/', \
57
+ { #{images.collect {|im| "'#{File.basename(im)}'"}.join(' ')} }, \
58
+ { #{@bold_reps.join(' ') } }, \
59
+ { #{@onsetsfiles.collect { |file| "'#{File.basename(file)}'"}.join(' ') } }, \
60
+ { #{@regressorsfiles.collect { |file| "'#{File.basename(file)}'"}.join(' ') } }, \
61
+ 'JohnsonTbiLongitudinalSnodStats_job.m')"
62
+
63
+ # puts queue.to_s
64
+ queue.run!
65
+ end
66
+
67
+ end
@@ -0,0 +1,59 @@
1
+ %-----------------------------------------------------------------------
2
+ % Job configuration created by cfg_util (rev $Rev: 3599 $)
3
+ %-----------------------------------------------------------------------
4
+ %% Specification
5
+ matlabbatch{1}.spm.stats.fmri_spec.dir = '<UNDEFINED>';
6
+ matlabbatch{1}.spm.stats.fmri_spec.timing.units = 'scans';
7
+ matlabbatch{1}.spm.stats.fmri_spec.timing.RT = 2;
8
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t = 16;
9
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t0 = 1;
10
+ for i = 1:2
11
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).scans = '<UNDEFINED>';
12
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).cond = struct('name', {}, 'onset', {}, 'duration', {}, 'tmod', {}, 'pmod', {});
13
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).multi = '<UNDEFINED>';
14
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).regress = struct('name', {}, 'val', {});
15
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).multi_reg = '<UNDEFINED>';
16
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).hpf = 128;
17
+ end
18
+ matlabbatch{1}.spm.stats.fmri_spec.fact = struct('name', {}, 'levels', {});
19
+ matlabbatch{1}.spm.stats.fmri_spec.bases.hrf.derivs = [0 0];
20
+ matlabbatch{1}.spm.stats.fmri_spec.volt = 1;
21
+ matlabbatch{1}.spm.stats.fmri_spec.global = 'None';
22
+ matlabbatch{1}.spm.stats.fmri_spec.mask = {''};
23
+ matlabbatch{1}.spm.stats.fmri_spec.cvi = 'AR(1)';
24
+ %% Estimation
25
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1) = cfg_dep;
26
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tname = 'Select SPM.mat';
27
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).name = 'filter';
28
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).value = 'mat';
29
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).name = 'strtype';
30
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).value = 'e';
31
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).sname = 'fMRI model specification: SPM.mat File';
32
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
33
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_output = substruct('.','spmmat');
34
+ matlabbatch{2}.spm.stats.fmri_est.method.Classical = 1;
35
+ %% Contrast Creation
36
+ matlabbatch{3}.spm.stats.con.spmmat(1) = cfg_dep;
37
+ matlabbatch{3}.spm.stats.con.spmmat(1).tname = 'Select SPM.mat';
38
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(1).name = 'filter';
39
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(1).value = 'mat';
40
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(2).name = 'strtype';
41
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(2).value = 'e';
42
+ matlabbatch{3}.spm.stats.con.spmmat(1).sname = 'Model estimation: SPM.mat File';
43
+ matlabbatch{3}.spm.stats.con.spmmat(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1});
44
+ matlabbatch{3}.spm.stats.con.spmmat(1).src_output = substruct('.','spmmat');
45
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.name = '<UNDEFINED>';
46
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.convec{1} = '<UNDEFINED>';
47
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.sessrep = 'repl';
48
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.name = '<UNDEFINED>';
49
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.convec = '<UNDEFINED>';
50
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.sessrep = 'repl';
51
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.name = '<UNDEFINED>';
52
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.convec = '<UNDEFINED>';
53
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.sessrep = 'repl';
54
+ matlabbatch{3}.spm.stats.con.delete = 0;
55
+
56
+ % NOTE! F-contrasts expect cells containing contrast arrays, while
57
+ % T-contrasts expect the arrays themslves (hence the cell reference in
58
+ % F-contrast definition (convec{1} = [];) but not in T-contrast definition
59
+ % (convec = [];)
@@ -0,0 +1,7 @@
1
+ module ReconWithHello
2
+
3
+ def hello
4
+ "=== Hello World! ==="
5
+ end
6
+
7
+ end
@@ -0,0 +1,13 @@
1
+ require 'log4r'
2
+
3
+ # Setup Command and General Logs to point to STDOUT if not defined.
4
+ module DefaultLogger
5
+ def setup_logger
6
+ %w{$Log $CommandLog}.each do |log|
7
+ unless eval(log)
8
+ eval("#{log} = Log4r::Logger.new('output')")
9
+ eval("#{log}.add Log4r::StdoutOutputter.new(:stdout)")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,76 @@
1
+ module DefaultPreproc
2
+
3
+ # Runs the preprocessing job, including spm job customization, run spm job, and handling motion issues.
4
+ # This function assumes a destination directory is set up; it will overwrite preexisting data. Careful!
5
+ def preproc_visit
6
+ $Log.info "Spatial Preprocessing Subject: #{@subid}"
7
+
8
+ setup_directory(@procdir, "PREPROC")
9
+
10
+ Dir.chdir(@procdir) do
11
+ link_files_into_proc
12
+ customize_templates
13
+ run_spm_jobs
14
+ deal_with_motion
15
+ end
16
+ end
17
+
18
+ alias_method :perform, :preproc_visit
19
+
20
+ # Links all the slice timing corrected data from the source "orig" directory using a wildcard a${subid}*.nii,
21
+ # where subid is the subject id specified in the preproc_spec hash.
22
+ def link_files_into_proc
23
+ flash "Linking files from #{@origdir} into #{@procdir}"
24
+ wildcard = File.join(@origdir,"a*#{@subid}*.nii")
25
+ files = Dir.glob(wildcard)
26
+ unless files.empty?
27
+ system("ln -s #{wildcard} #{@procdir}")
28
+ else
29
+ raise(IOError, "No files matching #{wildcard} found.")
30
+ end
31
+ end
32
+
33
+ def check_permissions(files)
34
+ unwritable = []
35
+ files.collect { |file| unwritable << file unless File.writable?(file) }
36
+ raise IOError, "Cannot write to #{unwritable.join(", ")} files." unless unwritable.empty?
37
+ end
38
+
39
+ # Customizes the template job in preproc_spec to be specific for this particular preproc job.
40
+ # Performs to recursive string replacements inside the spm job:
41
+ # - path inside template is replaced with destination "proc" directory
42
+ # - subid inside template is replaced with the current subid
43
+ def customize_templates
44
+ flash "Customizing template SPM job: #{@tspec['job']}"
45
+ replacecmd = "spmjobStringReplace.sh"
46
+
47
+ templatejob = File.join(@spmdir, @tspec['job'])
48
+ thisjob = @subid + '_preproc.mat'
49
+
50
+ File.copy(templatejob, thisjob)
51
+ system("#{replacecmd} #{thisjob} #{@tspec['path']} #{@procdir} #{thisjob}")
52
+ system("#{replacecmd} #{thisjob} #{@tspec['subid']} #{@subid} #{thisjob}")
53
+ end
54
+
55
+ # Runs the customized spm job using the shell script runSpmJob.sh. Make sure this is available at your site.
56
+ def run_spm_jobs
57
+ thisjob = "#{@subid}_preproc.mat"
58
+ flash "Running spatial preprocessing SPM job: #{thisjob}"
59
+ system("runSpmJob.sh #{thisjob}")
60
+ end
61
+
62
+ # Calculates the realignment motion derivatives and checks that displacement
63
+ # in all directions was less than the MOTION_THRESHOLD. Operates on all files
64
+ # in the current working directory that match rp_a*txt (SPM convention). Uses
65
+ # two shell scripts that must both be available on the local machine:
66
+ # - calc_derivs.sh
67
+ # - fmri_motion_check.sh
68
+ def deal_with_motion
69
+ flash "Calculating motion derivatives and checking for excessive motion"
70
+ Dir.glob("rp_a*txt").each do |rp|
71
+ run("calc_derivs.sh #{rp}")
72
+ run("fmri_motion_check.sh #{rp} #{@motion_threshold}")
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,80 @@
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', '..', 'physionoise/lib')
2
+
3
+ require 'metamri/core_additions'
4
+ require 'physionoise'
5
+ require 'pathname'
6
+ require 'default_methods/recon/physionoise_helper'
7
+ require 'default_methods/recon/raw_sequence'
8
+
9
+ module DefaultRecon
10
+
11
+ # Reconstructs, strips, and slice timing corrects all scans specified in the recon_spec.
12
+ # This function assumes a destination directory is set up in the filesystem and begins writing
13
+ # to it with no further checking. It will overwrite data if it already exists, be careful.
14
+ def recon_visit
15
+
16
+ setup_directory(@origdir, "RECON")
17
+
18
+ Dir.chdir(@origdir) do
19
+ @scans.each_with_index do |scan_spec, i|
20
+ outfile = "%s_%s.nii" % [@subid, scan_spec['label']]
21
+
22
+ reconstruct_scan(scan_spec, outfile)
23
+
24
+ if scan_spec['type'] == "func"
25
+ # if scan_spec['physio_files']
26
+ # create_physiosnoise_regressors(scan_spec)
27
+ # outfile = run_retroicor(scan_spec['physio_files'], outfile)
28
+ # end
29
+
30
+ slice_time_correct(outfile)
31
+ else
32
+ File.copy('tmp.nii', outfile)
33
+ end
34
+
35
+ File.delete('tmp.nii') if File.exist? 'tmp.nii'
36
+ end
37
+ end
38
+ end
39
+
40
+ alias_method :perform, :recon_visit
41
+
42
+ # Reconstructs a scan from dicoms or pfile to nifti, anatomical or functional.
43
+ # Uses a scan_spec hash to drive. Writes the result in current working
44
+ # directory. Raises an error if to3d system call fails. Conventions: I****.dcm
45
+ # filenaming, I0002.dcm is second file in series,
46
+ def reconstruct_scan(scan_spec, outfile)
47
+ if scan_spec['dir']
48
+ sequence = DicomRawSequence.new(scan_spec, @rawdir)
49
+ sequence.prepare('tmp.nii')
50
+ strip_leading_volumes('tmp.nii', outfile, @volume_skip, scan_spec['bold_reps'])
51
+ elsif scan_spec['pfile']
52
+ sequence = PfileRawSequence.new(scan_spec, @rawdir)
53
+ sequence.prepare(outfile)
54
+ else
55
+ raise ConfigError, "Scan must list either a pfile or a dicom directory."
56
+ end
57
+ end
58
+
59
+ # Removes the specified number of volumes from the beginning of a 4D functional nifti file.
60
+ # In most cases this will be 3 volumes. Writes result in current working directory.
61
+ def strip_leading_volumes(infile, outfile, volume_skip, bold_reps)
62
+ $Log.info "Stripping #{volume_skip.to_s} leading volumes: #{infile}"
63
+ cmd_fmt = "fslroi %s %s %s %s"
64
+ cmd_options = [infile, outfile, volume_skip.to_s, bold_reps.to_s]
65
+ cmd = cmd_fmt % cmd_options
66
+ unless run(cmd)
67
+ raise ScriptError, "Failed to strip volumes: #{cmd}"
68
+ end
69
+ end
70
+
71
+ # Uses to3d to slice time correct a 4D functional nifti file. Writes result in the current working directory.
72
+ def slice_time_correct(infile)
73
+ $Log.info "Slice Timing Correction: #{infile}"
74
+ cmd = "3dTshift -tzero 0 -tpattern alt+z -prefix a#{infile} #{infile}"
75
+ unless run(cmd)
76
+ raise ScriptError, "Failed to slice time correct: #{cmd}"
77
+ end
78
+ end
79
+
80
+ end