/// <summary>
        /// Writes the imaging data specified by the given <see cref="ImageSpectrumData"/> object
        /// to a file specified by the file name.
        /// </summary>
        /// <param name="specData"><see cref="ImageSpectrumData"/> object to be written.</param>
        /// <param name="fileName">The file to which the image data will be written.</param>
        /// <returns>A <see cref="bool"/> value indicating the success of the write operation.</returns>
        private bool Write(ImageSpectrumData specData, string fileName)
        {
            if (specData == null)
            {
                throw new ArgumentNullException("specData");
            }

            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("fileName");
            }

            // setup the three stream of an Analyze file...
            var imgStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
            var hdrStream = new FileStream(fileName.Replace(".img", ".hdr"), FileMode.Create, FileAccess.Write);
            var ttmStream = new FileStream(fileName.Replace(".img", ".t2m"), FileMode.Create, FileAccess.Write);

            // and assign to writers...
            var imgWriter = new BinaryWriter(imgStream);
            var hdrWriter = new BinaryWriter(hdrStream);
            var ttmWriter = new BinaryWriter(ttmStream);

            // write the header file.
            // this is dark, obscure and intrinsic...
            // ToDo JP 08/02/12 replace the stuff below with AnalyseFileHeaderObject
            hdrWriter.BaseStream.Position = 0;
            hdrWriter.Write(384);

            hdrWriter.BaseStream.Position = 4;
            hdrWriter.Write("        16");

            hdrWriter.BaseStream.Position = 32;
            hdrWriter.Write((short)16384);

            hdrWriter.BaseStream.Position = 38;
            hdrWriter.Write((byte)114);

            hdrWriter.BaseStream.Position = 40;
            hdrWriter.Write((short)4);

            hdrWriter.BaseStream.Position = 42;
            hdrWriter.Write((short)specData.DataSets.Count); // There will be files that contain parameter greater than 16 bit

            hdrWriter.BaseStream.Position = 44;
            hdrWriter.Write((short)specData.XPoints);

            hdrWriter.BaseStream.Position = 46;
            hdrWriter.Write((short)specData.YPoints);

            hdrWriter.BaseStream.Position = 48;
            hdrWriter.Write((short)1);

            hdrWriter.BaseStream.Position = 70; // this read the data type
            hdrWriter.Write((short)16);

            hdrWriter.BaseStream.Position = 72;
            hdrWriter.Write((short)16);

            hdrWriter.BaseStream.Position = 76; // step size x
            hdrWriter.Write((float)1);

            hdrWriter.BaseStream.Position = 80;
            hdrWriter.Write(specData.Dx);

            hdrWriter.BaseStream.Position = 84;
            hdrWriter.Write(specData.Dy);

            hdrWriter.BaseStream.Position = 88;
            hdrWriter.Write((float)1);

            foreach (MetaDataItem mdi in specData.MetaData)
            {
                switch (mdi.Name)
                {
                case "X1 (mm)":
                    hdrWriter.BaseStream.Position = 253;
                    hdrWriter.Write((ushort)float.Parse(mdi.ValueString));
                    break;

                case "Y1 (mm)":
                    hdrWriter.BaseStream.Position = 255;
                    hdrWriter.Write((ushort)float.Parse(mdi.ValueString));
                    break;
                }
            }

            // and cleanup
            hdrWriter.BaseStream.Position = 383;
            hdrWriter.Write((byte)0);

            hdrWriter.Close();

            // write the image formatted MS data...
            for (int y = 0; y < specData.YPoints; y++)
            {
                for (int x = 0; x < specData.XPoints; x++)
                {
                    foreach (ImageData imgData in specData.DataSets)
                    {
                        float intensity = imgData.Data[x][y];
                        imgWriter.Write(intensity);
                    }
                }
            }

            // and cleanup
            imgWriter.Close();

            // write calibration file
            for (int i = 0; i < specData.DataSets.Count; i++)
            {
                // TODO JP - should this contain the  contents of specData.MassCal?
                // specData.MassStep is now 1 [specData.MinMass + (specData.MassStep * i)]
                ttmWriter.Write(specData.MassCal[0] + i);  //specData.MinMass
            }

            // and cleanup
            ttmWriter.Close();

            return(true);
        }
