//FIXME: we really shouldn't be needing the freqcomp mode in here internal SmartScopeHeader(byte[] data) { int headerSize = SmartScope.AcquisitionRegisters.Length + SmartScope.DumpRegisters.Length + (int)Math.Ceiling(SmartScope.AcquisitionStrobes.Length / 8.0); raw = new byte[headerSize]; if (data[0] != 'L' || data[1] != 'N') throw new Exception("Invalid magic number, can't parse header"); int headerOffset = data[2]; Array.Copy(data, headerOffset, raw, 0, headerSize); BytesPerBurst = data[3]; NumberOfPayloadBursts = data[4] + (data[5] << 8); PackageOffset = (short)(data[6] + (data[7] << 8)); ViewportLength = (int)(BytesPerBurst / Channels) << GetRegister(REG.VIEW_BURSTS); AcquisitionDepth = (uint)(2048 << GetRegister(REG.ACQUISITION_DEPTH)); Samples = NumberOfPayloadBursts * BytesPerBurst / Channels; Acquiring = Utils.IsBitSet(data[10], 0); OverviewBuffer = Utils.IsBitSet(data[10], 1); LastAcquisition = Utils.IsBitSet(data[10], 2); Rolling = Utils.IsBitSet(data[10], 3); TimedOut = Utils.IsBitSet(data[10], 4); AwaitingTrigger = Utils.IsBitSet(data[10], 5); Armed = Utils.IsBitSet(data[10], 6); FullAcquisitionDump = Utils.IsBitSet(data[10], 7); AnalogTrigger = new AnalogTriggerValue() { channel = AnalogChannel.List.First(x => x.Value == ((GetRegister(REG.TRIGGER_MODE) >> 2) & 0x03)), direction = (TriggerDirection)((GetRegister(REG.TRIGGER_MODE) >> 4) & 0x03), }; ChannelSacrificedForLogicAnalyser = GetStrobe(STR.LA_CHANNEL) ? AnalogChannel.ChB : AnalogChannel.ChA; LogicAnalyserEnabled = GetStrobe(STR.LA_ENABLE); AcquisitionId = data[11]; SamplePeriod = SmartScope.BASE_SAMPLE_PERIOD * Math.Pow(2, GetRegister(REG.INPUT_DECIMATION)); ViewportSamplePeriod = SmartScope.BASE_SAMPLE_PERIOD * Math.Pow(2, GetRegister(REG.INPUT_DECIMATION) + GetRegister(REG.VIEW_DECIMATION)); ViewportOffset = SamplePeriod * ( GetRegister(REG.VIEW_OFFSET_B0) + (GetRegister(REG.VIEW_OFFSET_B1) << 8) + (GetRegister(REG.VIEW_OFFSET_B2) << 16) ); int viewportExcessiveSamples = GetRegister(REG.VIEW_EXCESS_B0) + (GetRegister(REG.VIEW_EXCESS_B1) << 8); ViewportExcess = viewportExcessiveSamples * SamplePeriod; Int64 holdoffSamples = GetRegister(REG.TRIGGERHOLDOFF_B0) + (GetRegister(REG.TRIGGERHOLDOFF_B1) << 8) + (GetRegister(REG.TRIGGERHOLDOFF_B2) << 16) + (GetRegister(REG.TRIGGERHOLDOFF_B3) << 24) - SmartScope.TriggerDelay(TriggerMode, GetRegister(REG.TRIGGER_WIDTH), GetRegister(REG.INPUT_DECIMATION)); TriggerHoldoff = holdoffSamples * (SmartScope.BASE_SAMPLE_PERIOD * Math.Pow(2, GetRegister(REG.INPUT_DECIMATION))); }
private static bool DoTriggerAnalog(float [] wave, AnalogTriggerValue trigger, int holdoff, float threshold, uint width, uint outputWaveLength, out int triggerIndex) { //Hold off: // - if positive, start looking for trigger at that index, so we are sure to have that many samples before the trigger // - if negative, start looking at index 0 triggerIndex = 0; float invertor = (trigger.direction == TriggerDirection.RISING) ? 1f : -1f; uint halfWidth = width / 2; uint preconditionCounter = 0; uint postconditionCounter = 0; for (int i = Math.Max(0, holdoff); i < wave.Length - width - outputWaveLength; i++) { bool preconditionMet = preconditionCounter == halfWidth; if (preconditionMet) { if (invertor * wave[i] >= invertor * trigger.level + threshold) { postconditionCounter++; } } else { if (invertor * wave[i] < invertor * trigger.level) { preconditionCounter++; } } if (preconditionMet && postconditionCounter == halfWidth) { int triggerIndexTmp = (int)(i + width / 2); if (triggerIndexTmp - holdoff + outputWaveLength <= wave.Length) { triggerIndex = triggerIndexTmp; return(true); } } } return(false); }
//FIXME: we really shouldn't be needing the freqcomp mode in here internal SmartScopeHeader(byte[] data) { int headerSize = SmartScope.AcquisitionRegisters.Length + SmartScope.DumpRegisters.Length + (int)Math.Ceiling(SmartScope.AcquisitionStrobes.Length / 8.0); raw = new byte[headerSize]; if (data[0] != 'L' || data[1] != 'N') { throw new Exception("Invalid magic number, can't parse header"); } int headerOffset = data[2]; Array.Copy(data, headerOffset, raw, 0, headerSize); BytesPerBurst = data[3]; NumberOfPayloadBursts = data[4] + (data[5] << 8); PackageOffset = (short)(data[6] + (data[7] << 8)); ViewportLength = (int)(BytesPerBurst / Channels) << GetRegister(REG.VIEW_BURSTS); AcquisitionDepth = (uint)(2048 << GetRegister(REG.ACQUISITION_DEPTH)); Samples = NumberOfPayloadBursts * BytesPerBurst / Channels; Acquiring = Utils.IsBitSet(data[10], 0); OverviewBuffer = Utils.IsBitSet(data[10], 1); LastAcquisition = Utils.IsBitSet(data[10], 2); Rolling = Utils.IsBitSet(data[10], 3); TimedOut = Utils.IsBitSet(data[10], 4); AwaitingTrigger = Utils.IsBitSet(data[10], 5); Armed = Utils.IsBitSet(data[10], 6); FullAcquisitionDump = Utils.IsBitSet(data[10], 7); AnalogTrigger = new AnalogTriggerValue() { channel = AnalogChannel.List.First(x => x.Value == ((GetRegister(REG.TRIGGER_MODE) >> 2) & 0x03)), direction = (TriggerDirection)((GetRegister(REG.TRIGGER_MODE) >> 4) & 0x03), }; ChannelSacrificedForLogicAnalyser = GetStrobe(STR.LA_CHANNEL) ? AnalogChannel.ChB : AnalogChannel.ChA; LogicAnalyserEnabled = GetStrobe(STR.LA_ENABLE); AcquisitionId = data[11]; SamplePeriod = SmartScope.BASE_SAMPLE_PERIOD * Math.Pow(2, GetRegister(REG.INPUT_DECIMATION)); ViewportSamplePeriod = SmartScope.BASE_SAMPLE_PERIOD * Math.Pow(2, GetRegister(REG.INPUT_DECIMATION) + GetRegister(REG.VIEW_DECIMATION)); ViewportOffset = SamplePeriod * ( GetRegister(REG.VIEW_OFFSET_B0) + (GetRegister(REG.VIEW_OFFSET_B1) << 8) + (GetRegister(REG.VIEW_OFFSET_B2) << 16) ); int viewportExcessiveSamples = GetRegister(REG.VIEW_EXCESS_B0) + (GetRegister(REG.VIEW_EXCESS_B1) << 8); ViewportExcess = viewportExcessiveSamples * SamplePeriod; Int64 holdoffSamples = GetRegister(REG.TRIGGERHOLDOFF_B0) + (GetRegister(REG.TRIGGERHOLDOFF_B1) << 8) + (GetRegister(REG.TRIGGERHOLDOFF_B2) << 16) + (GetRegister(REG.TRIGGERHOLDOFF_B3) << 24) - SmartScope.TriggerDelay(TriggerMode, GetRegister(REG.TRIGGER_WIDTH), GetRegister(REG.INPUT_DECIMATION)); TriggerHoldoff = holdoffSamples * (SmartScope.BASE_SAMPLE_PERIOD * Math.Pow(2, GetRegister(REG.INPUT_DECIMATION))); }
public static Dictionary<AnalogChannel, AnalogWaveProperties> MeasureAutoArrangeSettings(IScope scope, AnalogChannel aciveChannel, Action<float> progressReport) { float progress = 0f; progressReport(progress); //stop scope streaming scope.DataSourceScope.Stop(); //Prepare scope for test if (scope is SmartScope) (scope as SmartScope).SetDisableVoltageConversion(false); //set to timerange wide enough to capture 50Hz, but slightly off so smallest chance of aliasing const float initialTimeRange = 0.0277f; scope.AcquisitionMode = AcquisitionMode.AUTO; scope.AcquisitionLength = initialTimeRange; scope.SetViewPort(0, scope.AcquisitionLength); //s.AcquisitionDepth = 4096; scope.TriggerHoldOff = 0; scope.SendOverviewBuffer = false; AnalogTriggerValue atv = new AnalogTriggerValue(); atv.channel = AnalogChannel.ChA; atv.direction = TriggerDirection.RISING; atv.level = 5000; scope.TriggerAnalog = atv; foreach (AnalogChannel ch in AnalogChannel.List) scope.SetCoupling(ch, Coupling.DC); scope.CommitSettings(); progress += .1f; progressReport(progress); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // VERTICAL == VOLTAGE //set to largest input range float maxRange = 1.2f / 1f * 36f; foreach (AnalogChannel ch in AnalogChannel.List) scope.SetVerticalRange(ch, -maxRange / 2f, maxRange / 2f); float[] minValues = new float[] { float.MaxValue, float.MaxValue }; float[] maxValues = new float[] { float.MinValue, float.MinValue }; //measure min and max voltage over 3 full ranges for (int i = -1; i < 2; i++) { progress += .1f; progressReport(progress); foreach (AnalogChannel ch in AnalogChannel.List) scope.SetYOffset(ch, (float)i * maxRange); scope.CommitSettings(); System.Threading.Thread.Sleep(100); scope.ForceTrigger(); //fetch data DataPackageScope p = FetchLastFrame(scope); p = FetchLastFrame(scope); //needs this second fetch as well to get voltage conversion on ChanB right?!? if (p == null) { Logger.Error("Didn't receive data from scope, aborting"); return null; } //check if min or max need to be updated (only in case this measurement was not saturated) float[] dataA = (float[])p.GetData(DataSourceType.Viewport, AnalogChannel.ChA).array; float[] dataB = (float[])p.GetData(DataSourceType.Viewport, AnalogChannel.ChB).array; float minA = dataA.Min(); float maxA = dataA.Max(); float minB = dataB.Min(); float maxB = dataB.Max(); if (minA != p.SaturationLowValue[AnalogChannel.ChA] && minA != p.SaturationHighValue[AnalogChannel.ChA] && minValues[0] > minA) minValues[0] = minA; if (minB != p.SaturationLowValue[AnalogChannel.ChB] && minB != p.SaturationHighValue[AnalogChannel.ChB] && minValues[1] > minB) minValues[1] = minB; if (maxA != p.SaturationLowValue[AnalogChannel.ChA] && maxA != p.SaturationHighValue[AnalogChannel.ChA] && maxValues[0] < maxA) maxValues[0] = maxA; if (maxB != p.SaturationLowValue[AnalogChannel.ChB] && maxB != p.SaturationHighValue[AnalogChannel.ChB] && maxValues[1] < maxB) maxValues[1] = maxB; } //calc ideal voltage range and offset float sizer = 3; //meaning 3 waves would fill entire view float[] coarseAmplitudes = new float[2]; coarseAmplitudes[0] = maxValues[0] - minValues[0]; coarseAmplitudes[1] = maxValues[1] - minValues[1]; float[] desiredOffsets = new float[2]; desiredOffsets[0] = (maxValues[0] + minValues[0]) / 2f; desiredOffsets[1] = (maxValues[1] + minValues[1]) / 2f; float[] desiredRanges = new float[2]; desiredRanges[0] = coarseAmplitudes[0] * sizer; desiredRanges[1] = coarseAmplitudes[1] * sizer; //intervene in case the offset is out of range for this range if (desiredRanges[0] < Math.Abs(desiredOffsets[0])) desiredRanges[0] = Math.Abs(desiredOffsets[0]); if (desiredRanges[1] < Math.Abs(desiredOffsets[1])) desiredRanges[1] = Math.Abs(desiredOffsets[1]); //set fine voltage range and offset scope.SetVerticalRange(AnalogChannel.ChA, -desiredRanges[0] / 2f, desiredRanges[0] / 2f); scope.SetYOffset(AnalogChannel.ChA, -desiredOffsets[0]); scope.SetVerticalRange(AnalogChannel.ChB, -desiredRanges[1] / 2f, desiredRanges[1] / 2f); scope.SetYOffset(AnalogChannel.ChB, -desiredOffsets[1]); scope.CommitSettings(); //now get data in order to find accurate lowHigh levels (as in coarse mode this was not accurate) DataPackageScope pFine = FetchLastFrame(scope); pFine = FetchLastFrame(scope); //needs this second fetch as well to get voltage conversion on ChanB right?!? Dictionary<AnalogChannel, float[]> dataFine = new Dictionary<AnalogChannel, float[]>(); dataFine.Add(AnalogChannel.ChA, (float[])pFine.GetData(DataSourceType.Viewport, AnalogChannel.ChA).array); dataFine.Add(AnalogChannel.ChB, (float[])pFine.GetData(DataSourceType.Viewport, AnalogChannel.ChB).array); Dictionary<AnalogChannel, float> minimumValues = new Dictionary<AnalogChannel, float>(); Dictionary<AnalogChannel, float> maximumValues = new Dictionary<AnalogChannel, float>(); Dictionary<AnalogChannel, float> amplitudes = new Dictionary<AnalogChannel, float>(); Dictionary<AnalogChannel, float> offsets = new Dictionary<AnalogChannel, float>(); Dictionary<AnalogChannel, bool> isFlatline = new Dictionary<AnalogChannel, bool>(); foreach (var kvp in dataFine) { minimumValues.Add(kvp.Key, kvp.Value.Min()); maximumValues.Add(kvp.Key, kvp.Value.Max()); amplitudes.Add(kvp.Key, kvp.Value.Max() - kvp.Value.Min()); offsets.Add(kvp.Key, (kvp.Value.Max() + kvp.Value.Min())/2f); isFlatline.Add(kvp.Key, amplitudes[kvp.Key] < 0.01f); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // HORIZONTAL == FREQUENCY const float minTimeRange = 500f * 0.00000001f;//500 samples over full hor span const float maxTimeRange = 1f; double frequency, frequencyError, dutyCycle, dutyCycleError; Dictionary<AnalogChannel, double> finalFrequencies = new Dictionary<AnalogChannel, double>(); finalFrequencies.Add(AnalogChannel.ChA, double.MaxValue); finalFrequencies.Add(AnalogChannel.ChB, double.MaxValue); int iterationCounter = 0; //only for performance testing float currTimeRange = minTimeRange; bool continueLooping = true; if (isFlatline.Where(x => x.Value).ToList().Count == isFlatline.Count) //no need to find frequency in case of 2 DC signals continueLooping = false; while (continueLooping) { progress += .04f; progressReport(progress); iterationCounter++; //only for performance testing scope.AcquisitionLength = currTimeRange; scope.SetViewPort(0, scope.AcquisitionLength); scope.CommitSettings(); DataPackageScope pHor = FetchLastFrame(scope); pHor = FetchLastFrame(scope); Dictionary<AnalogChannel, float[]> timeData = new Dictionary<AnalogChannel, float[]>(); timeData.Add(AnalogChannel.ChA, (float[])pHor.GetData(DataSourceType.Viewport, AnalogChannel.ChA).array); timeData.Add(AnalogChannel.ChB, (float[])pHor.GetData(DataSourceType.Viewport, AnalogChannel.ChB).array); foreach (var kvp in timeData) { //make sure entire amplitude is in view float currMinVal = kvp.Value.Min(); float currMaxVal = kvp.Value.Max(); float lowMarginValue = minimumValues[kvp.Key] + amplitudes[kvp.Key] * 0.1f; float highMarginValue = maximumValues[kvp.Key] - amplitudes[kvp.Key] * 0.1f; if (currMinVal > lowMarginValue) break; if (currMaxVal < highMarginValue) break; ComputeFrequencyDutyCycle(pHor.GetData(DataSourceType.Viewport, kvp.Key), out frequency, out frequencyError, out dutyCycle, out dutyCycleError); if (!double.IsNaN(frequency) && (finalFrequencies[kvp.Key] == double.MaxValue)) finalFrequencies[kvp.Key] = frequency; } //update and check whether we've found what we were looking for currTimeRange *= 100f; bool freqFoundForAllActiveWaves = true; foreach (var kvp in timeData) if (!isFlatline[kvp.Key] && finalFrequencies[kvp.Key] == double.MaxValue) freqFoundForAllActiveWaves = false; continueLooping = !freqFoundForAllActiveWaves; if (currTimeRange > maxTimeRange) continueLooping = false; } //in case of flatline or very low freq, initial value will not have changed foreach (AnalogChannel ch in finalFrequencies.Keys.ToList()) if (finalFrequencies[ch] == double.MaxValue) finalFrequencies[ch] = 0; Dictionary<AnalogChannel, AnalogWaveProperties> waveProperties = new Dictionary<AnalogChannel, AnalogWaveProperties>(); foreach (var kvp in isFlatline) waveProperties.Add(kvp.Key, new AnalogWaveProperties(minimumValues[kvp.Key], maximumValues[kvp.Key], amplitudes[kvp.Key], offsets[kvp.Key], isFlatline[kvp.Key], finalFrequencies[kvp.Key])); return waveProperties; }