diff --git a/source/TS.NET.Engine/EngineManager.cs b/source/TS.NET.Engine/EngineManager.cs index 466a75f..d94eff6 100644 --- a/source/TS.NET.Engine/EngineManager.cs +++ b/source/TS.NET.Engine/EngineManager.cs @@ -188,21 +188,25 @@ public bool TryStart(string settingsFile, string calibrationFile, string deviceS { logger?.LogInformation($"Calibration loaded from path: {calibrationFile}"); loadedCalibration = Calibration.FromJsonFile(calibrationFile); + ts.SetCalibration(loadedCalibration); } else if (ThunderscopeNonVolatileMemory.TryReadUserCalibration(ts, out var userCalibration)) { logger?.LogInformation($"Calibration loaded from user calibration memory"); loadedCalibration = userCalibration!; + ts.SetCalibration(loadedCalibration); } else if (ThunderscopeNonVolatileMemory.TryReadFactoryCalibration(ts, out var factoryCalibration)) { logger?.LogInformation($"Calibration loaded from factory calibration memory"); loadedCalibration = factoryCalibration!; + // Factory Calibration loaded by tslitex } else if (File.Exists("thunderscope-calibration.json")) { logger?.LogInformation($"Calibration loaded from thunderscope-calibration.json"); loadedCalibration = Calibration.FromJsonFile("thunderscope-calibration.json"); + ts.SetCalibration(loadedCalibration); } else { @@ -225,7 +229,7 @@ public bool TryStart(string settingsFile, string calibrationFile, string deviceS initialHardwareConfiguration.ExtSyncMode = ThunderscopeExtSyncMode.Disabled; initialHardwareConfiguration.RefClockMode = ThunderscopeRefClockMode.Disabled; initialHardwareConfiguration.RefClockFrequencyHz = 10_000_000; - ts.Configure(initialHardwareConfiguration, loadedCalibration, thunderscopeSettings.HardwareRevision); + ts.Configure(initialHardwareConfiguration); ts.StartMonitoring(); thunderscope = ts; break; diff --git a/source/TS.NET.Sequences/Singletons/Instruments.cs b/source/TS.NET.Sequences/Singletons/Instruments.cs index 0709250..55fa968 100644 --- a/source/TS.NET.Sequences/Singletons/Instruments.cs +++ b/source/TS.NET.Sequences/Singletons/Instruments.cs @@ -89,7 +89,7 @@ public string Initialise() hardwareConfig.Frontend[3] = ThunderscopeChannelFrontend.Default(); thunderScope = new Driver.Libtslitex.Thunderscope(loggerFactory, 1024 * 1024); thunderScope.Open(0); - thunderScope.Configure(hardwareConfig, Calibration.Default(), ""); + thunderScope.Configure(hardwareConfig); // Need to set manual control on all channels so that SetRate doesn't run normal logic var manualControl = new ThunderscopeChannelFrontendManualControl() { Coupling = ThunderscopeCoupling.DC, Termination = ThunderscopeTermination.OneMegaohm, Attenuator = 0, DAC = 2000, DPOT = 50, PgaLadderAttenuation = 0, PgaFilter = ThunderscopeBandwidth.Bw20M, PgaHighGain = 0 }; thunderScope.SetChannelManualControl(0, manualControl); @@ -144,10 +144,10 @@ public void SetThunderscopeChannel(int[] enabledChannelIndices) { if (!enabledChannelIndices.SequenceEqual(cachedChannelIndices)) { - thunderScope?.SetChannelEnable(0, enabledChannelIndices.Contains(0), updateFrontends: false); - thunderScope?.SetChannelEnable(1, enabledChannelIndices.Contains(1), updateFrontends: false); - thunderScope?.SetChannelEnable(2, enabledChannelIndices.Contains(2), updateFrontends: false); - thunderScope?.SetChannelEnable(3, enabledChannelIndices.Contains(3), updateFrontends: false); + thunderScope?.SetChannelEnable(0, enabledChannelIndices.Contains(0)); + thunderScope?.SetChannelEnable(1, enabledChannelIndices.Contains(1)); + thunderScope?.SetChannelEnable(2, enabledChannelIndices.Contains(2)); + thunderScope?.SetChannelEnable(3, enabledChannelIndices.Contains(3)); cachedSampleRateHz = 0; cachedChannelIndices = enabledChannelIndices; } diff --git a/source/TS.NET/Drivers/Libtslitex/Interop/Interop.cs b/source/TS.NET/Drivers/Libtslitex/Interop/Interop.cs index cbbb816..ea425ed 100644 --- a/source/TS.NET/Drivers/Libtslitex/Interop/Interop.cs +++ b/source/TS.NET/Drivers/Libtslitex/Interop/Interop.cs @@ -87,35 +87,99 @@ public struct tsScopeState_t public byte acq_power_good; } + [StructLayout(LayoutKind.Sequential)] + public struct tsAfePathCalibration_s + { + public double bufferInputVpp; + public double trimOffsetDacZeroC; + public double trimOffsetDacZeroM; + public double trimOffsetDacScale; + public uint trimDPot; + } + [StructLayout(LayoutKind.Sequential)] public struct tsChannelCalibration_t { - public int buffer_uV; - public int bias_uV; - public int attenuatorGain1M_mdB; - public int attenuatorGain50_mdB; - public int bufferGain_mdB; - public int trimRheostat_range; - public int preampLowGainError_mdB; - public int preampHighGainError_mdB; + public double attenuatorScale; + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 11)] + public tsAfePathCalibration_s[] highPgaPathCal; + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 11)] + public tsAfePathCalibration_s[] lowPgaPathCal; + + public tsChannelCalibration_t() { + highPgaPathCal = new tsAfePathCalibration_s[11]; + lowPgaPathCal = new tsAfePathCalibration_s[11]; } + } - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public int[] preampAttenuatorGain_mdB; - public int preampOutputGainError_mdB; - public int preampLowOffset_uV; - public int preampHighOffset_uV; - public int preampInputBias_uA; + [StructLayout(LayoutKind.Sequential)] + public struct tsAdcLoad_t + { + public uint rate; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public double[] scale; - public tsChannelCalibration_t() { preampAttenuatorGain_mdB = new int[11]; } + public tsAdcLoad_t() { scale = new double[4];} } - + [StructLayout(LayoutKind.Sequential)] - public struct tsAdcCalibration_t + public struct tsAdcLoadCal_t + { + public uint channels; + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 8)] + public tsAdcLoad_t[] conf; + + public tsAdcLoadCal_t() { + conf = new tsAdcLoad_t[8]; + for (var cal = 0; cal < 8; cal++) + { + conf[cal] = new tsAdcLoad_t(); + } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct tsAdcGain_t { + public uint rate; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public byte[] branchFineGain; + public byte[] gain; - public tsAdcCalibration_t() { branchFineGain = new byte[8]; } + public tsAdcGain_t() { gain = new byte[8];} + } + + [StructLayout(LayoutKind.Sequential)] + public struct tsAdcGainCal_t + { + public uint channels; + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 8)] + public tsAdcGain_t[] conf; + + public tsAdcGainCal_t() { + conf = new tsAdcGain_t[8]; + for (var cal = 0; cal < 8; cal++) + { + conf[cal] = new tsAdcGain_t(); + } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct tsAdcCalibration_t + { + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 11)] + public tsAdcLoadCal_t[] loadCal; + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 11)] + public tsAdcGainCal_t[] branchFineGain; + + public tsAdcCalibration_t() { + loadCal = new tsAdcLoadCal_t[11]; + branchFineGain = new tsAdcGainCal_t[11]; + for (var cal = 0; cal < 11; cal++) + { + loadCal[cal] = new tsAdcLoadCal_t(); + branchFineGain[cal] = new tsAdcGainCal_t(); + } + } } public enum tsSampleFormat_t @@ -171,7 +235,10 @@ public enum tsSampleFormat_t public static unsafe partial int UserDataWrite(nint ts, byte* buffer, uint offset, uint writeLen); [DllImport(library, EntryPoint = "thunderscopeChanCalibrationSet")] // Use runtime marshalling for now. Custom marshalling later. - public static extern int SetCalibration(nint ts, uint channel, in tsChannelCalibration_t cal); + public static extern int SetAFECalibration(nint ts, uint channel, in tsChannelCalibration_t cal); + + [DllImport(library, EntryPoint = "thunderscopeAdcCalibrationGet")] // Use runtime marshalling for now. Custom marshalling later. + public static extern int GetAFECalibration(nint ts, uint channel, out tsChannelCalibration_t cal); [DllImport(library, EntryPoint = "thunderscopeAdcCalibrationSet")] // Use runtime marshalling for now. Custom marshalling later. public static extern int SetAdcCalibration(nint ts, in tsAdcCalibration_t cal); @@ -194,6 +261,9 @@ public struct tsChannelCtrl_t [LibraryImport(library, EntryPoint = "thunderscopeCalibrationManualCtrl")] public static unsafe partial int SetChannelManualControl(nint ts, uint channel, in tsChannelCtrl_t ctrl); + + [LibraryImport(library, EntryPoint = "thunderscopeCalibrationManualAdcFineGain")] + public static unsafe partial int SetAdcManualFineGain(nint ts, in byte[] ctrl); public enum tsEventType_t { diff --git a/source/TS.NET/Drivers/Libtslitex/Thunderscope.cs b/source/TS.NET/Drivers/Libtslitex/Thunderscope.cs index 8bf9b0e..97b2ac2 100644 --- a/source/TS.NET/Drivers/Libtslitex/Thunderscope.cs +++ b/source/TS.NET/Drivers/Libtslitex/Thunderscope.cs @@ -2,7 +2,17 @@ namespace TS.NET.Driver.Libtslitex; -public record ThunderscopeLiteXDevice(uint DeviceID, uint HardwareRev, uint GatewareRev, uint LitexRev, string DevicePath, string Identity, string Serial, string BuildConfiguration, string BuildDate, string ManufacturingSignature); +public record ThunderscopeLiteXDevice + (uint DeviceID, + uint HardwareRev, + uint GatewareRev, + uint LitexRev, + string DevicePath, + string Identity, + string Serial, + string BuildConfiguration, + string BuildDate, + string ManufacturingSignature); public class Thunderscope : IThunderscope { @@ -16,6 +26,7 @@ public class Thunderscope : IThunderscope private bool[] channelManualOverride; private Calibration calibration; private ThunderscopeChannelFrontend[] channelFrontend; + private ThunderscopeLiteXDevice tsInfo; uint cachedSampleRateHz = 1_000_000_000; AdcResolution cachedSampleResolution = AdcResolution.EightBit; @@ -50,12 +61,19 @@ public Thunderscope(ILoggerFactory loggerFactory, int dmaBufferSize) channelManualOverride = new bool[4]; calibration = new Calibration(); channelFrontend = new ThunderscopeChannelFrontend[4]; + tsInfo = new ThunderscopeLiteXDevice(0, 0, 0, 0, "", "", "", "", "", ""); } public void Open(uint devIndex) { if (open) Close(); + + if (0 == Interop.ListDevices(devIndex, out var devInfo)) + { + tsInfo = new ThunderscopeLiteXDevice(devInfo.deviceID, devInfo.hw_id, devInfo.gw_id, devInfo.litex, devInfo.devicePath, devInfo.identity, devInfo.serialNumber, devInfo.buildConfig, devInfo.buildDate, devInfo.mfgSignature); + beta = (devInfo.hw_id & (1 << 9)) == 0; // ID Valid in bit [9] is not set for Beta hardware builds + } tsHandle = Interop.Open(devIndex, false); @@ -64,47 +82,36 @@ public void Open(uint devIndex) open = true; } - public void Configure(ThunderscopeHardwareConfig initialHardwareConfiguration, Calibration calibration, string hardwareRevision) + public void SetCalibration(Calibration calibration) { CheckOpen(); - if (hardwareRevision.Equals("Rev4.1", StringComparison.InvariantCultureIgnoreCase)) - beta = true; - - //SetAdcCalibration(initialHardwareConfiguration.AdcCalibration); - //for (int chan = 0; chan < 4; chan++) - //{ - // SetChannelCalibration(chan, initialHardwareConfiguration.Calibration[chan]); - //} + SetAdcCalibration(calibration.Adc); + for (int chan = 0; chan < 4; chan++) + { + SetChannelCalibration(chan, calibration.Frontend[chan]); + } this.calibration = calibration; + } + public void Configure(ThunderscopeHardwareConfig initialHardwareConfiguration) + { + CheckOpen(); + GetStatus(); for (int chan = 0; chan < 4; chan++) { channelFrontend[chan] = initialHardwareConfiguration.Frontend[chan]; var chanEnabled = ((initialHardwareConfiguration.Acquisition.EnabledChannels >> chan) & 0x01) > 0; - SetChannelEnable(chan, chanEnabled, updateFrontends: false); + SetChannelEnable(chan, chanEnabled); } - SetSampleMode(initialHardwareConfiguration.Acquisition.SampleRateHz, initialHardwareConfiguration.Acquisition.Resolution, updateFrontends: false); + SetSampleMode(initialHardwareConfiguration.Acquisition.SampleRateHz, initialHardwareConfiguration.Acquisition.Resolution); UpdateFrontends(); SetExtSyncMode(initialHardwareConfiguration.ExtSyncMode); SetRefClockMode(initialHardwareConfiguration.RefClockMode); SetRefClockFrequency(initialHardwareConfiguration.RefClockFrequencyHz); - var temperature = GetStatus().FpgaTemp; - var frontend = calibration.Frontend[0]; - foreach (var path in frontend.Path) - { - Frontend.CalculateAllowableOffsetRangeV(logger, frontend, path, attenuator: false, temperature, out var minOffsetV, out var maxOffsetV); - logger.LogDebug($"Input range: {path.BufferInputVpp:F3}Vpp, input offset: {minOffsetV:F3}V to {maxOffsetV:F3}V [{path.PgaPreampGain}, {path.PgaLadder}, attenuator off]"); - } - - foreach (var path in frontend.Path) - { - Frontend.CalculateAllowableOffsetRangeV(logger, frontend, path, attenuator: true, temperature, out var minOffsetV, out var maxOffsetV); - logger.LogDebug($"Input range: {path.BufferInputVpp * frontend.AttenuatorScale:F3}Vpp, input offset: {minOffsetV:F3}V to {maxOffsetV:F3}V [{path.PgaPreampGain}, {path.PgaLadder}, attenuator on]"); - } } public void Close() @@ -243,42 +250,29 @@ public ThunderscopeChannelFrontend GetChannelFrontend(int channelIndex) { CheckOpen(); - //var channel = new ThunderscopeChannelFrontend(); - //var tsChannel = new Interop.tsChannelParam_t(); - - //var retVal = Interop.GetChannelConfig(tsHandle, (uint)channelIndex, out tsChannel); - //if (retVal < 0) - // throw new ThunderscopeException($"Failed to get channel {channelIndex} config ({GetLibraryReturnString(retVal)})"); - - //channel.RequestedVoltFullScale = requestedChannelVoltScale[channelIndex]; - //channel.ActualVoltFullScale = (double)tsChannel.volt_scale_uV / 1000000.0; - //channel.RequestedVoltOffset = requestedChannelVoltOffset[channelIndex]; - //channel.ActualVoltOffset = (double)tsChannel.volt_offset_uV / 1000000.0; - //channel.Coupling = (tsChannel.coupling == 1) ? ThunderscopeCoupling.AC : ThunderscopeCoupling.DC; - //channel.Termination = (tsChannel.term == 1) ? ThunderscopeTermination.FiftyOhm : ThunderscopeTermination.OneMegaohm; - //channel.Bandwidth = (tsChannel.bandwidth == 750) ? ThunderscopeBandwidth.Bw750M : - // (tsChannel.bandwidth == 650) ? ThunderscopeBandwidth.Bw650M : - // (tsChannel.bandwidth == 350) ? ThunderscopeBandwidth.Bw350M : - // (tsChannel.bandwidth == 200) ? ThunderscopeBandwidth.Bw200M : - // (tsChannel.bandwidth == 100) ? ThunderscopeBandwidth.Bw100M : - // (tsChannel.bandwidth == 20) ? ThunderscopeBandwidth.Bw20M : - // ThunderscopeBandwidth.BwFull; - - //return channel; - - - var channel = channelFrontend[channelIndex]; - //var tsChannel = new Interop.tsChannelParam_t(); - - //var retVal = Interop.GetChannelConfig(tsHandle, (uint)channelIndex, out tsChannel); - //if (retVal < 0) - // throw new ThunderscopeException($"Failed to get channel {channelIndex} config ({GetLibraryReturnString(retVal)})"); - //channel.Termination = tsChannel.term switch - //{ - // 0 => ThunderscopeTermination.OneMegaohm, - // 1 => ThunderscopeTermination.FiftyOhm, - // _ => throw new NotImplementedException() - //}; + var channel = new ThunderscopeChannelFrontend(); + var tsChannel = new Interop.tsChannelParam_t(); + + var retVal = Interop.GetChannelConfig(tsHandle, (uint)channelIndex, out tsChannel); + if (retVal < 0) + throw new ThunderscopeException($"Failed to get channel {channelIndex} config ({GetLibraryReturnString(retVal)})"); + + channel.RequestedVoltFullScale = channelFrontend[channelIndex].RequestedVoltFullScale; + channel.ActualVoltFullScale = (double)tsChannel.volt_scale_uV / 1000000.0; + channel.RequestedVoltOffset = channelFrontend[channelIndex].RequestedVoltOffset; + channel.ActualVoltOffset = (double)tsChannel.volt_offset_uV / 1000000.0; + channel.Coupling = (tsChannel.coupling == 1) ? ThunderscopeCoupling.AC : ThunderscopeCoupling.DC; + channel.RequestedTermination = channelFrontend[channelIndex].RequestedTermination; + channel.ActualTermination = (tsChannel.term == 1) ? ThunderscopeTermination.FiftyOhm : ThunderscopeTermination.OneMegaohm; + channel.Bandwidth = (tsChannel.bandwidth == 750) ? ThunderscopeBandwidth.Bw750M : + (tsChannel.bandwidth == 650) ? ThunderscopeBandwidth.Bw650M : + (tsChannel.bandwidth == 350) ? ThunderscopeBandwidth.Bw350M : + (tsChannel.bandwidth == 200) ? ThunderscopeBandwidth.Bw200M : + (tsChannel.bandwidth == 100) ? ThunderscopeBandwidth.Bw100M : + (tsChannel.bandwidth == 20) ? ThunderscopeBandwidth.Bw20M : + ThunderscopeBandwidth.BwFull; + + return channel; } @@ -327,15 +321,42 @@ private ThunderscopeAcquisitionConfig GetAcquisitionConfig() return acquisitionConfig; } - //public ThunderscopeChannelCalibration GetChannelCalibration(int channelIndex) - //{ - // CheckOpen(); + public FrontendCalibration GetChannelCalibration(int channelIndex) + { + CheckOpen(); + + var afeCalibration = FrontendCalibration.Default(channelIndex); + var tsCal = new Interop.tsChannelCalibration_t(); - // if (channelIndex >= 4 || channelIndex < 0) - // throw new ThunderscopeException($"Invalid Channel Index {channelIndex}"); + if (channelIndex >= 4 || channelIndex < 0) + throw new ThunderscopeException($"Invalid Channel Index {channelIndex}"); + + var retVal = Interop.GetAFECalibration(tsHandle, (uint)channelIndex, out tsCal); + + if (retVal < 0) + throw new ThunderscopeException($"Failed to get libtslitex AFE{channelIndex} Calibration ({GetLibraryReturnString(retVal)})"); - // return channelCalibration[channelIndex]; - //} + for (int i = 0; i < 11; i++) + { + afeCalibration.Path[i].PgaPreampGain = PgaPreampGain.High; + afeCalibration.Path[i].PgaLadder = (byte)i; + afeCalibration.Path[i].TrimDPot = (byte)tsCal.highPgaPathCal[i].trimDPot; + afeCalibration.Path[i].TrimDacScale = tsCal.highPgaPathCal[i].trimOffsetDacScale; + afeCalibration.Path[i].TrimDacZeroC = tsCal.highPgaPathCal[i].trimOffsetDacZeroC; + afeCalibration.Path[i].TrimDacZeroM = tsCal.highPgaPathCal[i].trimOffsetDacZeroM; + afeCalibration.Path[i].BufferInputVpp = tsCal.highPgaPathCal[i].bufferInputVpp; + + afeCalibration.Path[11 + i].PgaPreampGain = PgaPreampGain.Low; + afeCalibration.Path[11 + i].PgaLadder = (byte)i; + afeCalibration.Path[11 + i].TrimDPot = (byte)tsCal.lowPgaPathCal[i].trimDPot; + afeCalibration.Path[11 + i].TrimDacScale = tsCal.lowPgaPathCal[i].trimOffsetDacScale; + afeCalibration.Path[11 + i].TrimDacZeroC = tsCal.lowPgaPathCal[i].trimOffsetDacZeroC; + afeCalibration.Path[11 + i].TrimDacZeroM = tsCal.lowPgaPathCal[i].trimOffsetDacZeroM; + afeCalibration.Path[11 + i].BufferInputVpp = tsCal.lowPgaPathCal[i].bufferInputVpp; + } + afeCalibration.AttenuatorScale = tsCal.attenuatorScale; + return afeCalibration; + } public ThunderscopeLiteXStatus GetStatus() { @@ -365,7 +386,7 @@ public ThunderscopeLiteXStatus GetStatus() public void SetRate(ulong sampleRateHz) { - SetSampleMode(sampleRateHz, cachedSampleResolution, updateFrontends: true); + SetSampleMode(sampleRateHz, cachedSampleResolution); } public void SetResolution(AdcResolution resolution) @@ -374,108 +395,203 @@ public void SetResolution(AdcResolution resolution) if (resolution == AdcResolution.TwelveBit && sampleRateHz > 660_000_000) sampleRateHz = 660_000_000; - SetSampleMode(sampleRateHz, resolution, updateFrontends: true); + SetSampleMode(sampleRateHz, resolution); } public void SetChannelFrontend(int channelIndex, ThunderscopeChannelFrontend channel) { CheckOpen(); - //var tsChannel = new Interop.tsChannelParam_t(); - //var retVal = Interop.GetChannelConfig(tsHandle, (uint)channelIndex, out tsChannel); + var tsChannel = new Interop.tsChannelParam_t(); + var retVal = Interop.GetChannelConfig(tsHandle, (uint)channelIndex, out tsChannel); - //if (retVal < 0) - // throw new ThunderscopeException($"Failed to get channel {channelIndex} config ({GetLibraryReturnString(retVal)})"); + if (retVal < 0) + throw new ThunderscopeException($"Failed to get channel {channelIndex} config ({GetLibraryReturnString(retVal)})"); - //tsChannel.volt_scale_uV = (uint)(channel.RequestedVoltFullScale * 1000000); - //tsChannel.volt_offset_uV = (int)(channel.RequestedVoltOffset * 1000000); - //tsChannel.coupling = (channel.Coupling == ThunderscopeCoupling.DC) ? (byte)0 : (byte)1; - //tsChannel.term = (channel.Termination == ThunderscopeTermination.OneMegaohm) ? (byte)0 : (byte)1; - //tsChannel.bandwidth = channel.Bandwidth switch - //{ - // ThunderscopeBandwidth.BwFull => 900, - // ThunderscopeBandwidth.Bw750M => 750, - // ThunderscopeBandwidth.Bw650M => 650, - // ThunderscopeBandwidth.Bw350M => 350, - // ThunderscopeBandwidth.Bw200M => 200, - // ThunderscopeBandwidth.Bw100M => 100, - // ThunderscopeBandwidth.Bw20M => 20, - // _ => throw new NotImplementedException() - //}; + tsChannel.volt_scale_uV = (uint)(channel.RequestedVoltFullScale * 1000000); + tsChannel.volt_offset_uV = (int)(channel.RequestedVoltOffset * 1000000); + tsChannel.coupling = (channel.Coupling == ThunderscopeCoupling.DC) ? (byte)0 : (byte)1; + tsChannel.term = (channel.RequestedTermination == ThunderscopeTermination.OneMegaohm) ? (byte)0 : (byte)1; + tsChannel.bandwidth = channel.Bandwidth switch + { + ThunderscopeBandwidth.BwFull => 900, + ThunderscopeBandwidth.Bw750M => 750, + ThunderscopeBandwidth.Bw650M => 650, + ThunderscopeBandwidth.Bw350M => 350, + ThunderscopeBandwidth.Bw200M => 200, + ThunderscopeBandwidth.Bw100M => 100, + ThunderscopeBandwidth.Bw20M => 20, + _ => throw new NotImplementedException() + }; - //retVal = Interop.SetChannelConfig(tsHandle, (uint)channelIndex, in tsChannel); + logger.LogInformation($"Configure channel {channelIndex}: scale {tsChannel.volt_scale_uV}uVpp, offset {tsChannel.volt_offset_uV}uV, term {tsChannel.term}"); - //if (retVal < 0) - // throw new ThunderscopeException($"Failed to set channel {channelIndex} config ({GetLibraryReturnString(retVal)})"); + retVal = Interop.SetChannelConfig(tsHandle, (uint)channelIndex, in tsChannel); - //requestedChannelVoltScale[channelIndex] = channel.RequestedVoltFullScale; - //requestedChannelVoltOffset[channelIndex] = channel.RequestedVoltOffset; + if (retVal < 0) + throw new ThunderscopeException($"Failed to set channel {channelIndex} config ({GetLibraryReturnString(retVal)})"); - var loadScales = Frontend.GetAllLoadScales(calibration, channelsEnabled, cachedSampleRateHz); - SetChannelFrontend(channelIndex, channel, loadScales[channelIndex]); + + retVal = Interop.GetChannelConfig(tsHandle, (uint)channelIndex, out tsChannel); + + if (retVal < 0) + throw new ThunderscopeException($"Failed to get channel {channelIndex} config ({GetLibraryReturnString(retVal)})"); + + channelFrontend[channelIndex].ActualTermination = (tsChannel.term == 0) ? ThunderscopeTermination.OneMegaohm : ThunderscopeTermination.FiftyOhm; + channelFrontend[channelIndex].ActualVoltFullScale = tsChannel.volt_scale_uV * 1000000.0; + channelFrontend[channelIndex].ActualVoltOffset = tsChannel.volt_offset_uV * 1000000.0; + channelFrontend[channelIndex].RequestedVoltFullScale = channel.RequestedVoltFullScale; + channelFrontend[channelIndex].RequestedVoltOffset = channel.RequestedVoltOffset; + channelFrontend[channelIndex].RequestedTermination = channel.RequestedTermination; + channelFrontend[channelIndex].Bandwidth = channel.Bandwidth; + channelFrontend[channelIndex].Coupling = channel.Coupling; + + channelManualOverride[channelIndex] = false; // SetChannelManualControl sets to true, so immediately set to false } public void SetAdcBranchGainManualControl(byte[] branchGain) { CheckOpen(); - var tsCal = new Interop.tsAdcCalibration_t(); - tsCal.branchFineGain[0] = branchGain[0]; - tsCal.branchFineGain[1] = branchGain[1]; - tsCal.branchFineGain[2] = branchGain[2]; - tsCal.branchFineGain[3] = branchGain[3]; - tsCal.branchFineGain[4] = branchGain[4]; - tsCal.branchFineGain[5] = branchGain[5]; - tsCal.branchFineGain[6] = branchGain[6]; - tsCal.branchFineGain[7] = branchGain[7]; - - Interop.SetAdcCalibration(tsHandle, in tsCal); - } - - //public void SetChannelCalibration(int channelIndex, ThunderscopeChannelCalibration channelCalibration) - //{ - // CheckOpen(); - - // if (channelIndex >= 4 || channelIndex < 0) - // throw new ThunderscopeException($"Invalid Channel Index {channelIndex}"); - - // var tsCal = new Interop.tsChannelCalibration_t - // { - // //buffer_uV = (int)(channelCalibration.BufferOffset * 1000000), - // //bias_uV = (int)(channelCalibration.BiasVoltage * 1000000), - // //attenuatorGain1M_mdB = (int)(channelCalibration.AttenuatorGain1MOhm * 1000), - // //attenuatorGain50_mdB = (int)(channelCalibration.AttenuatorGain50Ohm * 1000), - // //bufferGain_mdB = (int)(channelCalibration.BufferGain * 1000), - // //trimRheostat_range = (int)channelCalibration.TrimResistorOhms, - // //preampLowGainError_mdB = (int)(channelCalibration.PgaLowGainError * 1000), - // //preampHighGainError_mdB = (int)(channelCalibration.PgaHighGainError * 1000), - // //preampLowOffset_uV = (int)(channelCalibration.PgaLowOffsetVoltage * 1000000), - // //preampHighOffset_uV = (int)(channelCalibration.PgaHighOffsetVoltage * 1000000), - // //preampOutputGainError_mdB = (int)(channelCalibration.PgaOutputGainError * 1000), - // //preampInputBias_uA = (int)channelCalibration.PgaInputBiasCurrent, - // }; - // //tsCal.preampAttenuatorGain_mdB[0] = (int)channelCalibration.PgaAttenuatorGain0; - // //tsCal.preampAttenuatorGain_mdB[1] = (int)channelCalibration.PgaAttenuatorGain1; - // //tsCal.preampAttenuatorGain_mdB[2] = (int)channelCalibration.PgaAttenuatorGain2; - // //tsCal.preampAttenuatorGain_mdB[3] = (int)channelCalibration.PgaAttenuatorGain3; - // //tsCal.preampAttenuatorGain_mdB[4] = (int)channelCalibration.PgaAttenuatorGain4; - // //tsCal.preampAttenuatorGain_mdB[5] = (int)channelCalibration.PgaAttenuatorGain5; - // //tsCal.preampAttenuatorGain_mdB[6] = (int)channelCalibration.PgaAttenuatorGain6; - // //tsCal.preampAttenuatorGain_mdB[7] = (int)channelCalibration.PgaAttenuatorGain7; - // //tsCal.preampAttenuatorGain_mdB[8] = (int)channelCalibration.PgaAttenuatorGain8; - // //tsCal.preampAttenuatorGain_mdB[9] = (int)channelCalibration.PgaAttenuatorGain9; - // //tsCal.preampAttenuatorGain_mdB[10] = (int)channelCalibration.PgaAttenuatorGain10; - - // this.channelCalibration[channelIndex] = channelCalibration; - // Interop.SetCalibration(tsHandle, (uint)channelIndex, in tsCal); - //} - - public void SetChannelEnable(int channelIndex, bool enabled) => SetChannelEnable(channelIndex, enabled, updateFrontends: true); + var tsCal = new byte[8]; + tsCal[0] = branchGain[0]; + tsCal[1] = branchGain[1]; + tsCal[2] = branchGain[2]; + tsCal[3] = branchGain[3]; + tsCal[4] = branchGain[4]; + tsCal[5] = branchGain[5]; + tsCal[6] = branchGain[6]; + tsCal[7] = branchGain[7]; + + Interop.SetAdcManualFineGain(tsHandle, in tsCal); + } + + public void SetChannelCalibration(int channelIndex, FrontendCalibration channelCalibration) + { + CheckOpen(); + + if (channelIndex >= 4 || channelIndex < 0) + throw new ThunderscopeException($"Invalid Channel Index {channelIndex}"); + + var tsCal = new Interop.tsChannelCalibration_t(); + + if (channelIndex >= 4 || channelIndex < 0) + throw new ThunderscopeException($"Invalid Channel Index {channelIndex}"); + + + + foreach (var path in channelCalibration.Path) + { + if (path.PgaPreampGain == PgaPreampGain.High) + { + tsCal.highPgaPathCal[path.PgaLadder].trimDPot = path.TrimDPot; + tsCal.highPgaPathCal[path.PgaLadder].trimOffsetDacZeroC = path.TrimDacZeroC; + tsCal.highPgaPathCal[path.PgaLadder].trimOffsetDacZeroM = path.TrimDacZeroM; + tsCal.highPgaPathCal[path.PgaLadder].trimOffsetDacScale = path.TrimDacScale; + tsCal.highPgaPathCal[path.PgaLadder].bufferInputVpp = path.BufferInputVpp; + } + else + { + tsCal.lowPgaPathCal[path.PgaLadder].trimDPot = path.TrimDPot; + tsCal.lowPgaPathCal[path.PgaLadder].trimOffsetDacZeroC = path.TrimDacZeroC; + tsCal.lowPgaPathCal[path.PgaLadder].trimOffsetDacZeroM = path.TrimDacZeroM; + tsCal.lowPgaPathCal[path.PgaLadder].trimOffsetDacScale = path.TrimDacScale; + tsCal.lowPgaPathCal[path.PgaLadder].bufferInputVpp = path.BufferInputVpp; + } + } + + tsCal.attenuatorScale = channelCalibration.AttenuatorScale; + + var retVal = Interop.SetAFECalibration(tsHandle, (uint)channelIndex, in tsCal); + if (retVal < 0) + throw new ThunderscopeException($"Failed to set libtslitex AFE{channelIndex} Calibration ({GetLibraryReturnString(retVal)})"); + + } + + public void SetAdcCalibration(AdcCalibration adcCalibration) + { + CheckOpen(); + + var tsAdcCal = new Interop.tsAdcCalibration_t(); + for (int i = 0; i < adcCalibration.LoadScale.Length; i++) + { + tsAdcCal.loadCal[i].channels = (uint)adcCalibration.LoadScale[i].Channel.Aggregate(0, (current, chan) => 1 << chan); + for (int j = 0; j < adcCalibration.LoadScale[i].RateScale.Length; j++) + { + tsAdcCal.loadCal[i].conf[j].rate = adcCalibration.LoadScale[i].RateScale[j].Rate; + for (int k = 0; k < adcCalibration.LoadScale[i].RateScale[j].Scale.Length; k++) + tsAdcCal.loadCal[i].conf[j].scale[k] = adcCalibration.LoadScale[i].RateScale[j].Scale[k]; + } + } + for (int i = 0; i < adcCalibration.BranchGain.Length; i++) + { + tsAdcCal.branchFineGain[i].channels = (uint)adcCalibration.BranchGain[i].Channel.Aggregate(0, (current, chan) => 1 << chan); + for (int j = 0; j < adcCalibration.BranchGain[i].RateGain.Length; j++) + { + tsAdcCal.branchFineGain[i].conf[j].rate = adcCalibration.BranchGain[i].RateGain[j].Rate; + for (int k = 0; k < adcCalibration.BranchGain[i].RateGain[j].Gain.Length; k++) + tsAdcCal.branchFineGain[i].conf[j].gain[k] = (byte)adcCalibration.BranchGain[i].RateGain[j].Gain[k]; + } + } + } + + public AdcCalibration GetAdcCalibration() + { + var adcCal = AdcCalibration.Default(); + var tsAdcCal = new Interop.tsAdcCalibration_t(); + if (0 == Interop.GetAdcCalibration(tsHandle, out tsAdcCal)) + { + // Convert Load Scale calibration + for (int load = 0; load < 11; load++) + { + var channelCount = 0; + for (int i = 0; i < 4; i++) + { + if ((tsAdcCal.loadCal[load].channels & (1 << i)) != 0) + { + adcCal.LoadScale[load].Channel[channelCount] = i; + channelCount++; + } + } + for (int rateIdx = 0; rateIdx < 8; rateIdx++) + { + adcCal.LoadScale[load].RateScale[rateIdx].Rate = tsAdcCal.loadCal[load].conf[rateIdx].rate; + for (int i = 0; i < channelCount; i++) + { + adcCal.LoadScale[load].RateScale[rateIdx].Scale[i] = tsAdcCal.loadCal[load].conf[rateIdx].scale[i]; + } + } + } + // Convert Branch Fine Gain calibration + for (int gain = 0; gain < 11; gain++) + { + var channelCount = 0; + for (int i = 0; i < 4; i++) + { + if ((tsAdcCal.branchFineGain[gain].channels & (1 << i)) != 0) + { + adcCal.BranchGain[gain].Channel[channelCount] = i; + channelCount++; + } + } + for (int rateIdx = 0; rateIdx < 8; rateIdx++) + { + adcCal.BranchGain[gain].RateGain[rateIdx].Rate = tsAdcCal.branchFineGain[gain].conf[rateIdx].rate; + for (int i = 0; i < 8; i++) + { + adcCal.BranchGain[gain].RateGain[rateIdx].Gain[i] = tsAdcCal.branchFineGain[gain].conf[rateIdx].gain[i]; + } + } + } + } + + return adcCal; + } /// /// Intended for use by testbench/calibration routines that use SetChannelManualControl /// - public void SetChannelEnable(int channelIndex, bool enabled, bool updateFrontends) + public void SetChannelEnable(int channelIndex, bool enabled) { CheckOpen(); @@ -505,10 +621,6 @@ public void SetChannelEnable(int channelIndex, bool enabled, bool updateFrontend channelsEnabled = channelsEnabled.Where(c => c != channelIndex).ToArray(); } - // There is a channel-count vs. scale relationship so update frontends - if (updateFrontends) - UpdateFrontends(); - if (restart) Start(); @@ -669,35 +781,16 @@ public int FactoryDataRead(uint tag, Span buffer) } } - private void SetChannelFrontend(int channelIndex, ThunderscopeChannelFrontend channel, double channelLoadScale) - { - CheckOpen(); - - lastFrontendUpdateTemp = GetStatus().FpgaTemp; - var frontend = calibration.Frontend[channelIndex]; - - var calculatedChannel = Frontend.CalculateFrontend(logger, channelIndex, channel, frontend, channelLoadScale, cachedSampleRateHz, lastFrontendUpdateTemp, beta, out var manualControl); - SetChannelManualControl(channelIndex, manualControl); - channelManualOverride[channelIndex] = false; // SetChannelManualControl sets to true, so immediately set to false - channelFrontend[channelIndex] = calculatedChannel; - } - private void UpdateFrontends() { - GetStatus(); // Update cachedSampleRateHz & cachedSampleResolution - - var loadScales = Frontend.GetAllLoadScales(calibration, channelsEnabled, cachedSampleRateHz); - for (int i = 0; i < channelFrontend.Length; i++) + for (int channelIndex = 0; channelIndex < 4; channelIndex++) { // Update all frontends that aren't under manual override, even for disabled channels (so relays actuate when expected) - if (!channelManualOverride[i]) + if (!channelManualOverride[channelIndex]) { - //Console.WriteLine($"Updating channel {i} frontend with load scale {loadScales[i]:F3}"); - SetChannelFrontend(i, channelFrontend[i], loadScales[i]); + SetChannelFrontend(channelIndex, channelFrontend[channelIndex]); } } - var branchGains = Frontend.GetAdcBranchGain(calibration, channelsEnabled, cachedSampleRateHz); - SetAdcBranchGainManualControl(branchGains); } private void CheckOpen() @@ -720,7 +813,7 @@ private static string GetLibraryReturnString(int retValue) }; } - private void SetSampleMode(ulong sampleRateHz, AdcResolution resolution, bool updateFrontends) + private void SetSampleMode(ulong sampleRateHz, AdcResolution resolution) { CheckOpen(); @@ -737,9 +830,6 @@ private void SetSampleMode(ulong sampleRateHz, AdcResolution resolution, bool up }; var retVal = Interop.SetSampleMode(tsHandle, (uint)sampleRateHz, format); - // There is a rate vs. scale relationship so update frontends - if (updateFrontends) - UpdateFrontends(); if (retVal == -2) logger.LogTrace($"Failed to set sample rate ({sampleRateHz}): {GetLibraryReturnString(retVal)}"); @@ -770,11 +860,8 @@ private void MonitoringLoop(ILogger logger, CancellationToken cancelToken) if (diff > tempDelta) { - for (int channelIndex = 0; channelIndex < 4; channelIndex++) - { - var frontend = GetChannelFrontend(channelIndex); - SetChannelFrontend(channelIndex, frontend); // Updates lastFrontendUpdateTemp - } + UpdateFrontends(); + lastFrontendUpdateTemp = currentTemp; } } diff --git a/source/TS.NET/Drivers/Simulation/Thunderscope.cs b/source/TS.NET/Drivers/Simulation/Thunderscope.cs index 75ee107..35b0c78 100644 --- a/source/TS.NET/Drivers/Simulation/Thunderscope.cs +++ b/source/TS.NET/Drivers/Simulation/Thunderscope.cs @@ -167,5 +167,22 @@ public void SetPeriodicEventSync(uint period_us) { throw new NotImplementedException(); } + public FrontendCalibration GetChannelCalibration(int channelIndex) + { + throw new NotImplementedException(); + } + public void SetChannelCalibration(int channelIndex, FrontendCalibration channelCalibration) + { + throw new NotImplementedException(); + } + + public AdcCalibration GetAdcCalibration() + { + throw new NotImplementedException(); + } + public void SetAdcCalibration(AdcCalibration adcCalibration) + { + throw new NotImplementedException(); + } } } diff --git a/source/TS.NET/IThunderscope.cs b/source/TS.NET/IThunderscope.cs index 27de239..d6c9fd9 100644 --- a/source/TS.NET/IThunderscope.cs +++ b/source/TS.NET/IThunderscope.cs @@ -6,10 +6,12 @@ public interface IThunderscope void Stop(); bool Running(); ThunderscopeChannelFrontend GetChannelFrontend(int channelIndex); - //ThunderscopeChannelCalibration GetChannelCalibration(int channelIndex); + FrontendCalibration GetChannelCalibration(int channelIndex); + AdcCalibration GetAdcCalibration(); void SetChannelEnable(int channelIndex, bool enabled); void SetChannelFrontend(int channelIndex, ThunderscopeChannelFrontend channel); - //void SetChannelCalibration(int channelIndex, ThunderscopeChannelCalibration channelCalibration); + void SetChannelCalibration(int channelIndex, FrontendCalibration channelCalibration); + void SetAdcCalibration(AdcCalibration adcCalibration); void Read(ThunderscopeMemory data); bool TryRead(Span data, out ulong sampleStartIndex, out int sampleLengthPerChannel); ThunderscopeHardwareConfig GetConfiguration();