Beispiel #2
0
        /// <summary>
        /// Fills this instance data members with the information read from <paramref name="wiffFile"/>.
        /// </summary>
        /// <param name="wiffFile">A <see cref="FMANWiffFileClass"/> instance. The data source.</param>
        protected override void Initialize(FMANWiffFileClass wiffFile)
        {
            // get experiment object and the experiment parameters
            ITripleQuadMALDI experimentParams = (ITripleQuadMALDI)wiffFile.GetExperimentObject(WiffPeriod.Sample.Index, WiffPeriod.Index, Index);
            Experiment       experiment       = (Experiment)wiffFile.GetExperimentObject(WiffPeriod.Sample.Index, WiffPeriod.Index, Index);

            MassRange massRange = (MassRange)experiment.GetMassRange(0);

            minMass      = massRange.QstartMass; // anaSpec.GetStartMass();
            maxMass      = massRange.QstopMass;  // anaSpec.GetStopMass();
            massStepSize = massRange.QstepMass;  //anaSpec.StepSize;

            // get the number of MS data points
            massSpecDataPoints = (int)((maxMass - minMass) / massStepSize + 1);

            // select 1. data points
            FMANChromData chrom = new FMANChromData();

            chrom.WiffFileName = wiffFile.GetWiffFileName();
            chrom.SetToTIC(WiffPeriod.Sample.Index, WiffPeriod.Index, Index);

            // dimension the data arrray
            nrDataPoints = chrom.GetNumberOfDataPoints();
            rawData      = new float[nrDataPoints];
            timeData     = new float[nrDataPoints];

            //loop through the mass ranges
            massRangeName = "TIC " + minMass.ToString() + " - " + maxMass.ToString();

            double xValue = chrom.GetDataPointXValue(1);
            double yValue = chrom.GetDataPointYValue(1);

            massRangeMax = double.MinValue;
            massRangeMin = double.MaxValue;
            timeOffset   = xValue * 1000;
            // helper for mean and median
            double       meanSum         = 0;
            List <float> valuesForMedian = new List <float>();

            // read the data...
            for (int actDataPoint = 1; actDataPoint <= nrDataPoints; actDataPoint++)
            {
                // get the actual value...
                xValue = chrom.GetDataPointXValue(actDataPoint);
                yValue = chrom.GetDataPointYValue(actDataPoint);
                // and copy it to our local data structur
                rawData[actDataPoint - 1]  = (float)yValue;
                timeData[actDataPoint - 1] = (float)xValue * 60;

                // keep track of the extrema
                if (yValue < massRangeMin)
                {
                    massRangeMin = yValue;
                }
                if (yValue > massRangeMax)
                {
                    massRangeMax = yValue;
                }

                // mean and median calculation...
                // sum up the value to calculate the mean
                meanSum += yValue;
                // fill an extra array to calculate the median
                valuesForMedian.Add((float)yValue);
            }
            // calculate the mean
            meanValue = (float)(meanSum / nrDataPoints);
            // calculate the median
            valuesForMedian.Sort();
            medianValue = ((valuesForMedian.Count % 2) == 0) ? (float)(valuesForMedian[(valuesForMedian.Count / 2) - 1] + valuesForMedian[valuesForMedian.Count / 2]) / 2.0f : valuesForMedian[valuesForMedian.Count / 2];


#if (false) // analyze the timing values in timeData
            List <float> timeSpans = new List <float>();
            for (int i = 1; i < timeData.Length; i++)
            {
                timeSpans.Add(timeData[i] - timeData[i - 1]);
            }
            float tMean   = 0;
            float tMedian = 0;
            timeSpans.Sort();
            foreach (float ts in timeSpans)
            {
                tMean += ts;
            }
            tMean   = tMean / timeSpans.Count;
            tMedian = ((timeSpans.Count % 2) == 0) ? (float)(timeSpans[(timeSpans.Count / 2) - 1] + timeSpans[timeSpans.Count / 2]) / 2.0f : timeSpans[timeSpans.Count / 2];
#endif

            // fetch the x1, x2, y1, y2, width and height from the WiffSample instance this experiment belongs to.
            double x1     = WiffPeriod.Sample.P1.X;
            double y1     = WiffPeriod.Sample.P1.Y;
            double x2     = WiffPeriod.Sample.P2.X;
            double y2     = WiffPeriod.Sample.P2.Y;
            double width  = WiffPeriod.Sample.Width;
            double height = WiffPeriod.Sample.Height;

            //get MALDI parmas and assign variables
            string strMALDIParams = experimentParams.TripleQuadMALDIParameters;

            // x speed in mm/s
            xSpeed = float.Parse(strMALDIParams.Split(sepMALDIParams, System.StringSplitOptions.None)[6]);

            //line distance in mm
            yDist = float.Parse(strMALDIParams.Split(sepMALDIParams, System.StringSplitOptions.None)[8]) / 1000;

            //time per point in s
            xTime = (float)((chrom.GetDataPointXValue(nrDataPoints) - chrom.GetDataPointXValue(2)) * 60 / (nrDataPoints - 2));
            xDist = (float)(int)(xSpeed * xTime * 1000) / 1000;

            //number of poins in x
            xPoints = (int)((width) / xSpeed / xTime);

            // fetch the position data from the WiffSample instance this experiment belongs to.
            uint[,] posData = WiffPeriod.Sample.PositionData;
            long posDataLength = WiffPeriod.Sample.PositionDataLength;

            // try and find the first and last valid indices in the posData, where valid means a nonzero time value...
            long firstNonZeroTimeInPos = -1;
            for (long t = 0; t < posDataLength - 1; t++)
            {
                if (posData[t, 0] > 0)
                {
                    firstNonZeroTimeInPos = t;
                    // ok, we're done...
                    break;
                }
            }

            long lastNonZeroTimeInPos = -1;
            for (long t = posDataLength - 1; t >= 0; t++)
            {
                if (posData[t, 0] > 0)
                {
                    lastNonZeroTimeInPos = t;
                    // ok, we're done...
                    break;
                }
            }

            if (firstNonZeroTimeInPos < 0 || lastNonZeroTimeInPos < 0)
            {
                // haven't found a valid posData triplet. All time values are zero or less...
                // bail out
                return;
            }

            // y2 from the wiff file is not the actual y2 from the stage - replace with value from path file...
            y2 = (double)Math.Round((decimal)posData[lastNonZeroTimeInPos, 2] / 1000, 2);

            // ...and this has an effect of the ypoints
            yPoints = (int)Math.Round((decimal)((y2 - y1) / yDist)) + 1;


            double timeSpanPos = posData[lastNonZeroTimeInPos, 0] / 1000.0 - posData[firstNonZeroTimeInPos, 0] / 1000.0;

            if (yPoints % 2 == 0)
            {
                // even number of scanlines
                lineBreak = (timeSpanPos
                             - (x2 - (float)posData[firstNonZeroTimeInPos, 1] / 1000) / xSpeed
                             - (yPoints - 2) * (width) / xSpeed
                             - (x2 - (float)posData[lastNonZeroTimeInPos, 1] / 1000) / xSpeed)
                            / (yPoints - 1);
            }
            else
            {
                // odd number of scanlines
                lineBreak = (timeSpanPos
                             - (x2 - (float)posData[firstNonZeroTimeInPos, 1] / 1000) / xSpeed
                             - (yPoints - 2) * (width) / xSpeed
                             - ((float)posData[lastNonZeroTimeInPos, 1] / 1000 - x1) / xSpeed)
                            / (yPoints - 1);
            }

            lineBreak = lineBreak / xTime;

            timeOffset = (float)posData[firstNonZeroTimeInPos, 0] / 1000 - (((float)posData[firstNonZeroTimeInPos, 1] / 1000 - x1) / xSpeed);

            lineOffset = (5 - (timeData[5] - timeOffset) / xTime) + 1;

            ///////////////////////////////////////////////////////////////////////////////////////////////////////
            // TIC throughout the total massrange...

            ///////////////////////////////////////////////////////////////////////////////////////////////////////
            // format the data into a rectangular, 2 dimensional array of floats that will represent the image data
            //

            AppContext.ProgressStart("formating image data...");

            // prepare the data structure
            float[][] dataTIC;
            try
            {
                dataTIC = new float[xPoints][];
                for (int i = 0; i < dataTIC.Length; i++)
                {
                    dataTIC[i] = new float[yPoints];
                }

                // copy the data from wiff file datastream to the rectangular array. Take account of the line offset and
                // the line break timings.
                for (int y = 0; y < yPoints; y++)
                {
                    int currentPoint;
                    // currentLine is the offset to the start of the current line in the linear datastream (rawData)
                    int currentLine = (int)Math.Floor(Math.Abs(lineOffset + ((x2 - x1) / xSpeed / xTime + lineBreak) * y));
                    for (int x = 0; x < xPoints; x++)
                    {
                        if (y % 2 == 0)
                        {
                            // even y: scan direction: -->
                            currentPoint = (int)(currentLine + x);
                        }
                        else
                        {
                            // odd y:  scan direction: <--
                            currentPoint = (int)(currentLine + xPoints - 1 - x);
                        }
                        if (currentPoint < nrDataPoints)
                        {
                            dataTIC[x][y] = rawData[currentPoint];
                        }
                        else
                        {
                            dataTIC[x][y] = 0;
                        }
                    }
                    AppContext.ProgressSetValue(100.0 * y / yPoints);
                }
            }
            finally
            {
                AppContext.ProgressClear();
            }

            // create the appropriate dataset and add it to the WiffFileContent object...
            string   imgName = WiffPeriod.Sample.Name + " : " + massRangeName;
            Document doc     = WiffPeriod.Sample.WiffFileContent.Document;
            //create the meta information data structure and populate with relevant information...
            ImageMetaData metaData = new ImageMetaData();
            try
            {
                metaData.Add("Sample Name", typeof(string), WiffPeriod.Sample.Name, false);
                metaData.Add("Mass Range", typeof(string), massRangeName, false);
                string[] splitted = strMALDIParams.Split(sepMALDIParams, System.StringSplitOptions.None);
                metaData.Add("Laser Frequency (Hz)", typeof(string), splitted[1], false);
                metaData.Add("Laser Power (%)", typeof(string), splitted[2], false);
                metaData.Add("Ablation Mode", typeof(string), splitted[3], false);
                metaData.Add("Skimmer Voltage (V)", typeof(string), splitted[4], false);
                metaData.Add("Source Gas", typeof(string), splitted[5], false);
                metaData.Add("Raster Speed (mm/s)", typeof(string), splitted[6], false);
                metaData.Add("Raster Pitch", typeof(string), splitted[8], false);
            }
            catch (Exception e) { Util.ReportException(e); }

            ImageData imageTIC = new ImageData(doc, dataTIC, imgName, metaData, (float)massRangeMin, (float)massRangeMax,
                                               (float)meanValue, (float)medianValue, (float)xDist, (float)yDist, (float)minMass, (float)maxMass);


            ///////////////////////////////////////////////////////////////////////////////////////////////////////
            // Q1 spectrum scan...
            FMANSpecData spec = new FMANSpecData();
            spec.WiffFileName = wiffFile.GetWiffFileName();

            // prepare the data structure
            List <float[][]> dataList;
            List <ImageData> imageDataList;
            AppContext.ProgressStart("formating spectrum data...");
            try
            {
                dataList = new List <float[][]>();
                for (int i = 0; i < massSpecDataPoints; i++)
                {
                    float[][] data = new float[xPoints][];
                    for (int j = 0; j < xPoints; j++)
                    {
                        data[j] = new float[yPoints];
                    }
                    dataList.Add(data);
                }


                for (int y = 0; y < yPoints; y++)
                {
                    int currentPoint;
                    // currentLine is the offset to the start of the current line in the linear datastream (rawData)
                    int currentLine = (int)Math.Floor(Math.Abs(lineOffset + ((x2 - x1) / xSpeed / xTime + lineBreak) * y));
                    for (int x = 0; x < xPoints; x++)
                    {
                        if (y % 2 == 0)
                        {
                            // even y: scan direction: -->
                            currentPoint = (int)(currentLine + x);
                        }
                        else
                        {
                            // odd y:  scan direction: <--
                            currentPoint = (int)(currentLine + xPoints - 1 - x);
                        }
                        if (currentPoint >= nrDataPoints)
                        {
                            continue;
                        }

                        float currentTime = (float)chrom.GetXValueInSec(currentPoint + 1);
                        spec.SetSpectrum(WiffPeriod.Sample.Index, WiffPeriod.Index, Index, currentTime, currentTime);
                        int specDataPoints = spec.GetNumberOfDataPoints();

                        for (int k = 1; k <= specDataPoints; k++)
                        {
                            int specIndex = (int)((spec.GetDataPointXValue(k) - minMass) / massStepSize + 0.4);
                            dataList[specIndex][x][y] = (float)spec.GetDataPointYValue(k);
                        }
                    }
                    AppContext.ProgressSetValue(100.0 * y / yPoints);
                }

                // create the list of imageData objects passed to the imageSpectrumData object on it's creation later on...
                imageDataList = new List <ImageData>();
                for (int k = 0; k < massSpecDataPoints; k++)
                {
                    float[][] specData = dataList[k];
                    float     mass     = (float)(minMass + (massStepSize * k));
                    imgName = WiffPeriod.Sample.Name + " : " + mass.ToString();

                    // TODO -- rethink if one should really calculate the mean, median, min, max etc. if the images aren't used for imaging but for export...
                    float minInt = float.MaxValue;
                    float maxInt = float.MinValue;
                    float mean   = 0;
                    float median = 0;
                    // helper for mean and median
                    meanSum = 0;
                    valuesForMedian.Clear();
                    {
                        for (int x = 0; x < xPoints; x++)
                        {
                            for (int y = 0; y < yPoints; y++)
                            {
                                float value = specData[x][y];

                                // keep track of the extrema
                                if (value < minInt)
                                {
                                    minInt = value;
                                }
                                if (value > maxInt)
                                {
                                    maxInt = value;
                                }

                                // mean and median calculation...
                                // sum up the value to calculate the mean
                                meanSum += value;
                                // fill an extra array to calculate the median
                                valuesForMedian.Add(value);
                            }
                        }
                    }
                    // calculate the mean
                    mean = (float)(meanSum / (xPoints * yPoints));
                    // calculate the median
                    valuesForMedian.Sort();
                    median = ((valuesForMedian.Count % 2) == 0) ? (float)(valuesForMedian[(valuesForMedian.Count / 2) - 1] + valuesForMedian[valuesForMedian.Count / 2]) / 2.0f : valuesForMedian[valuesForMedian.Count / 2];

                    // ok, now create the imageData and add to list...
                    ImageData imageData = new ImageData(doc, specData, imgName, new ImageMetaData(), minInt, maxInt, mean, median, (float)xDist, (float)yDist, (float)0.0f, (float)mass);
                    imageDataList.Add(imageData);
                }
            }
            finally
            {
                AppContext.ProgressClear();
            }


            // now everything should be set to create the ImageSpectrumData object...
            imgName = WiffPeriod.Sample.Name + " SPECT " + minMass.ToString() + " - " + maxMass.ToString();
            ImageSpectrumData imageSpectrum = new ImageSpectrumData(doc, imgName, new ImageMetaData(), imageDataList, (float)minMass, (float)maxMass, (float)massStepSize);
            imageSpectrum.ImageTIC = imageTIC;
            WiffPeriod.Sample.WiffFileContent.Add(imageSpectrum);


#if (false)
            // TODO -- find out what to do with the 'globalMassMax' value
            float anaTime = (float)chrom.GetXValueInSec(nrDataPoints - 1);
            chrom.SetToBPC(WiffPeriod.Sample.Index, WiffPeriod.Index, Index, 0, anaTime, minMass, maxMass, 2 * massStepSize);
            double tempMass;
            chrom.GetYValueRange(out tempMass, out globalMassMax);

            chrom.SetToTIC(WiffPeriod.Sample.Index, WiffPeriod.Index, Index);
#endif

            chrom.WiffFileName = "";
            chrom             = null;
            spec.WiffFileName = "";
            spec = null;
        }
