public WatchdogTimerTask(SequenceData sequence, int masterFrequency, NationalInstruments.DAQmx.Task taskToWatch,  double watchDogThresholdTime)
        {
            this.taskToWatch = taskToWatch;
            TimestepTimebaseSegmentCollection segments = sequence.generateVariableTimebaseSegments(SequenceData.VariableTimebaseTypes.AnalogGroupControlledVariableFrequencyClock,
                                         1.0 / ((double)masterFrequency));

            int currentSequenceSampleNumber = 0;
            long currentMasterSampleNumber=0;
            watchdogTimerSegments = new List<WatchdogTimerSegment>();

            int thresholdTime = (int)(masterFrequency*watchDogThresholdTime);

            for (int i = 0; i < sequence.TimeSteps.Count; i++)
            {
                TimeStep step = sequence.TimeSteps[i];
                if (step.StepEnabled)
                {

                    bool stepUsed = false;
                    if (segments.ContainsKey(step))
                    {
                        List<SequenceData.VariableTimebaseSegment> segmentList = segments[step];
                        for (int j = 0; j < segmentList.Count; j++)
                        {
                            SequenceData.VariableTimebaseSegment currentSegment = segmentList[j];

                            if (currentSegment.MasterSamplesPerSegmentSample >= thresholdTime)
                            {
                                if (!stepUsed)
                                {
                                    WatchdogTimerSegment wseg = new WatchdogTimerSegment();
                                    wseg.timeStepName = step.StepName;
                                    wseg.timeStepID = i;
                                    wseg.sequenceSampleNumber = currentSequenceSampleNumber + 1;
                                    wseg.masterSampleNumber = currentMasterSampleNumber + thresholdTime / 2;
                                    stepUsed = true;

                                    watchdogTimerSegments.Add(wseg);
                                }
                            }

                            currentSequenceSampleNumber += currentSegment.NSegmentSamples;
                            currentMasterSampleNumber += currentSegment.NSegmentSamples * currentSegment.MasterSamplesPerSegmentSample;
                        }

                    }
                }

            }
        }
        public FpgaTimebaseTask(DeviceSettings deviceSettings, okCFrontPanel opalKellyDevice, SequenceData sequence, double masterClockPeriod, out int nSegments, bool useRfModulation, bool assymetric)
            : base()
        {
            com.opalkelly.frontpanel.okCFrontPanel.ErrorCode errorCode;

            this.opalKellyDevice = opalKellyDevice;

            this.masterClockPeriod = masterClockPeriod;

            TimestepTimebaseSegmentCollection segments = sequence.generateVariableTimebaseSegments(SequenceData.VariableTimebaseTypes.AnalogGroupControlledVariableFrequencyClock,
                                                        masterClockPeriod);

            this.max_elapsedtime_ms = (UInt32)((sequence.SequenceDuration * 1000.0) + 100);

            byte[] data = FpgaTimebaseTask.createByteArray(segments, sequence, out nSegments, masterClockPeriod, assymetric );

            // Send the device an abort trigger.
            errorCode = opalKellyDevice.ActivateTriggerIn(0x40, 1);
            if (errorCode != okCFrontPanel.ErrorCode.NoError)
            {
                throw new Exception("Unable to set abort trigger to FPGA device. Error code " + errorCode.ToString());
            }

            UInt16 wireInValue = 0;
            if (deviceSettings.StartTriggerType != DeviceSettings.TriggerType.SoftwareTrigger)
            {
                wireInValue += 1;
            }

            if (useRfModulation)
            {
                wireInValue += 2;
            }

            setWireInValue(0x00, wireInValue);

            setWireInValue(0x01, deviceSettings.RetriggerDebounceSamples);

            opalKellyDevice.UpdateWireIns();

            // pipe the byte stream to the device
            int xfered = opalKellyDevice.WriteToPipeIn(0x80, data.Length, data);
            if (xfered != data.Length)
            {
                throw new Exception("Error when piping clock data to FPGA device. Sent " + xfered + " bytes instead of " + data.Length + "bytes.");
            }
        }
        /// <summary>
        /// Creates a task for a variable timebase output. Consumes the entire port (8 bits) that the timebase is on. (ie outputs the
        /// signal on all 8 bits
        /// </summary>
        /// <param name="channelName"></param>
        /// <param name="masterFrequency"></param>
        /// <param name="sequenceData"></param>
        /// <param name="timebaseType"></param>
        /// <returns></returns>
        public static Task createDaqMxVariableTimebaseSource(string channelName, int masterFrequency, SequenceData sequenceData, 
            SequenceData.VariableTimebaseTypes timebaseType, ServerSettings serverSettings, DeviceSettings deviceSettings)
        {
            Task task = new Task("Variable timebase output task");

            TimestepTimebaseSegmentCollection timebaseSegments = sequenceData.generateVariableTimebaseSegments(timebaseType,
                1.0 / (double)masterFrequency);

            bool [] buffer = sequenceData.getVariableTimebaseClock(timebaseSegments);

            string timebaseDeviceName = HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName);

            string timebasePort = HardwareChannel.parsePortStringFromChannelString(channelName);

            task.DOChannels.CreateChannel(timebasePort, "", ChannelLineGrouping.OneChannelForAllLines);

            task.Timing.ConfigureSampleClock("", (double)masterFrequency, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, buffer.Length);

            if (serverSettings.VariableTimebaseTriggerInput != "")
            {
                task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(serverSettings.VariableTimebaseTriggerInput, DigitalEdgeStartTriggerEdge.Rising);
            }

            DigitalSingleChannelWriter writer = new DigitalSingleChannelWriter(task.Stream);

            byte[] byteBuffer = new byte[buffer.Length];
            for (int j = 0; j < buffer.Length; j++)
            {
                if (buffer[j])
                {
                    byteBuffer[j] = 255;
                }
            }

            writer.WriteMultiSamplePort(false, byteBuffer);

            return task;
        }
        /// <summary>
        /// This method is a sort of combination of createDaqMxVariableTimebaseSource and createDaqMxTask. It is intended 
        /// for use to create a digital output task that has both a variable timebase source on it, without having to discard
        /// all of the other channels on that port (and possibly on its neighboring port).
        /// 
        /// NOTE: No longer true. This function can not create the variable timebase outputs at the same time as
        /// normal outputs. If you attempt to use digital outputs on the same half of the card as your variable timebase output,
        /// this method will complain.
        /// </summary>
        /// <param name="channelName">
        /// Name of the channel that will output the variable timebase clock.
        /// </param>
        /// <param name="portsToUse">
        /// A list of integers specifying the digital ports that this task will use. The task will automatically
        /// make use of the full port that the variable timebase clock belongs to. If portsToUse is null, then 
        /// this function will automatically use both this port and its neighboring port (0 with 1, 2 with 3, etc).
        /// The rationale for this is that on some NI devices, these pairs of ports will share a sample clock and 
        /// cannot truly be used independently.
        /// </param>
        /// <param name="masterFrequency">
        /// The frequency, in hertz, of the master clock which will drive the variable timebase clock and the rest of the
        /// channels in this task.
        /// </param>
        /// <param name="sequenceData"></param>
        /// <param name="timebaseType"></param>
        /// <param name="deviceName"></param>
        /// <param name="deviceSettings"></param>
        /// <param name="sequence"></param>
        /// <param name="settings"></param>
        /// <param name="usedDigitalChannels"></param>
        /// <param name="usedAnalogChannels"></param>
        /// <param name="serverSettings"></param>
        /// <returns></returns>
        public static Task createDaqMxDigitalOutputAndVariableTimebaseSource(string channelName, List<int> portsToUse, int masterFrequency, 
            SequenceData sequenceData, SequenceData.VariableTimebaseTypes timebaseType,
            string deviceName, DeviceSettings deviceSettings, SettingsData settings, Dictionary<int, HardwareChannel> usedDigitalChannels,  ServerSettings serverSettings)
        {
            // First generate the variable timebase buffer. We will need stuff like its length for configuring the task, which is why we do this first.

            TimestepTimebaseSegmentCollection timebaseSegments = sequenceData.generateVariableTimebaseSegments(timebaseType, 1.0 / deviceSettings.SampleClockRate);
            bool[] variableTimebaseBuffer = sequenceData.getVariableTimebaseClock(timebaseSegments);

            if (deviceName.ToUpper() != HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName).ToUpper())
            {
                throw new Exception("The variable timebase device " + HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName) + " does not match device " + deviceName + ". These must match for their their task to be created together.");
            }

            int timebasePortNum;
            int timebaseLineNum;

            try
            {
                timebasePortNum = HardwareChannel.parsePortNumberFromChannelString(channelName);
                timebaseLineNum = HardwareChannel.parseLineNumberFromChannelString(channelName);
            }
            catch (Exception)
            {
                throw new Exception("Channel name " + channelName + " is not a valid digital channel name. Cannot create a variable timebase output on this channel.");
            }

            if (portsToUse == null)
            {
                portsToUse = new List<int>();
                portsToUse.Add(timebasePortNum);
                int spousePort; // this port is likely to have a shared sample clock with timebasePortNum,
                // at least in my experience so far.
                if (timebasePortNum % 2 == 0)
                    spousePort = timebasePortNum + 1;
                else
                    spousePort = timebasePortNum - 1;

                portsToUse.Add(spousePort);
            }

            if (!portsToUse.Contains(timebasePortNum))
            {
                portsToUse.Add(timebasePortNum);
            }

            bool otherChannelsUsedOnUsedPort = false;
            foreach (HardwareChannel hc in usedDigitalChannels.Values)
            {
                if (hc.DeviceName.ToUpper() == deviceName.ToUpper())
                {
                    if (portsToUse.Contains(hc.daqMxDigitalPortNumber()))
                        otherChannelsUsedOnUsedPort = true;
                }
            }

            if (otherChannelsUsedOnUsedPort)
            {
                throw new Exception("Variable timebase channel is on a port that shares a sample clock with a used output channel (on most devices, port 0 and 1 have a shared clock, and port 2 and 3 have a shared clock). This usage is not recommended, and not currently supported. Aborting buffer generation.");

                #region Deprecated code

                /*
                Task task = new Task("Variable timebase output task");

                // Create channels in the task
                foreach (int portNum in portsToUse)
                {
                    task.DOChannels.CreateChannel(deviceName + '/' + HardwareChannel.digitalPhysicalChannelName(portNum), "", ChannelLineGrouping.OneChannelForAllLines);
                }

                // Configure the task...

                task.Timing.ConfigureSampleClock("", masterFrequency, SampleClockActiveEdge.Rising, SampleQuantityMode.FiniteSamples, variableTimebaseBuffer.Length);

                if (serverSettings.VariableTimebaseTriggerInput != "")
                {
                    task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(serverSettings.VariableTimebaseTriggerInput, DigitalEdgeStartTriggerEdge.Rising);
                }

                // Figure out which ports we are going to use, and which digital ID each line on each of those ports
                // maps to.
                Dictionary<int, HardwareChannel> digitalChannelsToUse = getChannelsOnDevice(usedDigitalChannels, deviceName);
                List<int> temp = new List<int>(digitalChannelsToUse.Keys);
                foreach (int id in temp)
                {
                    HardwareChannel ch = digitalChannelsToUse[id];
                    if (!portsToUse.Contains(HardwareChannel.parsePortNumberFromChannelString(ch.ChannelName)))
                    {
                        digitalChannelsToUse.Remove(id);
                    }
                }

                // Remove all of the digital channels this buffer generation is consuming from the
                // usedDigitalChannels dictionary. Why? Because there may still be another task to
                // generate on this device, and this is a way of keeping track of which channels
                // have already had their buffers generated.
                // Since digitalChannelsToUse gets passed by reference from AtticusServerRuntime.generateBuffers(...),
                // these changes thus get communicated back to AtticusServerRuntime.
                foreach (HardwareChannel hc in digitalChannelsToUse.Values)
                {
                    foreach (int i in usedDigitalChannels.Keys)
                    {
                        if (usedDigitalChannels[i] == hc)
                        {
                            usedDigitalChannels.Remove(i);
                            break;
                        }
                    }
                }

                List<int> ids = new List<int>(digitalChannelsToUse.Keys);
                ids.Sort();
                List<HardwareChannel> hcs = new List<HardwareChannel>();
                foreach (int id in ids)
                {
                    hcs.Add(digitalChannelsToUse[id]);
                }

                Dictionary<int, int[]> port_digital_ids;
                List<int> usedPorts;

                groupDigitalChannels(ids, hcs, out port_digital_ids, out usedPorts);

                // now to generate the buffers.

                if (usedPorts.Count != 0)
                {
                    byte[,] digitalBuffer;

                    try
                    {
                        digitalBuffer = new byte[usedPorts.Count, variableTimebaseBuffer.Length];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < usedPorts.Count; i++)
                    {
                        int portNum = usedPorts[i];
                        byte digitalBitMask = 1;
                        for (int lineNum = 0; lineNum < 8; lineNum++)
                        {
                            bool[] singleChannelBuffer = null;

                            if (portNum == timebasePortNum && lineNum == timebaseLineNum)
                            { // this current line is the variable timebase...
                                singleChannelBuffer = variableTimebaseBuffer;
                            }
                            else
                            {
                                int digitalID = port_digital_ids[portNum][lineNum];
                                if (digitalID != -1)
                                {
                                    if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                                    {
                                        singleChannelBuffer = new bool[variableTimebaseBuffer.Length];
                                        if (settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue)
                                        {
                                            for (int j = 0; j < singleChannelBuffer.Length; j++)
                                            {
                                                singleChannelBuffer[j] = true;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        singleChannelBuffer = sequenceData.getDigitalBufferClockSharedWithVariableTimebaseClock(timebaseSegments, digitalID, 1.0 / deviceSettings.SampleClockRate);
                                    }
                                }
                            }

                            if (singleChannelBuffer != null)
                            {
                                for (int j = 0; j < singleChannelBuffer.Length; j++)
                                {
                                    if (singleChannelBuffer[j])
                                    {
                                        digitalBuffer[i, j] |= digitalBitMask;
                                    }
                                }
                            }
                            digitalBitMask = (byte)(digitalBitMask << 1);

                        }
                    }
                    System.GC.Collect();
                    DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                    writer.WriteMultiSamplePort(false, digitalBuffer);

                }

                return task; */

                #endregion
            }
            else
            {
                return createDaqMxVariableTimebaseSource(
                    channelName, masterFrequency, sequenceData, timebaseType, serverSettings, deviceSettings);
            }
        }
        /// <summary>
        /// This method creates analog and digital output buffers for daqMx cards. Note that the daqmx library seems to only support
        /// either analog OR digital on a given card at one time. Despite the fact that this method will create both types of buffers,
        /// it will probably throw some daqMX level exceptions if asked to create both analog and digital buffers for the same device.
        /// </summary>
        /// <param name="deviceName"></param>
        /// <param name="deviceSettings"></param>
        /// <param name="sequence"></param>
        /// <param name="settings"></param>
        /// <param name="usedDigitalChannels">digital channels which reside on this server.</param>
        /// <param name="usedAnalogChannels">analog channels which reside on this server</param>
        /// <returns></returns>
        public static Task createDaqMxTask(string deviceName, DeviceSettings deviceSettings, SequenceData sequence, 
            SettingsData settings, Dictionary<int, HardwareChannel> usedDigitalChannels, Dictionary<int, HardwareChannel> usedAnalogChannels,
            ServerSettings serverSettings, out long expectedSamplesGenerated)
        {
            expectedSamplesGenerated = 0;

            Task task = new Task(deviceName + " output task");

            List<int> analogIDs;
            List<HardwareChannel> analogs;
            Dictionary<int, int[]> port_digital_IDs;
            List<int> usedPortNumbers;

            // Parse and create channels.
            parseAndCreateChannels(deviceName,deviceSettings, usedDigitalChannels, usedAnalogChannels, task, out analogIDs, out analogs, out port_digital_IDs, out usedPortNumbers);

            if (analogIDs.Count != 0)
            {
                if (deviceSettings.UseCustomAnalogTransferSettings)
                {
                    task.AOChannels.All.DataTransferMechanism = deviceSettings.AnalogDataTransferMechanism;
                    task.AOChannels.All.DataTransferRequestCondition = deviceSettings.AnalogDataTransferCondition;
                }
            }
            if (usedPortNumbers.Count != 0)
            {
                if (deviceSettings.UseCustomDigitalTransferSettings)
                {
                    task.DOChannels.All.DataTransferMechanism = deviceSettings.DigitalDataTransferMechanism;
                    task.DOChannels.All.DataTransferRequestCondition = deviceSettings.DigitalDataTransferCondition;
                }
            }

            // ok! now create the buffers

            #region NON variable timebase buffer
            if (deviceSettings.UsingVariableTimebase == false)
            {
                // non "variable timebase" buffer creation

                double timeStepSize = 1.0 / (double)deviceSettings.SampleClockRate;
                int nBaseSamples = sequence.nSamples(timeStepSize);

                // for reasons that are utterly stupid and frustrating, the DAQmx libraries seem to prefer sample
                // buffers with lengths that are a multiple of 4. (otherwise they, on occasion, depending on the parity of the
                // number of channels, throw exceptions complaining.
                // thus we add a few filler samples at the end of the sequence which parrot back the last sample.

                int nFillerSamples = 4 - nBaseSamples % 4;
                if (nFillerSamples == 4)
                    nFillerSamples = 0;

                int nSamples = nBaseSamples + nFillerSamples;

                if (deviceSettings.MySampleClockSource == DeviceSettings.SampleClockSource.DerivedFromMaster)
                {
                    task.Timing.ConfigureSampleClock("", deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }
                else
                {
                    task.Timing.ConfigureSampleClock(deviceSettings.SampleClockExternalSource, deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }
                if (deviceSettings.MasterTimebaseSource != "" && deviceSettings.MasterTimebaseSource != null)
                {
                    task.Timing.MasterTimebaseSource = deviceSettings.MasterTimebaseSource.ToString();
                }

                // Analog first...

                if (analogIDs.Count != 0)
                {
                    double[,] analogBuffer;
                    double[] singleChannelBuffer;
                    try
                    {
                        analogBuffer = new double[analogs.Count, nSamples];
                        singleChannelBuffer = new double[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate analog buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < analogIDs.Count; i++)
                    {
                        int analogID = analogIDs[i];

                        if (settings.logicalChannelManager.Analogs[analogID].TogglingChannel)
                        {
                            DaqMxTaskGenerator.getAnalogTogglingBuffer(singleChannelBuffer);
                        }
                        else if (settings.logicalChannelManager.Analogs[analogID].overridden)
                        {
                            for (int j = 0; j < singleChannelBuffer.Length; j++)
                            {
                                singleChannelBuffer[j] = settings.logicalChannelManager.Analogs[analogID].analogOverrideValue;
                            }
                        }
                        else
                        {
                            sequence.computeAnalogBuffer(analogIDs[i], timeStepSize, singleChannelBuffer);
                        }

                        for (int j = 0; j < nBaseSamples; j++)
                        {
                            analogBuffer[i, j] = singleChannelBuffer[j];
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            analogBuffer[i, j] = analogBuffer[i, j - 1];
                        }
                    }

                    singleChannelBuffer = null;
                    System.GC.Collect();

                    AnalogMultiChannelWriter writer = new AnalogMultiChannelWriter(task.Stream);

                    writer.WriteMultiSample(false, analogBuffer);
                    // analog cards report the exact number of generated samples. for non-variable timebase this is nSamples
                    expectedSamplesGenerated = nSamples;

                }

                if (usedPortNumbers.Count != 0)
                {
                    byte[,] digitalBuffer;
                    bool[] singleChannelBuffer;

                    try
                    {
                        digitalBuffer = new byte[usedPortNumbers.Count, nSamples];
                        singleChannelBuffer = new bool[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < usedPortNumbers.Count; i++)
                    {
                        int portNum = usedPortNumbers[i];
                        byte digitalBitMask = 1;
                        for (int lineNum = 0; lineNum < 8; lineNum++)
                        {
                            int digitalID = port_digital_IDs[portNum][lineNum];
                            if (digitalID != -1)
                            {
                                if (settings.logicalChannelManager.Digitals[digitalID].TogglingChannel)
                                {
                                    getDigitalTogglingBuffer(singleChannelBuffer);
                                }
                                else if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                                {
                                    for (int j = 0; j < singleChannelBuffer.Length; j++)
                                    {
                                        singleChannelBuffer[j] = settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue;
                                    }
                                }
                                else
                                {

                                    sequence.computeDigitalBuffer(digitalID, timeStepSize, singleChannelBuffer);
                                }
                                // byte digitalBitMask = (byte)(((byte) 2)^ ((byte)lineNum));
                                for (int j = 0; j < nBaseSamples; j++)
                                {
                                    // copy the bit value into the digital buffer byte.
                                    if (singleChannelBuffer[j])
                                        digitalBuffer[i, j] |= digitalBitMask;
                                }

                            }
                            digitalBitMask = (byte)(digitalBitMask << 1);
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            digitalBuffer[i, j] = digitalBuffer[i, j - 1];
                        }
                    }
                    singleChannelBuffer = null;
                    System.GC.Collect();
                    DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                    writer.WriteMultiSamplePort(false, digitalBuffer);
                    /// Digital cards report the number of generated samples as a multiple of 4
                    expectedSamplesGenerated = nSamples;
                }
            }
            #endregion
            #region Variable timebase buffer creation
            else // variable timebase buffer creation...
            {

                double timeStepSize = 1.0 / (double)deviceSettings.SampleClockRate;

                TimestepTimebaseSegmentCollection timebaseSegments =
            sequence.generateVariableTimebaseSegments(serverSettings.VariableTimebaseType,
                                            timeStepSize);

                int nBaseSamples = timebaseSegments.nSegmentSamples();

                nBaseSamples++; // add one sample for the dwell sample at the end of the buffer

                // for reasons that are utterly stupid and frustrating, the DAQmx libraries seem to prefer sample
                // buffers with lengths that are a multiple of 4. (otherwise they, on occasion, depending on the parity of the
                // number of channels, throw exceptions complaining.
                // thus we add a few filler samples at the end of the sequence which parrot back the last sample.

                int nFillerSamples = 4 - nBaseSamples % 4;
                if (nFillerSamples == 4)
                    nFillerSamples = 0;

                int nSamples = nBaseSamples + nFillerSamples;

                if (deviceSettings.MySampleClockSource == DeviceSettings.SampleClockSource.DerivedFromMaster)
                {
                    throw new Exception("Attempt to use a uniform sample clock with a variable timebase enabled device. This will not work. To use a variable timebase for this device, you must specify an external sample clock source.");
                }
                else
                {
                    task.Timing.ConfigureSampleClock(deviceSettings.SampleClockExternalSource, deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }

                // Analog first...

                if (analogIDs.Count != 0)
                {
                    double[,] analogBuffer;
                    double[] singleChannelBuffer;
                    try
                    {
                        analogBuffer = new double[analogs.Count, nSamples];
                        singleChannelBuffer = new double[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate analog buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < analogIDs.Count; i++)
                    {
                        int analogID = analogIDs[i];

                        if (settings.logicalChannelManager.Analogs[analogID].TogglingChannel)
                        {
                            getAnalogTogglingBuffer(singleChannelBuffer);
                        }
                        else if (settings.logicalChannelManager.Analogs[analogID].overridden)
                        {
                            for (int j = 0; j < singleChannelBuffer.Length; j++)
                            {
                                singleChannelBuffer[j] = settings.logicalChannelManager.Analogs[analogID].analogOverrideValue;
                            }
                        }
                        else
                        {
                            sequence.computeAnalogBuffer(analogIDs[i], timeStepSize, singleChannelBuffer, timebaseSegments);
                        }
                        for (int j = 0; j < nBaseSamples; j++)
                        {
                            analogBuffer[i, j] = singleChannelBuffer[j];
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            analogBuffer[i, j] = analogBuffer[i, j - 1];
                        }
                    }

                    singleChannelBuffer = null;
                    System.GC.Collect();

                    AnalogMultiChannelWriter writer = new AnalogMultiChannelWriter(task.Stream);

                    writer.WriteMultiSample(false, analogBuffer);
                    // Analog cards report the exact number of samples generated. for variable timebase this is nBaseSamples
                    expectedSamplesGenerated = nBaseSamples;

                }

                if (usedPortNumbers.Count != 0)
                {
                    byte[,] digitalBuffer;
                    bool[] singleChannelBuffer;

                    try
                    {
                        digitalBuffer = new byte[usedPortNumbers.Count, nSamples];
                        singleChannelBuffer = new bool[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < usedPortNumbers.Count; i++)
                    {
                        int portNum = usedPortNumbers[i];
                        byte digitalBitMask = 1;
                        for (int lineNum = 0; lineNum < 8; lineNum++)
                        {
                            int digitalID = port_digital_IDs[portNum][lineNum];
                            if (digitalID != -1)
                            {
                                if (settings.logicalChannelManager.Digitals[digitalID].TogglingChannel)
                                {
                                    getDigitalTogglingBuffer(singleChannelBuffer);
                                }
                                else if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                                {
                                    for (int j = 0; j < singleChannelBuffer.Length; j++)
                                    {
                                        singleChannelBuffer[j] = settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue;
                                    }
                                }
                                else
                                {

                                    sequence.computeDigitalBuffer(digitalID, timeStepSize, singleChannelBuffer, timebaseSegments);
                                }
                                // byte digitalBitMask = (byte)(((byte) 2)^ ((byte)lineNum));
                                for (int j = 0; j < nBaseSamples; j++)
                                {
                                    // copy the bit value into the digital buffer byte.
                                    if (singleChannelBuffer[j])
                                        digitalBuffer[i, j] |= digitalBitMask;
                                }

                            }
                            digitalBitMask = (byte)(digitalBitMask << 1);
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            digitalBuffer[i, j] = digitalBuffer[i, j - 1];
                        }
                    }
                    singleChannelBuffer = null;
                    System.GC.Collect();
                    DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                    writer.WriteMultiSamplePort(false, digitalBuffer);
                    // digital cards report number of samples generated up to multiple of 4
                    expectedSamplesGenerated = nSamples;
                }
            }

            #endregion

            if (deviceSettings.StartTriggerType == DeviceSettings.TriggerType.TriggerIn)
            {

                task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(
                    deviceSettings.TriggerInPort,
                    DigitalEdgeStartTriggerEdge.Rising);

            }

            task.Control(TaskAction.Verify);
            task.Control(TaskAction.Commit);
            task.Control(TaskAction.Reserve);

            return task;
        }