@@ -27,7 +27,12 @@ public class MzMlSpectrumWriter : SpectrumWriter
2727 private static readonly ILog Log =
2828 LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2929
30- private const string FilterStringIsolationMzPattern = @"ms2 (.*?)@" ;
30+ private readonly Regex FilterStringIsolationMzPattern = new Regex ( @"ms2 (.*?)@" ) ;
31+
32+ //tune version < 3 produces multiple trailer entry like "SPS Mass [number]"
33+ private readonly Regex SPSentry = new Regex ( @"SPS Mass\s+\d+:" ) ;
34+ //tune version == 3 produces trailer entry "SPS Masses/Continued"
35+ private readonly Regex SPSentry3 = new Regex ( @"SPS Masses(?:\s+Continued)?:" ) ;
3136
3237 private IRawDataPlus _rawFile ;
3338
@@ -1146,7 +1151,7 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
11461151 int ? charge = null ;
11471152 double ? monoisotopicMz = null ;
11481153 double ? ionInjectionTime = null ;
1149- double ? isolationWidth = null ;
1154+ List < double > SPSMasses = new List < double > ( ) ;
11501155 for ( var i = 0 ; i < trailerData . Length ; i ++ )
11511156 {
11521157 if ( trailerData . Labels [ i ] == "Charge State:" )
@@ -1169,10 +1174,21 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
11691174 CultureInfo . CurrentCulture ) ;
11701175 }
11711176
1172- if ( trailerData . Labels [ i ] == "MS" + ( int ) scanFilter . MSOrder + " Isolation Width:" )
1177+ //tune version < 3 produced trailer entry like "SPS Mass #", one entry per mass
1178+ if ( SPSentry . IsMatch ( trailerData . Labels [ i ] ) )
11731179 {
1174- isolationWidth = double . Parse ( trailerData . Values [ i ] , NumberStyles . Any ,
1175- CultureInfo . CurrentCulture ) ;
1180+ var mass = Double . Parse ( trailerData . Values [ i ] ) ;
1181+ if ( mass > 0 ) SPSMasses . Add ( mass ) ; //zero means mass does not exist
1182+ }
1183+
1184+ //tune version == 3 produces trailer entry "SPS Masses", comma separated list of masses
1185+ if ( SPSentry3 . IsMatch ( trailerData . Labels [ i ] ) )
1186+ {
1187+ foreach ( var mass in trailerData . Values [ i ] . Trim ( ) . Split ( new char [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries ) )
1188+ {
1189+ SPSMasses . Add ( Double . Parse ( mass ) ) ;
1190+ }
1191+
11761192 }
11771193 }
11781194
@@ -1206,7 +1222,7 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
12061222 } ) ;
12071223
12081224 // Keep track of scan number and isolation m/z for precursor reference
1209- var result = Regex . Match ( scanEvent . ToString ( ) , FilterStringIsolationMzPattern ) ;
1225+ var result = FilterStringIsolationMzPattern . Match ( scanEvent . ToString ( ) ) ;
12101226 if ( result . Success )
12111227 {
12121228 if ( _precursorMs2ScanNumbers . ContainsKey ( result . Groups [ 1 ] . Value ) )
@@ -1219,7 +1235,7 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
12191235
12201236 // Construct and set the precursor list element of the spectrum
12211237 var precursorListType =
1222- ConstructPrecursorList ( scanEvent , charge , scanFilter . MSOrder , monoisotopicMz , isolationWidth ) ;
1238+ ConstructPrecursorList ( scanEvent , charge , scanFilter . MSOrder , monoisotopicMz , SPSMasses ) ;
12231239 spectrum . precursorList = precursorListType ;
12241240 break ;
12251241 case MSOrderType . Ms3 :
@@ -1230,8 +1246,7 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
12301246 name = "MSn spectrum" ,
12311247 value = ""
12321248 } ) ;
1233- precursorListType = ConstructPrecursorList ( scanEvent , charge , scanFilter . MSOrder , monoisotopicMz ,
1234- isolationWidth ) ;
1249+ precursorListType = ConstructPrecursorList ( scanEvent , charge , scanFilter . MSOrder , monoisotopicMz , SPSMasses ) ;
12351250 spectrum . precursorList = precursorListType ;
12361251 break ;
12371252 default :
@@ -1772,19 +1787,20 @@ private SpectrumType ConstructPDASpectrum(int scanNumber, int instrumentNumber)
17721787 /// <param name="isolationWidth">the isolation width</param>
17731788 /// <returns>the precursor list</returns>
17741789 private PrecursorListType ConstructPrecursorList ( IScanEventBase scanEvent , int ? charge , MSOrderType msLevel ,
1775- double ? monoisotopicMz , double ? isolationWidth )
1790+ double ? monoisotopicMz , List < double > SPSMasses )
17761791 {
17771792 // Construct the precursor
17781793 var precursorList = new PrecursorListType
17791794 {
1780- count = "1" ,
1781- precursor = new PrecursorType [ 1 ]
1795+ count = ( Math . Max ( SPSMasses . Count , 1 ) ) . ToString ( ) ,
1796+ precursor = new PrecursorType [ Math . Max ( SPSMasses . Count , 1 ) ]
17821797 } ;
17831798
17841799 var spectrumRef = "" ;
17851800 int precursorScanNumber = _precursorMs1ScanNumber ;
17861801 IReaction reaction = null ;
17871802 var precursorMz = 0.0 ;
1803+ double ? isolationWidth = null ;
17881804 try
17891805 {
17901806 switch ( msLevel )
@@ -1814,6 +1830,7 @@ private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int?
18141830 }
18151831
18161832 precursorMz = reaction . PrecursorMass ;
1833+ isolationWidth = reaction . IsolationWidth ;
18171834 }
18181835 catch ( ArgumentOutOfRangeException )
18191836 {
@@ -1823,15 +1840,11 @@ private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int?
18231840 var precursor = new PrecursorType
18241841 {
18251842 selectedIonList =
1826- new SelectedIonListType { count = 1 . ToString ( ) , selectedIon = new ParamGroupType [ 1 ] } ,
1843+ new SelectedIonListType { count = "1" , selectedIon = new ParamGroupType [ 1 ] } ,
18271844 spectrumRef = spectrumRef
18281845 } ;
18291846
1830- precursor . selectedIonList . selectedIon [ 0 ] =
1831- new ParamGroupType
1832- {
1833- cvParam = new CVParamType [ 3 ]
1834- } ;
1847+ precursor . selectedIonList . selectedIon [ 0 ] = new ParamGroupType ( ) ;
18351848
18361849 // Selected ion MZ
18371850 var selectedIonMz = CalculateSelectedIonMz ( reaction , monoisotopicMz , isolationWidth ) ;
@@ -1886,6 +1899,7 @@ private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int?
18861899 {
18871900 cvParam = new CVParamType [ 3 ]
18881901 } ;
1902+
18891903 precursor . isolationWindow . cvParam [ 0 ] =
18901904 new CVParamType
18911905 {
@@ -2022,6 +2036,44 @@ private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int?
20222036
20232037 precursorList . precursor [ 0 ] = precursor ;
20242038
2039+ //the first SPS mass seems to be the same as the one from reaction or scan filter
2040+ for ( int n = 1 ; n < SPSMasses . Count ; n ++ )
2041+ {
2042+ var SPSPrecursor = new PrecursorType
2043+ {
2044+ selectedIonList =
2045+ new SelectedIonListType { count = "1" , selectedIon = new ParamGroupType [ 1 ] } ,
2046+ spectrumRef = spectrumRef
2047+ } ;
2048+
2049+ // Selected ion MZ only
2050+ SPSPrecursor . selectedIonList . selectedIon [ 0 ] =
2051+ new ParamGroupType
2052+ {
2053+ cvParam = new CVParamType [ ]
2054+ {
2055+ new CVParamType {
2056+ name = "selected ion m/z" ,
2057+ value = SPSMasses [ n ] . ToString ( ) ,
2058+ accession = "MS:1000744" ,
2059+ cvRef = "MS" ,
2060+ unitCvRef = "MS" ,
2061+ unitAccession = "MS:1000040" ,
2062+ unitName = "m/z"
2063+ }
2064+ }
2065+ } ;
2066+
2067+ //All SPS masses have the same activation (i.e. it was calculated above)
2068+ SPSPrecursor . activation =
2069+ new ParamGroupType
2070+ {
2071+ cvParam = activationCvParams . ToArray ( )
2072+ } ;
2073+
2074+ precursorList . precursor [ n ] = SPSPrecursor ;
2075+ }
2076+
20252077 return precursorList ;
20262078 }
20272079
0 commit comments