Beispiel #3
0
        /// <summary>
        /// Fills this instance data members with the information read from <paramref name="inputMsExperiment"/>
        /// </summary>
        /// <param name="inputMsExperiment">Mass Spec Experiment</param>
        /// <param name="wiffsample">Wiff Sample</param>
        protected override void Initialize(MSExperiment inputMsExperiment, WiffSample wiffsample)
        {
            Cursor.Current = Cursors.WaitCursor;
            try
            {
                // (1) Get Maldi Parameters
                string strMaldiParams = wiffsample.MaldiParametersString;

                this.msexperiment = inputMsExperiment;

                // (2) Get the number of mass transitions - For MS experiments by default we will look at the first scan ([0])
                MassRange massRange         = this.msexperiment.Details.MassRangeInfo[0];
                var       fullScanMassRange = massRange as FullScanMassRange;

                if (fullScanMassRange != null)
                {
                    this.minMass      = fullScanMassRange.StartMass;
                    this.maxMass      = fullScanMassRange.EndMass;
                    this.massStepSize = fullScanMassRange.StepSize;
                }

                this.massSpecDataPoints = (int)(((this.maxMass - this.minMass) / this.massStepSize) + 1);

                // (3) Populate masscal
                this.masscal = new float[this.massSpecDataPoints];

                for (int i = 0; i < this.massSpecDataPoints; i++)
                {
                    this.masscal[i] = (float)(this.minMass + (i * this.massStepSize));
                }

                // (4) Select 1 trace
                XYData tic = this.msexperiment.GetTotalIonChromatogram();

                // (5) Dimension the data array, the number "data points"
                this.numDataPoints = tic.NumDataPoints;

                this.rawData  = new float[this.numDataPoints];
                this.timeData = new float[this.numDataPoints];

                // (6) mass range info
                this.massRangeName = "TIC " + this.msexperiment.Details.MassRangeInfo[0].Name;

                double actualXValue = tic.GetActualXValues()[0];
                this.massRangeMax = double.MinValue;
                this.massRangeMin = double.MaxValue;
                this.timeOffset   = actualXValue * 1000;

                // helper for mean and median
                double meanSum         = 0;
                var    valuesForMedian = new List <float>();

                // (7) read the data...
                for (int actDataPoint = 0; actDataPoint < this.numDataPoints; actDataPoint++)
                {
                    // get the actual value...
                    actualXValue = tic.GetActualXValues()[actDataPoint];
                    double actualYValue = tic.GetActualYValues()[actDataPoint];

                    // and copy it to our local data structure
                    this.rawData[actDataPoint]  = (float)actualYValue;
                    this.timeData[actDataPoint] = (float)actualXValue * 60;

                    // keep track of the extrema
                    if (actualYValue < this.massRangeMin)
                    {
                        this.massRangeMin = actualYValue;
                    }

                    if (actualYValue > this.massRangeMax)
                    {
                        this.massRangeMax = actualYValue;
                    }

                    // mean and median calculation... sum up the value to calculate the mean
                    meanSum += actualYValue;

                    // fill an extra array to calculate the median
                    valuesForMedian.Add((float)actualYValue);
                }

                // (8) Calculate the mean
                this.meanValue = (float)(meanSum / this.numDataPoints);

                // (9) Calculate the median
                valuesForMedian.Sort();
                this.medianValue = ((valuesForMedian.Count % 2) == 0) ? (valuesForMedian[(valuesForMedian.Count / 2) - 1] + valuesForMedian[valuesForMedian.Count / 2]) / 2.0f : valuesForMedian[valuesForMedian.Count / 2];

                // (10) fetch the x1, x2, y1, y2, width and height from the WiffSample instance this experiment belongs to.
                this.x1    = wiffsample.P1.X;
                this.y2    = 82 - wiffsample.P1.Y;
                this.x2    = wiffsample.P2.X;
                this.width = wiffsample.Width;

                // (11) time per point in s
                this.timeinXDirection = (float)((tic.GetActualXValues()[this.numDataPoints - 1] - tic.GetActualXValues()[1]) * 60 / (this.numDataPoints - 2));

                // (12) fetch the position data from the WiffSample instance this experiment belongs to.
                uint[,] posData = wiffsample.PositionData;
                long posDataLength = wiffsample.PositionDataLength;

                // try and find the first and last valid indices in the posData, where valid means a nonzero time value...
                long firstNonZeroTimeInPos = -1;
                for (long t = 0; t < posDataLength - 1; t++)
                {
                    if (posData[t, 0] > 0)
                    {
                        firstNonZeroTimeInPos = t;

                        break;
                    }
                }

                long lastNonZeroTimeInPos = -1;
                for (long t = posDataLength - 1; t >= 0; t++)
                {
                    if (posData[t, 0] > 0)
                    {
                        lastNonZeroTimeInPos = t;

                        break;
                    }
                }

                if (firstNonZeroTimeInPos < 0 || lastNonZeroTimeInPos < 0)
                {
                    // haven't found a valid posData triplet. All time values are zero or less... bail out (Put in an Error Message here?)
                    return;
                }

                // (13) Distance in Y direction
                this.distInYDirection = 0;
                for (long t = firstNonZeroTimeInPos; t < lastNonZeroTimeInPos; t++)
                {
                    if ((posData[t, 1] > ((this.x2 + this.x1) * 500)) & Equals(this.distInYDirection, 0.0))
                    {
                        this.distInYDirection = posData[t, 2];
                    }

                    if ((posData[t, 1] < ((this.x2 + this.x1) * 500)) & (this.distInYDirection > 0))
                    {
                        this.distInYDirection = (float)Math.Round((decimal)(posData[t, 2] - this.distInYDirection) / 500, (int)(1 - Math.Log10((posData[t, 2] - this.distInYDirection) / 500))) / 2;
                        break;
                    }
                }

                // (14) calculate speed in x direction
                this.speedinXDirection = (float)Math.Round(((posData[firstNonZeroTimeInPos + 2, 1] - posData[firstNonZeroTimeInPos + 1, 1]) / (decimal)(posData[firstNonZeroTimeInPos + 2, 0] - posData[firstNonZeroTimeInPos + 1, 0]) * 2), 0) / 2;

                // (15) distInXDirection
                this.distInXDirection = (float)(int)(this.speedinXDirection * this.timeinXDirection * 1000) / 1000;

                // (16) number of points in x
                this.numPointsOnXAxis = (int)(this.width / this.speedinXDirection / this.timeinXDirection);

                // (17) number of points in y
                // y1 from the wiff file is not the actual y1 from the stage - replace with value from path file...
                this.y1 = 82 - (double)Math.Round(((decimal)posData[lastNonZeroTimeInPos, 2] / 1000), 2);

                // ...and this has an effect of the ypoints
                this.numPointsOnYAxis = (int)Math.Round((decimal)((this.y2 - this.y1) / this.distInYDirection) + 1);

                // (18) calculate the line breaks
                var timeSpanPos = (posData[lastNonZeroTimeInPos, 0] / 1000.0)
                                  - (posData[firstNonZeroTimeInPos, 0] / 1000.0);

                if (this.numPointsOnYAxis % 2 == 0)
                {
                    // even number of scanlines
                    this.lineBreak = (timeSpanPos - ((this.x2 - ((float)posData[firstNonZeroTimeInPos, 1] / 1000)) / this.speedinXDirection)
                                      - (((this.numPointsOnYAxis - 2) * this.width) / this.speedinXDirection)
                                      - ((this.x2 - ((float)posData[lastNonZeroTimeInPos, 1] / 1000)) / this.speedinXDirection)) / (this.numPointsOnYAxis - 1);
                }
                else
                {
                    // odd number of scanlines
                    this.lineBreak = (timeSpanPos - ((this.x2 - ((float)posData[firstNonZeroTimeInPos, 1] / 1000)) / this.speedinXDirection)
                                      - (((this.numPointsOnYAxis - 2) * this.width) / this.speedinXDirection)
                                      - ((((float)posData[lastNonZeroTimeInPos, 1] / 1000) - this.x1) / this.speedinXDirection)) / (this.numPointsOnYAxis - 1);
                }

                this.lineBreak = this.lineBreak / this.timeinXDirection;

                this.timeOffset = ((float)posData[firstNonZeroTimeInPos, 0] / 1000)
                                  - ((((float)posData[firstNonZeroTimeInPos, 1] / 1000) - this.x1) / this.speedinXDirection);

                this.lineOffset = (5 - ((this.timeData[5] - this.timeOffset) / this.timeinXDirection)) + 1;

                // TIC throughout the total massrange...
                // format the data into a rectangular, 2 dimensional array of floats that will represent the image data
                AppContext.ProgressStart("formatting image data...");

                // (19) copy the data from wiff file datastream to the rectangular array. Take account of the line offset and the line break timings
                float[][] dataTic;

                try
                {
                    dataTic            = new float[this.numPointsOnXAxis][];
                    this.sampleDataPos = new int[this.numPointsOnXAxis][];

                    for (int i = 0; i < dataTic.Length; i++)
                    {
                        dataTic[i]            = new float[this.numPointsOnYAxis];
                        this.sampleDataPos[i] = new int[this.numPointsOnYAxis];
                    }

                    for (int pointOnYAxis = 0; pointOnYAxis < this.numPointsOnYAxis; pointOnYAxis++)
                    {
                        // currentLine is the offset to the start of the current line in the linear datastream (rawData)
                        var currentLine = (int)Math.Floor(Math.Abs(this.lineOffset
                                                                   + (((((this.x2 - this.x1) / this.speedinXDirection) / this.timeinXDirection)
                                                                       + this.lineBreak) * pointOnYAxis)));
                        for (int pointOnXAxis = 0; pointOnXAxis < this.numPointsOnXAxis; pointOnXAxis++)
                        {
                            int currentPoint;
                            if (pointOnYAxis % 2 == 0)
                            {
                                // even y: scan direction: -->
                                currentPoint = currentLine + pointOnXAxis;
                            }
                            else
                            {
                                // odd y:  scan direction: <--
                                currentPoint = currentLine + this.numPointsOnXAxis - 1 - pointOnXAxis;
                            }

                            if (currentPoint < this.numDataPoints)
                            {
                                dataTic[pointOnXAxis][this.numPointsOnYAxis - 1 - pointOnYAxis] = this.rawData[currentPoint];

                                // Add in a new array to save these dataPos. We can use them to get the scan number
                                // Scan number is the current point
                                this.sampleDataPos[pointOnXAxis][this.numPointsOnYAxis - 1 - pointOnYAxis] = currentPoint;
                            }
                            else
                            {
                                dataTic[pointOnXAxis][this.numPointsOnYAxis - 1 - pointOnYAxis]            = 0;
                                this.sampleDataPos[pointOnXAxis][this.numPointsOnYAxis - 1 - pointOnYAxis] = 0;
                            }
                        }

                        AppContext.ProgressSetValue((100.0 * pointOnYAxis) / this.numPointsOnYAxis);
                    }
                }
                finally
                {
                    AppContext.ProgressClear();
                }

                // (20) create the appropriate dataset and add it to the WiffFileContent object...
                string   imgName = wiffsample.Name + " : " + this.massRangeName;
                Document doc     = wiffsample.WiffFileContent.Document;

                // (21) Calculate Bin Points
                // Set a default bin size of 1 - Important it is set to 1 and not zero
                this.binsize = 1;
                this.GetBinSize(wiffsample.ScanFileSize, this.numPointsOnXAxis, this.numPointsOnYAxis);

                // By default we set this to massSpecDataPoints
                this.binnedmassSpecDataPoints = this.massSpecDataPoints;

                if (this.binsize > 1)
                {
                    this.binnedmassSpecDataPoints = this.massSpecDataPoints / this.binsize;
                }

                // create the meta information data structure and populate with relevant information...
                var metaData = new ImageMetaData();
                try
                {
                    const float Epsilon = (float)1E-10;
                    metaData.Add("Sample Name", typeof(string), wiffsample.Name, false);
                    metaData.Add("Mass Range", typeof(string), this.massRangeName, false);
                    metaData.Add("Mass Step Size", typeof(string), this.massStepSize, false);
                    metaData.Add("X1 (mm)", typeof(string), (Math.Abs(this.x1 - (int)this.x1) < Epsilon) ? this.x1.ToString("0.0") : this.x1.ToString(CultureInfo.InvariantCulture), false);
                    metaData.Add("Y1 (mm)", typeof(string), (Math.Abs(this.y1 - (int)this.y1) < Epsilon) ? this.y1.ToString("0.0") : this.y1.ToString(CultureInfo.InvariantCulture), false);
                    metaData.Add("X2 (mm)", typeof(string), (Math.Abs(this.x2 - (int)this.x2) < Epsilon) ? this.x2.ToString("0.0") : this.x2.ToString(CultureInfo.InvariantCulture), false);
                    metaData.Add("Y2 (mm)", typeof(string), (Math.Abs(this.y2 - (int)this.y2) < Epsilon) ? this.y2.ToString("0.0") : this.y2.ToString(CultureInfo.InvariantCulture), false);
                    metaData.Add("Data Points in X", typeof(string), this.numPointsOnXAxis.ToString(CultureInfo.InvariantCulture), false);
                    metaData.Add("Data Points in Y", typeof(string), this.numPointsOnYAxis.ToString(CultureInfo.InvariantCulture), false);
                    metaData.Add("Point Width (mm)", typeof(string), Math.Round(this.distInXDirection, 2).ToString(CultureInfo.InvariantCulture), false);
                    metaData.Add("Point Height (mm)", typeof(string), Math.Round(this.distInYDirection, 2).ToString(CultureInfo.InvariantCulture), false);
                    metaData.Add("Bin Size", typeof(string), this.binsize.ToString(CultureInfo.InvariantCulture), false);

                    // Add Maldi Parameters
                    string[] splitstring = strMaldiParams.Split(SepMaldiParams, StringSplitOptions.None);
                    metaData.Add("Laser Frequency (Hz)", typeof(string), splitstring[1], false);
                    metaData.Add("Laser Power (%)", typeof(string), splitstring[2], false);
                    metaData.Add("Ablation Mode", typeof(string), splitstring[3], false);
                    metaData.Add("Skimmer Voltage (V)", typeof(string), splitstring[4], false);
                    metaData.Add("Source Gas", typeof(string), splitstring[5], false);
                    metaData.Add("Raster Speed (mm/s)", typeof(string), splitstring[6], false);
                    metaData.Add("Line Direction", typeof(string), splitstring[7], false);
                    metaData.Add("Rastor Pitch ", typeof(string), splitstring[8], false);
                }
                catch (Exception e)
                {
                    Util.ReportException(e);
                }

                // (22) Create the ImageData
                var imageTic = new ImageData(
                    doc,
                    dataTic,
                    imgName,
                    metaData,
                    (float)this.massRangeMin,
                    (float)this.massRangeMax,
                    this.meanValue,
                    this.medianValue,
                    (float)this.distInXDirection,
                    (float)this.distInYDirection,
                    this.masscal,
                    Core.ExperimentType.MS);

                // (23) Collate the Spectrum Data
                List <ImageData> imageDataList = null;

                try
                {
                    // (24) Create a list of array's to hold the mass spec images (in effect a 3x3 array or a list of 2x2 array)
                    this.dataList = new List <float[][]>();
                    for (int numMassSpecDataPts = 0; numMassSpecDataPts < this.binnedmassSpecDataPoints; numMassSpecDataPts++)
                    {
                        var data = new float[this.numPointsOnXAxis][];
                        for (int pointOnXAxis = 0; pointOnXAxis < this.numPointsOnXAxis; pointOnXAxis++)
                        {
                            data[pointOnXAxis] = new float[this.numPointsOnYAxis];
                        }

                        this.dataList.Add(data);
                    }

                    // (25) Populate dataList. We can populate this by Scan (PopulateListByScan()) Or MassSpec (PopulateListByMass())
                    // For now we will use by Scan as it is more effcient
                    this.PopulateListByScan();

                    // (26) Create the list of imageData objects passed to the imageSpectrumData object on it's creation later on...
                    imageDataList = new List <ImageData>();

                    for (int massSpecDataPt = 0; massSpecDataPt < this.binnedmassSpecDataPoints; massSpecDataPt++)
                    {
                        float[][] specData = this.dataList[massSpecDataPt];

                        imgName = wiffsample.Name + " : " + this.massRangeName;

                        // TODO -- rethink if one should really calculate the mean, median, min, max etc. if the images aren't used for imaging but for export...
                        float minInt = float.MaxValue;
                        float maxInt = float.MinValue;

                        // helper for mean and median
                        meanSum = 0;
                        valuesForMedian.Clear();
                        {
                            for (int x = 0; x < this.numPointsOnXAxis; x++)
                            {
                                for (int y = 0; y < this.numPointsOnYAxis; y++)
                                {
                                    float value = specData[x][y];

                                    // keep track of the extrema
                                    if (value < minInt)
                                    {
                                        minInt = value;
                                    }

                                    if (value > maxInt)
                                    {
                                        maxInt = value;
                                    }

                                    // mean and median calculation...
                                    // sum up the value to calculate the mean
                                    meanSum += value;

                                    // fill an extra array to calculate the median
                                    valuesForMedian.Add(value);
                                }
                            }
                        }

                        // calculate the mean
                        var mean = (float)(meanSum / (this.numPointsOnXAxis * this.numPointsOnYAxis));

                        // calculate the median
                        valuesForMedian.Sort();
                        float median = ((valuesForMedian.Count % 2) == 0)
                                           ? (valuesForMedian[(valuesForMedian.Count / 2) - 1]
                                              + valuesForMedian[valuesForMedian.Count / 2]) / 2.0f
                                           : valuesForMedian[valuesForMedian.Count / 2];

                        // ok, now create the imageData and add to list...
                        var imageData = new ImageData(
                            doc,
                            specData,
                            imgName,
                            metaData,
                            minInt,
                            maxInt,
                            mean,
                            median,
                            (float)this.distInXDirection,
                            (float)this.distInYDirection,
                            this.masscal,
                            Core.ExperimentType.MS);
                        imageDataList.Add(imageData);
                    }
                }
                catch (Exception e)
                {
                    Util.ReportException(e);
                }

                // (27) Now everything should be set to create the ImageSpectrumData object...
                imgName = wiffsample.Name + " SPECT " + this.massRangeMin.ToString(CultureInfo.InvariantCulture) + " - " + this.massRangeMax.ToString(CultureInfo.InvariantCulture);

                var imageSpectrum = new ImageSpectrumData(
                    doc,
                    imgName,
                    metaData,
                    this.masscal,
                    imageDataList,
                    Core.ExperimentType.MS)
                {
                    ImageTic = imageTic
                };

                wiffsample.WiffFileContent.Add(imageSpectrum);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }