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,
                                                                                                               Common.getPeriodFromFrequency(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>
        /// Computer digital output buffer for a given channel, using a variable timebase described by timebaseSegments.
        /// Stores result in ans
        /// </summary>
        /// <param name="digitalID"></param>
        /// <param name="masterTimebaseSampleDuration"></param>
        /// <param name="ans"></param>
        /// <param name="timebaseSegments"></param>
        public void computeDigitalBuffer(int digitalID, double masterTimebaseSampleDuration, bool[] ans, TimestepTimebaseSegmentCollection timebaseSegments)
        {
            int currentSample = 0;

            for (int stepID = 0; stepID < TimeSteps.Count; stepID++)
            {
                if (TimeSteps[stepID].StepEnabled)
                {
                    TimeStep currentStep = TimeSteps[stepID];
                    bool channelValue = TimeSteps[stepID].getDigitalValue(digitalID, TimeSteps, stepID);
                    int nSegmentSamples = 0;
                    if (!timebaseSegments.ContainsKey(currentStep))
                        throw new Exception("No timebase segment for timestep " + currentStep.ToString());

                    nSegmentSamples = timebaseSegments.nSegmentSamples(currentStep);

                    for (int j = 0; j < nSegmentSamples; j++)
                    {
                        ans[j + currentSample] = channelValue;
                    }

                    currentSample += nSegmentSamples;
                }
            }

            ans[currentSample] = dwellWord().getDigitalValue(digitalID, TimeSteps, 0);

            if (this.digitalChannelUsesPulses(digitalID))
            {

                int currentMasterSample = 0;
                // now fill in any pulses which act on this channel...
                foreach (TimeStep step in enabledTimeSteps())
                {

                        int nMasterSamplesInTimestep = timebaseSegments.nMasterSamples(step);

                        if (step.DigitalData.ContainsKey(digitalID))
                        {
                            if (step.DigitalData[digitalID].usesPulse())
                            {
                                Pulse pulse = step.DigitalData[digitalID].DigitalPulse;
                                Pulse.PulseSampleTimes sampleTimes = pulse.getPulseSampleTimes(nMasterSamplesInTimestep, masterTimebaseSampleDuration);

                                int start = currentMasterSample + sampleTimes.startSample;
                                int end = currentMasterSample + sampleTimes.endSample;

                                // ok. Paint the pulse...
                                // to do this, we need to find which derived sample (ie sample in this buffer) corresponds to
                                // which master timestep.

                                int derivedStart = getDerivedSampleFromMasterSample(start, timebaseSegments);
                                int derivedEnd = getDerivedSampleFromMasterSample(end, timebaseSegments);

                                derivedStart = Math.Max(0, derivedStart);
                                derivedEnd = Math.Min(derivedEnd, ans.Length);

                                for (int i = derivedStart; i < derivedEnd; i++)
                                {
                                    ans[i] = pulse.PulseValue;
                                }
                            }
                        }
                        currentMasterSample += nMasterSamplesInTimestep;
                }
            }
        }
        public void computeAnalogBuffer(int analogChannelID, double masterTimebaseSampleDuration, double[] ans, TimestepTimebaseSegmentCollection timebaseSegments)
        {
            int currentSample = 0;
            double dwellingValue = dwellWord().getEndAnalogValue(analogChannelID, Variables, CommonWaveforms);
            AnalogGroup currentlyRunningGroup = null;
            double groupRunningTime = 0;

            for (int stepID = 0; stepID < TimeSteps.Count; stepID++)
            {
                TimeStep currentStep = TimeSteps[stepID];
                if (currentStep.StepEnabled)
                {
                    if (currentStep.AnalogGroup != null)
                    {
                        if (currentStep.AnalogGroup.channelEnabled(analogChannelID))
                        {
                            currentlyRunningGroup = currentStep.AnalogGroup;
                            groupRunningTime = 0;
                        }
                    }

                    if (currentlyRunningGroup == null)
                    {
                        int nSegmentSamples = timebaseSegments.nSegmentSamples(currentStep);

                        for (int j = 0; j < nSegmentSamples; j++)
                        {
                            ans[j + currentSample] = dwellingValue;
                        }
                        currentSample += nSegmentSamples;
                    }
                    else
                    {
                        Waveform runningWaveform = currentlyRunningGroup.ChannelDatas[analogChannelID].waveform;
                        double waveformRunningTime = groupRunningTime;
                        foreach (VariableTimebaseSegment segment in timebaseSegments[currentStep])
                        {

                            runningWaveform.getInterpolation(segment.NSegmentSamples, waveformRunningTime,
                                waveformRunningTime + segment.NSegmentSamples * segment.MasterSamplesPerSegmentSample * masterTimebaseSampleDuration,
                                ans,
                                currentSample, Variables, CommonWaveforms);
                            currentSample += segment.NSegmentSamples;
                            waveformRunningTime += segment.NSegmentSamples * segment.MasterSamplesPerSegmentSample * masterTimebaseSampleDuration;
                        }

                        groupRunningTime += currentStep.StepDuration.getBaseValue();
                        if (runningWaveform.getEffectiveWaveformDuration() <= groupRunningTime)
                        {
                            currentlyRunningGroup = null;
                        }

                        dwellingValue = runningWaveform.getValueAtTime(runningWaveform.WaveformDuration.getBaseValue(),
                            Variables, CommonWaveforms);
                    }
                }
            }

            ans[currentSample] = dwellWord().getEndAnalogValue(analogChannelID, Variables, CommonWaveforms);
        }
        public bool[] getVariableTimebaseClock(TimestepTimebaseSegmentCollection timebaseSegments)
        {
            int nSamples = timebaseSegments.nMasterSamples();

            nSamples += 1 + 2; // add 1 false sample at the beginning so that we start with a false, and 2 at the end so we can
             // trigger the dwell values;

            if (nSamples % 4 != 0)
                nSamples += (4 - nSamples % 4); // for daqMx drivers, the number of samples in a digital stream has to be a multiple of 4

            bool[] ans = new bool[nSamples];
            int currentSample = 1;  // start at sample 1, thus leaving sample 0 as false.

            for (int stepID = 0; stepID < TimeSteps.Count; stepID++)
            {
                TimeStep currentStep = TimeSteps[stepID];
                if (currentStep.StepEnabled)
                {
                    List<VariableTimebaseSegment> segments = timebaseSegments[currentStep];
                    foreach (VariableTimebaseSegment seg in segments)
                    {
                        for (int i = 0; i < seg.NSegmentSamples; i++)
                        {
                            ans[currentSample] = true;

                            // make the clock pulse a little bit longer if the clocks are sufficiently far apart
                            // for this segment. This is mainly an aesthetic detail, it makes it easier to
                            // inspect the variable timebase on a scope, and should
                            // make no functional different
                            if (seg.MasterSamplesPerSegmentSample > 2)
                            {
                                int repeats = (seg.MasterSamplesPerSegmentSample / 2);
                                for (int j = 0; j < repeats; j++)
                                {
                                    ans[currentSample + j] = true;
                                }
                            }

                            currentSample += seg.MasterSamplesPerSegmentSample;
                        }
                    }
                }
            }

            // Add one last pulse at the end to push into dwell values? I don't remember, this comment
            // it being written long after the code was.
            ans[currentSample] = true;
            return ans;
        }
 /// <summary>
 /// Gets the master timebase sample from a given derived timebase sample.
 /// </summary>
 /// <param name="derivedSample"></param>
 /// <param name="timebaseSegments"></param>
 /// <returns></returns>
 public int getMasterSampleFromDerivedSample(int derivedSample, TimestepTimebaseSegmentCollection timebaseSegments)
 {
     int currentDerivedSample = 0;
     int currentMasterSample = 0;
     if (derivedSample == 0)
         return 0;
     foreach (TimeStep step in enabledTimeSteps())
     {
         foreach (VariableTimebaseSegment segment in timebaseSegments[step])
         {
             for (int i = 0; i < segment.NSegmentSamples; i++)
             {
                 currentMasterSample += segment.MasterSamplesPerSegmentSample;
                 currentDerivedSample++;
                 if (currentDerivedSample >= derivedSample)
                     return currentMasterSample;
             }
         }
     }
     return currentMasterSample;
 }
        /// <summary>
        /// Generates the digital buffer for a digital output that will be clocked with the same clock
        /// as the one driving the variable timebase clock. This is for use on digital outputs that
        /// share a clock with the variable timebase clock output, but that we want to use
        /// for sequence data rather than as clocks.
        /// </summary>
        /// <param name="?"></param>
        /// <returns></returns>
        public bool[] getDigitalBufferClockSharedWithVariableTimebaseClock(TimestepTimebaseSegmentCollection timebaseSegments, int digitalID, double masterTimestepSize)
        {
            int nSamples = timebaseSegments.nMasterSamples();

            nSamples += 1 + 2; // 1 extra sample at the beginning for the clock's leading LOW, and 2 at the end for the extgra dwell pulse. (this matches
             // the number of samples in the variable timebase clock, see the following function

            int currentSample = 0;

            if (nSamples % 4 != 0)
                nSamples += (4 - nSamples % 4);

            bool [] ans = new bool[nSamples];

            ans[0] = dwellWord().getDigitalValue(digitalID, TimeSteps, 0);

            currentSample++;
            int stepID = 0;
            foreach (TimeStep step in TimeSteps)
            {
                if (step.StepEnabled)
                {
                    int nStepSamples = timebaseSegments.nMasterSamples(step);

                    // if the digital is true, fill this part of the buffer with trues. If not,
                    // no need to do anything as the inital value of the array is false.
                    if (step.getDigitalValue(digitalID, TimeSteps, stepID))
                    {
                        for (int j = 0; j < nStepSamples; j++)
                        {
                            ans[j + currentSample] = true;
                        }
                    }
                    currentSample += nStepSamples;
                }
                stepID++;
            }

            // fill the rest of the buffer with dwell values
            if (dwellWord().getDigitalValue(digitalID, TimeSteps, 0))
            {
                for (int j = currentSample; j < nSamples; j++)
                {
                    ans[j] = true;
                }
            }

            // now, search for digital pulses, and if there are any on this channel, paint them on
            if (digitalChannelUsesPulses(digitalID))
            {
                currentSample = 1;
                foreach (TimeStep step in enabledTimeSteps())
                {
                    int nStepSamples = timebaseSegments.nMasterSamples(step);

                    if (step.DigitalData[digitalID].usesPulse())
                    {
                        Pulse pulse = step.DigitalData[digitalID].DigitalPulse;

                        Pulse.PulseSampleTimes sampleTimes = pulse.getPulseSampleTimes(nStepSamples, masterTimestepSize);

                        int start = currentSample + sampleTimes.startSample;
                        int end = currentSample + sampleTimes.endSample;

                        start = Math.Max(0, start);
                        end = Math.Min(end, ans.Length);

                        for (int i = start; i < end; i++)
                        {
                            ans[i] = pulse.PulseValue;
                        }
                    }
                    currentSample += nStepSamples;
                }
            }

            return ans;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="timebaseType"></param>
        /// <param name="masterTimebaseSampleDuration"></param>
        /// <returns></returns>
        public TimestepTimebaseSegmentCollection generateVariableTimebaseSegments(VariableTimebaseTypes timebaseType, double masterTimebaseSampleDuration)
        {
            switch (timebaseType)
            {
                case VariableTimebaseTypes.AnalogGroupControlledVariableFrequencyClock:
                    {

                        TimestepTimebaseSegmentCollection ans = new TimestepTimebaseSegmentCollection();

                        Dictionary<TimeStep, List<DigitalImpingement>> digitalImpingements = getDigitalImpingements(masterTimebaseSampleDuration);

                        for (int stepID = 0; stepID < TimeSteps.Count; stepID++)
                        {
                            if (TimeSteps[stepID].StepEnabled)
                            {
                                TimeStep currentStep = TimeSteps[stepID];

                                VariableTimebaseSegmentCollection timestepSegments = new VariableTimebaseSegmentCollection();
                                Dictionary<AnalogGroup, double> runningGroups = getRunningGroupRemainingTime(stepID);

                                // first cull groups that have less remaining time than 2 master timbase cycles.
                                {
                                    List<AnalogGroup> groups = new List<AnalogGroup>(runningGroups.Keys);
                                    foreach (AnalogGroup ag in groups)
                                    {
                                        if (runningGroups[ag] < 2 * masterTimebaseSampleDuration)
                                        {
                                            runningGroups.Remove(ag);
                                        }
                                    }
                                }

                                if (runningGroups.Count == 0)
                                {

                                    timestepSegments.Add(new VariableTimebaseSegment(1, (int)(currentStep.StepDuration.getBaseValue() / masterTimebaseSampleDuration)));
                                }
                                else
                                {
                                    double timeIntoCurrentStep = 0;

                                    while (runningGroups.Count != 0 && timeIntoCurrentStep < currentStep.StepDuration.getBaseValue())
                                    {
                                        // determine the most sensitive running group
                                        AnalogGroup smallestResolutionGroup = null;
                                        double smallestResolution = Double.MaxValue;
                                        double durationTiebreaker = Double.MinValue;
                                        foreach (AnalogGroup ag in runningGroups.Keys)
                                        {
                                            if (ag.TimeResolution.getBaseValue() < smallestResolution)
                                            {
                                                durationTiebreaker = runningGroups[ag];
                                                smallestResolution = ag.TimeResolution.getBaseValue();
                                                smallestResolutionGroup = ag;
                                            }
                                            else if (ag.TimeResolution.getBaseValue() == smallestResolution)
                                            {
                                                if (runningGroups[ag] >= durationTiebreaker)
                                                {
                                                    durationTiebreaker = runningGroups[ag];
                                                    smallestResolution = ag.TimeResolution.getBaseValue();
                                                    smallestResolutionGroup = ag;
                                                }
                                            }
                                        }

                                        double timeToRunGroup = Math.Min(currentStep.StepDuration.getBaseValue() - timeIntoCurrentStep, runningGroups[smallestResolutionGroup]);

                                        int masterTimebaseSamplesPerSegmentSample = 0;

                                        if (smallestResolutionGroup.TimeResolution.getBaseValue() != 0)
                                        {
                                            masterTimebaseSamplesPerSegmentSample = (int)(0.5 + smallestResolutionGroup.TimeResolution.getBaseValue() / masterTimebaseSampleDuration);
                                        }

                                        if (masterTimebaseSamplesPerSegmentSample < 2)
                                        {
                                            masterTimebaseSamplesPerSegmentSample = 2;
                                        }

                                        int nSegmentSamples = (int)(timeToRunGroup / ((double)masterTimebaseSamplesPerSegmentSample * masterTimebaseSampleDuration));

                                        //         if (nSegmentSamples > 0)
                                        //         {

                                        double actualGroupRunTime = nSegmentSamples * masterTimebaseSampleDuration * masterTimebaseSamplesPerSegmentSample;
                                        if (nSegmentSamples != 0)
                                        {
                                            timestepSegments.Add(new VariableTimebaseSegment(nSegmentSamples, masterTimebaseSamplesPerSegmentSample));
                                        }
                                        else
                                        {
                                        }
                                        runningGroups.Remove(smallestResolutionGroup);
                                        List<AnalogGroup> groups = new List<AnalogGroup>(runningGroups.Keys);
                                        foreach (AnalogGroup ag in groups)
                                        {
                                            runningGroups[ag] -= actualGroupRunTime;
                                            if (runningGroups[ag] <= 2 * masterTimebaseSampleDuration)
                                                runningGroups.Remove(ag);

                                        }

                                        timeIntoCurrentStep += actualGroupRunTime;
                                        //         }
                                        //         else
                                        //         {
                                        //             timeIntoCurrentStep = currentStep.StepDuration.getBaseValue();
                                        //         }
                                    }

                                    // if the present collection of segments does not get us to the end of the timestep, add
                                    // one filler segment.
                                    if (currentStep.StepDuration.getBaseValue() - timeIntoCurrentStep >= 2 * masterTimebaseSampleDuration)
                                    {
                                        timestepSegments.Add(new VariableTimebaseSegment(1, (int)(0.5 + (currentStep.StepDuration.getBaseValue() - timeIntoCurrentStep) / masterTimebaseSampleDuration)));
                                    }
                                }

                                // ok, now, for the love of god, we have to take into account digital pulse impingements, which may
                                // require the creation of yet more segments (by splitting those segments which already exist...)

                                #region Splitting segments due to digital pulse impingements. This is messy ugly business.
                                if (digitalImpingements.ContainsKey(currentStep))
                                {
                                    List<DigitalImpingement> impigs = digitalImpingements[currentStep];
                                    foreach (DigitalImpingement impig in impigs)
                                    {
                                        int samplesTillImpig = impig.nSamplesFromTimestepStart;
                                        VariableTimebaseSegment segmentToSplit = null;
                                        foreach (VariableTimebaseSegment seg in timestepSegments)
                                        {
                                            if (samplesTillImpig <= 0) break;
                                            if (samplesTillImpig < seg.NSegmentSamples * seg.MasterSamplesPerSegmentSample)
                                            {
                                                segmentToSplit = seg;
                                                break;
                                            }
                                            samplesTillImpig -= seg.NSegmentSamples * seg.MasterSamplesPerSegmentSample;
                                        }

                                        // if necessary, now we split the current segment (segmentToSplit)... argh.
                                        if (samplesTillImpig > 0)
                                        {
                                            // we now need to split the segment
                                            // this may take as many as 4 new segments to accomplish
                                            List<VariableTimebaseSegment> newSegments = new List<VariableTimebaseSegment>();
                                            int newSegmentSamples = 0;

                                            // Lead in segment. This runs at the original segment's usual clock rate, and runs until right before temp
                                            int nLeadSegmentSamples = samplesTillImpig / segmentToSplit.MasterSamplesPerSegmentSample;
                                            if (nLeadSegmentSamples != 0)
                                            {
                                                VariableTimebaseSegment newSeg = new VariableTimebaseSegment(nLeadSegmentSamples, segmentToSplit.MasterSamplesPerSegmentSample);
                                                newSegments.Add(newSeg);
                                                newSegmentSamples += newSeg.NSegmentSamples * newSeg.MasterSamplesPerSegmentSample;
                                                samplesTillImpig -= newSeg.MasterSamplesPerSegmentSample * newSeg.NSegmentSamples;
                                            }

                                            // Now, if necessary, add small jog in and jog out segment
                                            int jogIn = samplesTillImpig;
                                            int jogOut = segmentToSplit.MasterSamplesPerSegmentSample - jogIn;

                                            // have to be carefull. We can't jog in or out by less than 2.
                                            // If both are either greater than 1 or equal to zero, no problem.
                                            // If jogIn is 1, we will make it 2 if we can (ie if jogOut is not 0 or 2).
                                            // If jogIn is 1 and jogOut is 0, we make jogOut and jogIn 0.
                                            // If jogIn is 1 and jogOut is 2, we make jogin 3 and jogOut 0.
                                            // If jogOut is 1, then we will make it 0 if we can.

                                            if (jogIn == 1)
                                            {
                                                if (jogOut != 0 && jogOut != 2)
                                                {
                                                    jogIn++;
                                                    jogOut--;
                                                }
                                                else
                                                {
                                                    if (jogOut == 0)
                                                    {
                                                        jogIn = 0;
                                                    }
                                                    if (jogOut == 2)
                                                    {
                                                        jogOut = 0;
                                                        jogIn = 3;
                                                    }
                                                }
                                            }

                                            if (jogOut == 1)
                                            {
                                                if (jogIn > 2)
                                                {
                                                    jogOut--;
                                                    jogIn++;
                                                }
                                                else
                                                {
                                                    jogOut = 0;
                                                }
                                            }

                                            if (jogIn != 0)
                                            {
                                                VariableTimebaseSegment newSeg = new VariableTimebaseSegment(1, jogIn);
                                                newSegments.Add(newSeg);
                                                newSegmentSamples += jogIn;
                                            }
                                            if (jogOut != 0)
                                            {
                                                VariableTimebaseSegment newSeg = new VariableTimebaseSegment(1, jogOut);
                                                newSegments.Add(newSeg);
                                                newSegmentSamples += jogOut;
                                            }

                                            // now add lead out.
                                            int nSamplesToReplace = segmentToSplit.MasterSamplesPerSegmentSample * segmentToSplit.NSegmentSamples - newSegmentSamples;
                                            int nEndSegments = nSamplesToReplace / segmentToSplit.MasterSamplesPerSegmentSample;
                                            if (nEndSegments * segmentToSplit.MasterSamplesPerSegmentSample != nSamplesToReplace)
                                            {
                                                throw new InvalidDataException("Confusion during splitting of variable timebase segments due to digital pulses. This should not happen.");
                                            }
                                            if (nEndSegments != 0)
                                            {
                                                VariableTimebaseSegment newSeg = new VariableTimebaseSegment(nEndSegments, segmentToSplit.MasterSamplesPerSegmentSample);
                                                newSegments.Add(newSeg);
                                            }

                                            // ok. We have constructed the replacement segments. Now lets do the swap.
                                            int originalIndex = timestepSegments.IndexOf(segmentToSplit);
                                            timestepSegments.RemoveAt(originalIndex);
                                            timestepSegments.InsertRange(originalIndex, newSegments);

                                        }
                                    }
                                }

                                #endregion

                                // clean up -- if there any segments of length <=0, remove them
                                List<VariableTimebaseSegment> segmentsToRemove = new List<VariableTimebaseSegment>();
                                foreach (VariableTimebaseSegment seg in timestepSegments)
                                {
                                    if (seg.MasterSamplesPerSegmentSample <= 0 || seg.NSegmentSamples <= 0)
                                        segmentsToRemove.Add(seg);
                                }
                                foreach (VariableTimebaseSegment seg in segmentsToRemove)
                                {
                                    timestepSegments.Remove(seg);
                                }

                                ans.Add(currentStep, timestepSegments);
                            }
                        }
                        return ans;
                    }
                default:
                    throw new Exception("Unsupported variable timebase type.");
            }
        }
        public static byte[] createByteArray(TimestepTimebaseSegmentCollection segments,
            SequenceData sequence, out int nSegments, double masterClockPeriod, bool assymetric)
        {
            List<ListItem> listItems = new List<ListItem>();

            for (int stepID = 0; stepID < sequence.TimeSteps.Count; stepID++)
            {
                if (sequence.TimeSteps[stepID].StepEnabled)
                {
                    if (sequence.TimeSteps[stepID].WaitForRetrigger)
                    {
                        listItems.Add(new ListItem(0, 0, 0)); // 0,0,0 list item is code for "wait for retrigger
                                                              // FPGA knows how to handle this
                    }

                    List<SequenceData.VariableTimebaseSegment> stepSegments = segments[sequence.TimeSteps[stepID]];
                    for (int i = 0; i < stepSegments.Count; i++)
                    {
                        ListItem item = new ListItem();
                        SequenceData.VariableTimebaseSegment currentSeg = stepSegments[i];

                        item.repeats = currentSeg.NSegmentSamples;
                        item.offCounts = currentSeg.MasterSamplesPerSegmentSample / 2;
                        item.onCounts = currentSeg.MasterSamplesPerSegmentSample - item.offCounts;

                        // in assymmetric mode (spelling?), the clock duty cycle is not held at 50%, but rather the pulses are made to be
                        // 5 master cycles long at most. This is a workaround for the weird behavior of one of our fiber links
                        // for sharing the variable timebase signal.
                        if (assymetric)
                        {
                            if (item.onCounts > 5)
                            {
                                int difference = item.onCounts - 5;
                                item.onCounts = 5;
                                item.offCounts = item.offCounts + difference;
                            }
                        }

                        if (!item.isAllZeros())
                        { // filter out any erroneously produced all-zero codes, since these have
                            // special meaning to the FPGA (they are "wait for retrigger" codes
                            listItems.Add(item);
                        }
                    }
                }
            }

            // Add one final "pulse" at the end to trigger the dwell values. I'm basing this off the
            // old variable timebase code that I found in the SequenceData program.

            // This final pulse is made to be 100 us long at least, just to be on the safe side. (unless assymetric mode is on)

            int minCounts = (int)(0.0001 / masterClockPeriod);
            if (minCounts <= 0)
                minCounts = 1;

            ListItem finishItem = new ListItem(minCounts, minCounts, 1);

            if (assymetric)
            {
                finishItem.onCounts = 5;
            }

            listItems.Add(finishItem);

            nSegments = listItems.Count;

            byte[] byteArray = new byte[listItems.Count * 16];

            // This loop goes through the list items and creates
            // the data as it is to be sent to the FPGA
            // the data is a little shuffled because
            // of the details of the byte order in
            // piping data to the fpga.
            for (int i = 0; i < listItems.Count; i++)
            {
                ListItem item = listItems[i];

                byte[] onb = splitIntToBytes(item.onCounts);
                byte[] offb = splitIntToBytes(item.offCounts);
                byte[] repb = splitIntToBytes(item.repeats);

                int offs = 16 * i;

                byteArray[offs + 2] = onb[1];
                byteArray[offs + 3] = onb[0];
                byteArray[offs + 4] = onb[3];
                byteArray[offs + 5] = onb[2];

                byteArray[offs + 8] = offb[1];
                byteArray[offs + 9] = offb[0];
                byteArray[offs + 10] = offb[3];
                byteArray[offs + 11] = offb[2];

                byteArray[offs + 12] = repb[1];
                byteArray[offs + 13] = repb[0];
                byteArray[offs + 14] = repb[3];
                byteArray[offs + 15] = repb[2];
            }

            return byteArray;
        }
        /// <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 = Common.getPeriodFromFrequency(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 = Common.getPeriodFromFrequency(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);
        }
        /// <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, Common.getPeriodFromFrequency(masterFrequency));

            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>
        /// Create byte array for use in programming FPGA
        /// </summary>
        /// <param name="segments"></param>
        /// <param name="sequence"></param>
        /// <param name="nSegments"></param>
        /// <param name="masterClockPeriod"></param>
        /// <param name="assymetric"></param>
        /// <returns></returns>
        private static byte[] createByteArray(TimestepTimebaseSegmentCollection segments,
                                              SequenceData sequence, out int nSegments, double masterClockPeriod, bool assymetric)
        {
            List <ListItem> listItems = new List <ListItem>();

            for (int stepID = 0; stepID < sequence.TimeSteps.Count; stepID++)
            {
                if (sequence.TimeSteps[stepID].StepEnabled)
                {
                    if (sequence.TimeSteps[stepID].RetriggerOptions.WaitForRetrigger)
                    {
                        uint waitTime = (uint)(sequence.TimeSteps[stepID].RetriggerOptions.RetriggerTimeout.getBaseValue() / masterClockPeriod);

                        uint retriggerFlags = 0;
                        if (sequence.TimeSteps[stepID].RetriggerOptions.RetriggerOnEdge)
                        {
                            retriggerFlags += 1;
                        }
                        if (!sequence.TimeSteps[stepID].RetriggerOptions.RetriggerOnNegativeValueOrEdge)
                        {
                            retriggerFlags += 2;
                        }

                        listItems.Add(new ListItem(waitTime, retriggerFlags, 0));
                        // counts = 0 is a special signal for WAIT_FOR_RETRIGGER mode
                        // in this mode, FPGA waits a maximum of on_counts master samples
                        // before moving on anyway.
                        // (unless on_counts = 0, in which case it never artificially retriggers)
                        // retrigger flags set if the FPGA will trigger on edge or on value
                        // and whether to trigger on positive or negative (edge or value)
                    }

                    List <SequenceData.VariableTimebaseSegment> stepSegments = segments[sequence.TimeSteps[stepID]];
                    for (int i = 0; i < stepSegments.Count; i++)
                    {
                        ListItem item = new ListItem();
                        SequenceData.VariableTimebaseSegment currentSeg = stepSegments[i];

                        item.repeats   = (uint)currentSeg.NSegmentSamples;
                        item.offCounts = (uint)(currentSeg.MasterSamplesPerSegmentSample / 2);
                        item.onCounts  = (uint)(currentSeg.MasterSamplesPerSegmentSample - item.offCounts);

                        // in assymmetric mode (spelling?), the clock duty cycle is not held at 50%, but rather the pulses are made to be
                        // 5 master cycles long at most. This is a workaround for the weird behavior of one of our fiber links
                        // for sharing the variable timebase signal.
                        if (assymetric)
                        {
                            if (item.onCounts > 5)
                            {
                                uint difference = item.onCounts - 5;
                                item.onCounts  = 5;
                                item.offCounts = item.offCounts + difference;
                            }
                        }

                        if (!item.isAllZeros())
                        { // filter out any erroneously produced all-zero codes, since these have
                            // special meaning to the FPGA (they are "wait for retrigger" codes
                            listItems.Add(item);
                        }
                    }
                }
            }



            // Add one final "pulse" at the end to trigger the dwell values. I'm basing this off the
            // old variable timebase code that I found in the SequenceData program.

            // This final pulse is made to be 100 us long at least, just to be on the safe side. (unless assymetric mode is on)

            int minCounts = (int)(0.0001 / masterClockPeriod);

            if (minCounts <= 0)
            {
                minCounts = 1;
            }

            ListItem finishItem = new ListItem((uint)minCounts, (uint)minCounts, 1);

            if (assymetric)
            {
                finishItem.onCounts = 5;
            }

            listItems.Add(finishItem);


            nSegments = listItems.Count;

            byte[] byteArray = new byte[listItems.Count * 16];


            // This loop goes through the list items and creates
            // the data as it is to be sent to the FPGA
            // the data is a little shuffled because
            // of the details of the byte order in
            // piping data to the fpga.

            // Each list item takes up 16 bytes in the output FIFO.
            for (int i = 0; i < listItems.Count; i++)
            {
                ListItem item = listItems[i];

                byte[] onb  = splitIntToBytes(item.onCounts);
                byte[] offb = splitIntToBytes(item.offCounts);
                byte[] repb = splitIntToBytes(item.repeats);

                int offs = 16 * i;

                byteArray[offs + 2] = onb[1];
                byteArray[offs + 3] = onb[0];
                byteArray[offs + 4] = onb[3];
                byteArray[offs + 5] = onb[2];

                byteArray[offs + 8]  = offb[1];
                byteArray[offs + 9]  = offb[0];
                byteArray[offs + 10] = offb[3];
                byteArray[offs + 11] = offb[2];

                byteArray[offs + 12] = repb[1];
                byteArray[offs + 13] = repb[0];
                byteArray[offs + 14] = repb[3];
                byteArray[offs + 15] = repb[2];
            }

            return(byteArray);
        }
        /// <summary>
        /// Create byte array for use in programming FPGA
        /// </summary>
        /// <param name="segments"></param>
        /// <param name="sequence"></param>
        /// <param name="nSegments"></param>
        /// <param name="masterClockPeriod"></param>
        /// <param name="assymetric"></param>
        /// <returns></returns>
        private static byte[] createByteArray(TimestepTimebaseSegmentCollection segments,
            SequenceData sequence, out int nSegments, double masterClockPeriod, bool assymetric)
        {
            List<ListItem> listItems = new List<ListItem>();

            for (int stepID = 0; stepID < sequence.TimeSteps.Count; stepID++)
            {
                if (sequence.TimeSteps[stepID].StepEnabled)
                {
                    if (sequence.TimeSteps[stepID].RetriggerOptions.WaitForRetrigger)
                    {
                        uint waitTime = (uint)(sequence.TimeSteps[stepID].RetriggerOptions.RetriggerTimeout.getBaseValue() / masterClockPeriod);

                        uint retriggerFlags = 0;
                        if (sequence.TimeSteps[stepID].RetriggerOptions.RetriggerOnEdge)
                            retriggerFlags += 1;
                        if (!sequence.TimeSteps[stepID].RetriggerOptions.RetriggerOnNegativeValueOrEdge)
                            retriggerFlags += 2;

                        listItems.Add(new ListItem(waitTime, retriggerFlags, 0));
                               // counts = 0 is a special signal for WAIT_FOR_RETRIGGER mode
                               // in this mode, FPGA waits a maximum of on_counts master samples
                               // before moving on anyway.
                               // (unless on_counts = 0, in which case it never artificially retriggers)
                                // retrigger flags set if the FPGA will trigger on edge or on value
                                // and whether to trigger on positive or negative (edge or value)
                    }

                    List<SequenceData.VariableTimebaseSegment> stepSegments = segments[sequence.TimeSteps[stepID]];
                    for (int i = 0; i < stepSegments.Count; i++)
                    {
                        ListItem item = new ListItem();
                        SequenceData.VariableTimebaseSegment currentSeg = stepSegments[i];

                        item.repeats = (uint)currentSeg.NSegmentSamples;
                        item.offCounts = (uint)(currentSeg.MasterSamplesPerSegmentSample / 2);
                        item.onCounts = (uint)(currentSeg.MasterSamplesPerSegmentSample - item.offCounts);

                        // in assymmetric mode (spelling?), the clock duty cycle is not held at 50%, but rather the pulses are made to be
                        // 5 master cycles long at most. This is a workaround for the weird behavior of one of our fiber links
                        // for sharing the variable timebase signal.
                        if (assymetric)
                        {
                            if (item.onCounts > 5)
                            {
                                uint difference = item.onCounts - 5;
                                item.onCounts = 5;
                                item.offCounts = item.offCounts + difference;
                            }
                        }

                        if (!item.isAllZeros())
                        { // filter out any erroneously produced all-zero codes, since these have
                            // special meaning to the FPGA (they are "wait for retrigger" codes
                            listItems.Add(item);
                        }
                    }
                }
            }

            // Add one final "pulse" at the end to trigger the dwell values. I'm basing this off the
            // old variable timebase code that I found in the SequenceData program.

            // This final pulse is made to be 100 us long at least, just to be on the safe side. (unless assymetric mode is on)

            int minCounts = (int)(0.0001 / masterClockPeriod);
            if (minCounts <= 0)
                minCounts = 1;

            ListItem finishItem = new ListItem((uint)minCounts, (uint)minCounts, 1);

            if (assymetric)
            {
                finishItem.onCounts = 5;
            }

            listItems.Add(finishItem);

            nSegments = listItems.Count;

            byte[] byteArray = new byte[listItems.Count * 16];

            // This loop goes through the list items and creates
            // the data as it is to be sent to the FPGA
            // the data is a little shuffled because
            // of the details of the byte order in
            // piping data to the fpga.

            // Each list item takes up 16 bytes in the output FIFO.
            for (int i = 0; i < listItems.Count; i++)
            {
                ListItem item = listItems[i];

                byte[] onb = splitIntToBytes(item.onCounts);
                byte[] offb = splitIntToBytes(item.offCounts);
                byte[] repb = splitIntToBytes(item.repeats);

                int offs = 16 * i;

                byteArray[offs + 2] = onb[1];
                byteArray[offs + 3] = onb[0];
                byteArray[offs + 4] = onb[3];
                byteArray[offs + 5] = onb[2];

                byteArray[offs + 8] = offb[1];
                byteArray[offs + 9] = offb[0];
                byteArray[offs + 10] = offb[3];
                byteArray[offs + 11] = offb[2];

                byteArray[offs + 12] = repb[1];
                byteArray[offs + 13] = repb[0];
                byteArray[offs + 14] = repb[3];
                byteArray[offs + 15] = repb[2];
            }

            return byteArray;
        }