From 01a0a053af211be21cfd2cd2bf96585a18a52e01 Mon Sep 17 00:00:00 2001 From: Lasse Beyer Date: Fri, 31 Oct 2025 17:10:49 +0100 Subject: [PATCH 1/7] Add support for PicoScope ps4000a, ps5000a (A API) --- scopehal/PicoOscilloscope.cpp | 911 +++++++++++++++++++++++++++------- scopehal/PicoOscilloscope.h | 44 +- 2 files changed, 741 insertions(+), 214 deletions(-) diff --git a/scopehal/PicoOscilloscope.cpp b/scopehal/PicoOscilloscope.cpp index f5fa2188..e47b25b1 100644 --- a/scopehal/PicoOscilloscope.cpp +++ b/scopehal/PicoOscilloscope.cpp @@ -41,7 +41,18 @@ using namespace std; #define RATE_5GSPS (INT64_C(5000) * INT64_C(1000) * INT64_C(1000)) #define RATE_2P5GSPS (INT64_C(2500) * INT64_C(1000) * INT64_C(1000)) #define RATE_1P25GSPS (INT64_C(1250) * INT64_C(1000) * INT64_C(1000)) +#define RATE_1GSPS (INT64_C(1000) * INT64_C(1000) * INT64_C(1000)) #define RATE_625MSPS (INT64_C(625) * INT64_C(1000) * INT64_C(1000)) +#define RATE_500MSPS (INT64_C(500) * INT64_C(1000) * INT64_C(1000)) +#define RATE_400MSPS (INT64_C(400) * INT64_C(1000) * INT64_C(1000)) +#define RATE_250MSPS (INT64_C(250) * INT64_C(1000) * INT64_C(1000)) +#define RATE_200MSPS (INT64_C(200) * INT64_C(1000) * INT64_C(1000)) +#define RATE_125MSPS (INT64_C(125) * INT64_C(1000) * INT64_C(1000)) +#define RATE_100MSPS (INT64_C(100) * INT64_C(1000) * INT64_C(1000)) +#define RATE_80MSPS (INT64_C(80) * INT64_C(1000) * INT64_C(1000)) +#define RATE_62P5MSPS (INT64_C(625) * INT64_C(1000) * INT64_C(100)) +#define RATE_50MSPS (INT64_C(50) * INT64_C(1000) * INT64_C(1000)) +#define RATE_40MSPS (INT64_C(40) * INT64_C(1000) * INT64_C(1000)) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Construction / destruction @@ -56,7 +67,7 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) IdentifyHardware(); //Set resolution - SetADCMode(0, ADC_MODE_8BIT); + SetADCMode(0, 0); //Add analog channel objects for(size_t i = 0; i < m_analogChannelCount; i++) @@ -84,39 +95,79 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) SetChannelVoltageRange(i, 0, 5); } - //Add digital channels (named 1D0...7 and 2D0...7) + //Add digital channels (named 1D0...7 and 2D0...7 for Pods, D0...15 for MSO models) m_digitalChannelBase = m_analogChannelCount; - for(size_t i=0; iSetDefaultDisplayName(); + case 3: + case 5: + { + for(size_t i=0; iSetDefaultDisplayName(); + //Change the display name to D0...D15 + chname = "D" + to_string(i); + chan->SetDisplayName(chname); + //Hysteresis is fixed to 250mV for MSO models + SetDigitalHysteresis(chnum, 0.25); + SetDigitalThreshold(chnum, 0); + } + } + break; - SetDigitalHysteresis(chnum, 0.1); - SetDigitalThreshold(chnum, 0); + case 6: + { + for(size_t i=0; iSetDefaultDisplayName(); + + SetDigitalHysteresis(chnum, 0.1); + SetDigitalThreshold(chnum, 0); + } + } } - + //Set initial memory configuration. - switch(m_series) + switch(m_picoSeries) { - case SERIES_3x0xD: - case SERIES_3x0xDMSO: + case 3: + case 5: { //62.5 Msps is the highest rate the 3000 series supports with all channels, including MSO, active. SetSampleRate(62500000L); @@ -124,9 +175,15 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) } break; - case SERIES_6403E: - case SERIES_6x0xE: - case SERIES_6x2xE: + case 4: + { + //40 Msps is the highest rate the 4000 series supports with all channels active. + SetSampleRate(40000000L); + SetSampleDepth(100000); + } + break; + + case 6: { //625 Msps is the highest rate the 6000 series supports with all channels, including MSO, active. SetSampleRate(625000000L); @@ -140,36 +197,28 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) } //Set initial AWG configuration - switch(m_series) + if(m_picoHasAwg) { //has function generator - case SERIES_3x0xD: - case SERIES_3x0xDMSO: - case SERIES_6403E: - case SERIES_6x0xE: - case SERIES_6x2xE: - SetFunctionChannelAmplitude(0, 0.1); - SetFunctionChannelShape(0, SHAPE_SQUARE); - SetFunctionChannelDutyCycle(0, 0.5); - SetFunctionChannelFrequency(0, 1e6); - SetFunctionChannelOffset(0, 0); - SetFunctionChannelOutputImpedance(0, IMPEDANCE_HIGH_Z); - SetFunctionChannelActive(0, false); - m_awgChannel = new FunctionGeneratorChannel( - this, - "AWG", - "#808080", - m_channels.size()); - m_channels.push_back(m_awgChannel); - - //Default to not showing in the filter graph to avoid clutter - m_awgChannel->m_visibilityMode = InstrumentChannel::VIS_HIDE; - break; + SetFunctionChannelAmplitude(0, 0.1); + SetFunctionChannelShape(0, SHAPE_SQUARE); + SetFunctionChannelDutyCycle(0, 0.5); + SetFunctionChannelFrequency(0, 1e6); + SetFunctionChannelOffset(0, 0); + SetFunctionChannelOutputImpedance(0, IMPEDANCE_HIGH_Z); + SetFunctionChannelActive(0, false); + m_awgChannel = new FunctionGeneratorChannel( + this, + "AWG", + "#808080", + m_channels.size()); + m_channels.push_back(m_awgChannel); - //no AWG - default: - m_awgChannel = nullptr; + //Default to not showing in the filter graph to avoid clutter + m_awgChannel->m_visibilityMode = InstrumentChannel::VIS_HIDE; } + else + m_awgChannel = nullptr; //Add the external trigger input m_extTrigChannel = @@ -274,58 +323,155 @@ void PicoOscilloscope::IdentifyHardware() m_digitalChannelCount = 0; //Figure out device family - if(m_model.length() < 5) + m_picoSeries = m_model[0] - '0'; + m_picoMinAdc = 8; + m_picoHasAwg = false; + m_picoHasExttrig = false; + m_picoHasBwlimiter = false; + m_picoHas50ohm = false; + m_BandwidthLimitLow = 20000000; //most common setting 20MHz + m_BandwidthLimitHigh = 200000000; //and 200MHz + + /*if(m_model.length() < 4) { LogWarning("Unknown PicoScope model \"%s\"\n", m_model.c_str()); - m_series = SERIES_UNKNOWN; - } - else if(m_model[0] == '3') + m_picoSeries = 0; + }*/ + switch(m_picoSeries) { - m_series = SERIES_3x0xD; - if(m_model.find("MSO") != string::npos) + case 3: { - // PicoScope3000 support 16 Digital Channels for MSO (or nothing) - m_digitalChannelCount = 16; - m_series = SERIES_3x0xDMSO; - LogWarning("SERIES_3x0xDMSO PicoScope model \"%s\"\n", m_model.c_str()); + m_BandwidthLimitHigh = 0; + if(m_model[4] != 'A') + m_picoHasAwg = true; + + if(m_model[4] == 'D') + { + m_picoHasBwlimiter = true; + m_awgBufferSize = 32768; + } + else + { + m_awgBufferSize = 32768; + switch(m_model[3]) + { + case '4': + case '5': + m_awgBufferSize = 8192; + break; + case '6': + m_awgBufferSize = 16384; + break; + } + } + + if(m_model.find("MSO") != string::npos) + { + m_digitalChannelCount = 16; + m_picoHasExttrig = false; + } + else + { + m_picoHasExttrig = true; + } + m_adcModes = {8}; + break; } - else + + case 4: { - LogWarning("SERIES_3x0xD PicoScope model \"%s\"\n", m_model.c_str()); + m_picoHasAwg = true; + m_awgBufferSize = 16384; + m_picoMinAdc = 12; + m_picoHasBwlimiter = false; + m_BandwidthLimitLow = 0; + m_BandwidthLimitHigh = 0; + + if(m_model.find("4444") != string::npos) + { + m_picoHasAwg = false; + m_picoHasExttrig = false; + m_picoHasBwlimiter = true; + m_BandwidthLimitLow = 100000; + m_BandwidthLimitHigh = 1000000; + m_awgBufferSize = 0; + } + m_adcModes = {12, 14}; + break; } - } - else if(m_model[0] == '6') - { - //We have two MSO pod connectors - m_digitalChannelCount = 16; - - switch(m_model[2]) + + case 5: { - case '2': - m_series = SERIES_6x2xE; - break; - - case '0': - if(m_model == "6403E") - m_series = SERIES_6403E; - else - m_series = SERIES_6x0xE; - break; - - default: - LogWarning("Unknown PicoScope model \"%s\"\n", m_model.c_str()); - m_series = SERIES_UNKNOWN; - break; + m_BandwidthLimitHigh = 0; + m_picoHasBwlimiter = true; + if(m_model[4] == 'A') + { + m_awgBufferSize = 0; + } + else if(m_model[4] == 'B') + { + m_picoHasAwg = true; + switch(m_model[3]) + { + case '2': + m_awgBufferSize = 16384; + break; + case '3': + m_awgBufferSize = 32768; + break; + case '4': + m_awgBufferSize = 49152; + break; + } + } + else if(m_model[4] == 'D') + { + m_picoHasAwg = true; + m_awgBufferSize = 32768; + } + + if(m_model.find("MSO") != string::npos) + { + m_digitalChannelCount = 16; + m_picoHasExttrig = false; + } + else + { + m_picoHasExttrig = true; + } + m_adcModes = {8, 12, 14, 15, 16}; + break; + } + + case 6: + { + m_digitalChannelCount = 16; + m_picoHas50ohm = true; + m_picoHasExttrig = true; + m_picoHasBwlimiter = true; + m_picoHasAwg = true; + m_awgBufferSize = 40960; + if(m_model.find("6428") != string::npos) + { + m_picoHasBwlimiter = false; + } + if(m_model[3] == '5' or m_model[3] == '6') + m_BandwidthLimitHigh = 0; + m_adcModes = {8, 10, 12}; + break; + } + + default: + { + LogWarning("Unknown PicoScope model \"%s\"\n", m_model.c_str()); + break; } } - else - { - LogWarning("Unknown PicoScope model \"%s\"\n", m_model.c_str()); - m_series = SERIES_UNKNOWN; - } - - //Ask the scope how many channels it has + //Ask the scope how many channels it has available or enabled m_analogChannelCount = stoi(m_transport->SendCommandQueuedWithReply("CHANS?")); + + LogDebug("PicoScope model \"%s\", series: %i, digital channels: %zu, BW-Limiter: %s/%u/%u, AWG: %s/%u, Ext.Trig: %s, 50 ohm: %s\n", + m_model.c_str(), m_picoSeries, m_digitalChannelCount, m_picoHasBwlimiter ? "1" : "0", m_BandwidthLimitLow, m_BandwidthLimitHigh, m_picoHasAwg ? "1" : "0", m_awgBufferSize, m_picoHasExttrig ? "1" : "0", m_picoHas50ohm ? "1" : "0"); } PicoOscilloscope::~PicoOscilloscope() @@ -337,19 +483,11 @@ PicoOscilloscope::~PicoOscilloscope() unsigned int PicoOscilloscope::GetInstrumentTypes() const { - switch(m_series) - { - //has function generator - case SERIES_3x0xD: - case SERIES_3x0xDMSO: - case SERIES_6x0xE: - case SERIES_6x2xE: - return Instrument::INST_OSCILLOSCOPE | Instrument::INST_FUNCTION; - - //no special features - default: - return Instrument::INST_OSCILLOSCOPE; - } + + if(m_picoHasAwg) + return Instrument::INST_OSCILLOSCOPE | Instrument::INST_FUNCTION; + else + return Instrument::INST_OSCILLOSCOPE; } uint32_t PicoOscilloscope::GetInstrumentTypesForChannel(size_t i) const @@ -401,6 +539,10 @@ void PicoOscilloscope::EnableChannel(size_t i) } RemoteBridgeOscilloscope::EnableChannel(i); + + //Memory configuration might have changed. Update availabe sample rates and memory depths. + GetSampleRatesNonInterleaved(); + GetSampleDepthsNonInterleaved(); } void PicoOscilloscope::DisableChannel(size_t i) @@ -419,29 +561,34 @@ void PicoOscilloscope::DisableChannel(size_t i) } m_transport->SendCommandQueued(":" + m_channels[i]->GetHwname() + ":OFF"); + + //Memory configuration might have changed. Update availabe sample rates and memory depths. + GetSampleRatesNonInterleaved(); + GetSampleDepthsNonInterleaved(); } vector PicoOscilloscope::GetAvailableCouplings(size_t /*i*/) { vector ret; - switch(m_series) + switch(m_picoSeries) { - case SERIES_3x0xD: - case SERIES_3x0xDMSO: + case 6: { - ret.push_back(OscilloscopeChannel::COUPLE_DC_1M); - ret.push_back(OscilloscopeChannel::COUPLE_AC_1M); + if(m_model.find("6428") == string::npos) + { + ret.push_back(OscilloscopeChannel::COUPLE_DC_1M); + ret.push_back(OscilloscopeChannel::COUPLE_AC_1M); + } + ret.push_back(OscilloscopeChannel::COUPLE_DC_50); + //ret.push_back(OscilloscopeChannel::COUPLE_GND); + break; } - break; - - case SERIES_6x0xE: - case SERIES_6x2xE: default: { ret.push_back(OscilloscopeChannel::COUPLE_DC_1M); ret.push_back(OscilloscopeChannel::COUPLE_AC_1M); - ret.push_back(OscilloscopeChannel::COUPLE_DC_50); - ret.push_back(OscilloscopeChannel::COUPLE_GND); + //ret.push_back(OscilloscopeChannel::COUPLE_DC_50); + //ret.push_back(OscilloscopeChannel::COUPLE_GND); } } return ret; @@ -884,18 +1031,29 @@ Oscilloscope::AnalogBank PicoOscilloscope::GetAnalogBank(size_t /*channel*/) bool PicoOscilloscope::IsADCModeConfigurable() { - switch(m_series) + switch(m_picoSeries) { - case SERIES_3x0xD: - case SERIES_3x0xDMSO: + case 3: return false; + break; - case SERIES_6x0xE: - case SERIES_6403E: - return false; + case 4: + if(m_model.find("4444") == string::npos) + return true; + else + return false; + break; - case SERIES_6x2xE: + case 5: return true; + break; + + case 6: + if(m_model[2] == '2') + return true; + else + return false; + break; default: LogWarning("PicoOscilloscope::IsADCModeConfigurable: unknown series\n"); @@ -905,46 +1063,71 @@ bool PicoOscilloscope::IsADCModeConfigurable() vector PicoOscilloscope::GetADCModeNames(size_t /*channel*/) { - //All scopes with variable resolution start at 8 bit and go up from there vector ret; - ret.push_back("8 Bit"); - if(Is10BitModeAvailable()) + + switch(m_picoSeries) { - ret.push_back("10 Bit"); - if(Is12BitModeAvailable()) - ret.push_back("12 Bit"); + case 4: + if(m_model.find("4444") == string::npos) + ret.push_back("12 Bit"); + break; + + case 5: + ret.push_back("8 Bit"); + if(Is12BitModeAvailable()) + { + ret.push_back("12 Bit"); + if(Is14BitModeAvailable()) + { + ret.push_back("14 Bit"); + if(Is15BitModeAvailable()) + { + ret.push_back("15 Bit"); + if(Is16BitModeAvailable()) + ret.push_back("16 Bit"); + } + } + } + break; + + case 6: + ret.push_back("8 Bit"); + if(Is10BitModeAvailable()) + { + ret.push_back("10 Bit"); + if(Is12BitModeAvailable()) + ret.push_back("12 Bit"); + } + break; + + default: + break; } + return ret; } size_t PicoOscilloscope::GetADCMode(size_t /*channel*/) { - return m_adcMode; + size_t n = 0; + for (int i : m_adcModes) + { + if(m_adcBits == i) + break; + n++; + } + return n; } void PicoOscilloscope::SetADCMode(size_t /*channel*/, size_t mode) { - m_adcMode = (ADCMode)mode; + m_adcBits = m_adcModes[mode]; + m_transport->SendCommand("BITS " + to_string(m_adcBits)); - switch(mode) - { - case ADC_MODE_8BIT: - m_transport->SendCommand("BITS 8"); - break; - - case ADC_MODE_10BIT: - m_transport->SendCommand("BITS 10"); - break; - - case ADC_MODE_12BIT: - m_transport->SendCommand("BITS 12"); - break; - - default: - LogWarning("PicoOscilloscope::SetADCMode requested invalid mode %zu, interpreting as 8 bit\n", mode); - m_adcMode = ADC_MODE_8BIT; - break; - } + + //Memory configuration might have changed. Update availabe sample rates and memory depths. + GetSampleRatesNonInterleaved(); + GetSampleDepthsNonInterleaved(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -971,7 +1154,10 @@ Oscilloscope::DigitalBank PicoOscilloscope::GetDigitalBank(size_t channel) bool PicoOscilloscope::IsDigitalHysteresisConfigurable() { - return true; + if(m_picoSeries == 6) + return true; + else + return false; } bool PicoOscilloscope::IsDigitalThresholdConfigurable() @@ -1003,12 +1189,43 @@ void PicoOscilloscope::SetDigitalHysteresis(size_t channel, float level) void PicoOscilloscope::SetDigitalThreshold(size_t channel, float level) { + switch(m_picoSeries) { - lock_guard lock(m_cacheMutex); - m_digitalThresholds[channel] = level; - } + case 3: + case 5: + { + //MSO scopes: sync threshold for the whole channel w/8 lanes + size_t chnum = channel - m_digitalChannelBase; + int n = 8; + if(chnum<8) + n = 0; + for(size_t i=0; i<8; i++) + { + //Set the threshold for every lane of the channel + chnum = i + n + m_digitalChannelBase; + { + lock_guard lock(m_cacheMutex); + m_digitalThresholds[chnum] = level; + } + //Only actually set the threshold on the first hardware channel though + if(i==0) + { + m_transport->SendCommand(GetOscilloscopeChannel(chnum)->GetHwname() + ":THRESH " + to_string(level)); + } + } + break; + } - m_transport->SendCommand(GetOscilloscopeChannel(channel)->GetHwname() + ":THRESH " + to_string(level)); + default: + { + { + lock_guard lock(m_cacheMutex); + m_digitalThresholds[channel] = level; + } + m_transport->SendCommand(GetOscilloscopeChannel(channel)->GetHwname() + ":THRESH " + to_string(level)); + break; + } + } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1127,33 +1344,56 @@ bool PicoOscilloscope::CanEnableChannel(size_t i) } //Fall back to the main path if we get here - switch(m_series) + switch(m_picoSeries) { - case SERIES_3x0xD: - case SERIES_3x0xDMSO: + case 3: return CanEnableChannel6000Series8Bit(i); break; - //6000 series - case SERIES_6403E: - case SERIES_6x0xE: - case SERIES_6x2xE: - switch(GetADCMode(0)) + case 4: + switch(m_adcBits) { - case ADC_MODE_8BIT: - return CanEnableChannel6000Series8Bit(i); + case 12: + return CanEnableChannel4000Series12Bit(i); + case 14: + return CanEnableChannel4000Series14Bit(i); + default: + return false; + } + break; + + case 5: + switch(m_adcBits) + { + case 8: + return CanEnableChannel5000Series8Bit(i); + case 12: + return CanEnableChannel5000Series12Bit(i); + case 14: + return CanEnableChannel5000Series14Bit(i); + case 15: + return CanEnableChannel5000Series15Bit(i); + case 16: + return CanEnableChannel5000Series16Bit(i); + default: + return false; + } + break; - case ADC_MODE_10BIT: + case 6: + switch(m_adcBits) + { + case 8: + return CanEnableChannel6000Series8Bit(i); + case 10: return CanEnableChannel6000Series10Bit(i); - - case ADC_MODE_12BIT: + case 12: return CanEnableChannel6000Series12Bit(i); - default: - break; + return false; } default: - break; + return false; } //When in doubt, assume all channels are available @@ -1177,7 +1417,7 @@ bool PicoOscilloscope::CanEnableChannel6000Series8Bit(size_t i) return false; //6403E only allows *one* 5 Gsps channel - else if(m_series == SERIES_6403E) + else if(m_model.find("6403") != string::npos) return (EnabledChannelCount == 0); //No banking restrictions for MSO pods if we have enough memory bandwidth @@ -1221,7 +1461,7 @@ bool PicoOscilloscope::CanEnableChannel6000Series8Bit(size_t i) return true; //6403E allows up to 2 channels, one AB and one CD - else if(m_series == SERIES_6403E) + else if(m_model.find("6403") != string::npos) { //Can enable a left bank channel if there's none in use if(i < 2) @@ -1353,10 +1593,198 @@ bool PicoOscilloscope::CanEnableChannel6000Series12Bit(size_t i) } } +/** + @brief Checks if we can enable a channel on a 5000 series scope configured for 8-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel5000Series8Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + if(rate > RATE_1GSPS) + return false; + + //1 Gsps allows only one channel/pod + else if(rate >= RATE_500MSPS) + return (EnabledChannelCount == 0); + + //500 Msps is allowed up to 2 total channels/pods + else if(rate >= RATE_250MSPS) + return (EnabledChannelCount <= 1); + + //250 Msps is allowed up to 4 total channels/pods + else if(rate >= RATE_125MSPS) + return (EnabledChannelCount <= 3); + + //Slow enough that there's no capacity limits + else + return true; +} + +/** + @brief Checks if we can enable a channel on a 5000 series scope configured for 12-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel5000Series12Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + //1 Gsps not allowed + if(rate > RATE_500MSPS) + return false; + + //500 Msps allows only one channel/pod + else if(rate >= RATE_250MSPS) + return (EnabledChannelCount == 0); + + //250 Msps is allowed up to 2 total channels/pods + else if(rate >= RATE_125MSPS) + return (EnabledChannelCount <= 1); + + //125 Msps is allowed up to 4 total channels/pods + else if(rate >= RATE_62P5MSPS) + return (EnabledChannelCount <= 3); + + //Slow enough that there's no capacity limits + else + return true; +} + +/** + @brief Checks if we can enable a channel on a 5000 series scope configured for 14-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel5000Series14Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + if(rate > RATE_125MSPS) + return false; + + //125 Msps is allowed up to 4 total channels/pods + else if(rate >= RATE_62P5MSPS) + return (EnabledChannelCount <= 3); + + //Slow enough that there's no capacity limits + else + return true; +} + +/** + @brief Checks if we can enable a channel on a 5000 series scope configured for 15-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel5000Series15Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + //15-bit allows up to 2 channels at 125 Msps plus one or two digital channels + if(rate > RATE_125MSPS) + return false; + + //No banking restrictions on MSO pods + else if(IsChannelIndexDigital(i)) + return true; + + //Too many channels enabled? + else if(GetEnabledAnalogChannelCount() >= 2) + return false; + + //125 Msps is allowed up to 2 channels + else + return (EnabledChannelCount <= 1); +} + +/** + @brief Checks if we can enable a channel on a 5000 series scope configured for 16-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel5000Series16Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + //16-bit allows just one channel at 62.5 Msps plus one or two digital channels + if(rate > RATE_62P5MSPS) + return false; + + //No banking restrictions on MSO pods + else if(IsChannelIndexDigital(i)) + return true; + + //Too many channels enabled? + else if(GetEnabledAnalogChannelCount() >= 1) + return false; + + //125 Msps is allowed only one channel + else + return (EnabledChannelCount == 0); +} + +/** + @brief Checks if we can enable a channel on a 4000 series scope configured for 12-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel4000Series12Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + if(m_model.find("4444") != string::npos) + { + if(rate > RATE_400MSPS) + return false; + + else if(rate >= RATE_200MSPS) + return (EnabledChannelCount == 0); + + else if(rate >= RATE_100MSPS) + return (EnabledChannelCount <= 1); + + else + return (EnabledChannelCount <= 3); + } + else + { + if(rate > RATE_80MSPS) + return false; + + //80 Msps is allowed up to 4 total channels + else if(rate >= RATE_40MSPS) + return (EnabledChannelCount <= 3); + + //40 Msps is allowed up to 8 total channels + else + return (EnabledChannelCount <= 7); + } +} + +/** + @brief Checks if we can enable a channel on a 4000 series scope configured for 14-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel4000Series14Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + //Only 4444 can do 14 bit + if(m_model.find("4444") == string::npos) + return false; + + else if(rate > RATE_50MSPS) + return false; + + //50 Msps is allowed up to 4 total channels + else + return (EnabledChannelCount <= 3); +} + +/** + @brief Checks if higher ADC resolutions are available + */ + bool PicoOscilloscope::Is10BitModeAvailable() { - //FlexRes only available on one series at the moment - if(m_series != SERIES_6x2xE) + //10-bit only available for 6x2xE models + if( !(m_picoSeries == 6) and (m_model[2] == '2') ) return false; int64_t rate = GetSampleRate(); @@ -1403,23 +1831,120 @@ bool PicoOscilloscope::Is10BitModeAvailable() bool PicoOscilloscope::Is12BitModeAvailable() { - //FlexRes only available on one series at the moment - if(m_series != SERIES_6x2xE) - return false; + int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + switch(m_picoSeries) + { + case 4: + return true; + + case 5: + //12 bit mode only available at 500 Msps and below + if(rate > RATE_500MSPS) + return false; + //500 Msps only one channel + else if(rate > RATE_250MSPS) + return (EnabledChannelCount <= 1); + //250 Msps allows 2 channels + else if(rate > RATE_125MSPS) + return (EnabledChannelCount <= 2); + //125 Msps allows 4 channels + else if(rate > RATE_62P5MSPS) + return (EnabledChannelCount <= 4); + //62.5 Msps allows more than 4 channels + else + return true; + + case 6: + //12 bit mode only available at 1.25 Gsps and below + if(rate > RATE_1P25GSPS) + return false; + //1.25 Gsps and below have the same banking restrictions: at most one channel from the left and right half + else + { + if(m_analogChannelCount == 8) + return (GetEnabledAnalogChannelCountAToD() <= 1) && (GetEnabledAnalogChannelCountEToH() <= 1); + else + return (GetEnabledAnalogChannelCountAToB() <= 1) && (GetEnabledAnalogChannelCountCToD() <= 1); + } + default: + return false; + } +} + +bool PicoOscilloscope::Is14BitModeAvailable() +{ int64_t rate = GetSampleRate(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); - //12 bit mode only available at 1.25 Gsps and below - if(rate > RATE_1P25GSPS) - return false; + switch(m_picoSeries) + { + case 4: + if(m_model.find("4444") == string::npos) + return false; + else + { + //14 bit mode only available at 50 Msps and below + if(rate > RATE_50MSPS) + return false; + else + return true; + } + case 5: + //14 bit mode only available at 125 Msps and below + if(rate > RATE_125MSPS) + return false; + //125 Msps allows 4 channels + else if(rate > RATE_62P5MSPS) + return (EnabledChannelCount <= 4); + //62.5 Msps allows more than 4 channels + else + return true; + + default: + return false; + } +} - //1.25 Gsps and below have the same banking restrictions: at most one channel from the left and right half - else +bool PicoOscilloscope::Is15BitModeAvailable() +{ + int64_t rate = GetSampleRate(); + //size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + switch(m_picoSeries) { - if(m_analogChannelCount == 8) - return (GetEnabledAnalogChannelCountAToD() <= 1) && (GetEnabledAnalogChannelCountEToH() <= 1); - else - return (GetEnabledAnalogChannelCountAToB() <= 1) && (GetEnabledAnalogChannelCountCToD() <= 1); + case 5: + //15 bit mode only available at 125 Msps and below + if(rate > RATE_125MSPS) + return false; + //125 Msps allows 2 channels plus one or two digital channels, but no more + else + return (GetEnabledAnalogChannelCount() <= 2); + + default: + return false; + } +} + +bool PicoOscilloscope::Is16BitModeAvailable() +{ + int64_t rate = GetSampleRate(); + //size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); + + switch(m_picoSeries) + { + case 5: + //16 bit mode only available at 62.5 Msps and below + if(rate > RATE_62P5MSPS) + return false; + //62.5 Msps allows 1 channels plus one or two digital channels, but no more + else + return (GetEnabledAnalogChannelCount() <= 1); + + default: + return false; } } diff --git a/scopehal/PicoOscilloscope.h b/scopehal/PicoOscilloscope.h index be56f9f7..4d280e8c 100644 --- a/scopehal/PicoOscilloscope.h +++ b/scopehal/PicoOscilloscope.h @@ -139,24 +139,6 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope virtual void SetDigitalHysteresis(size_t channel, float level) override; virtual void SetDigitalThreshold(size_t channel, float level) override; - enum Series - { - SERIES_3x0xD, //3000 series (first x=2 or 4 Chan, 2nd x is BW) - SERIES_3x0xDMSO,//3000 series+16bits MSO(first x=2 or 4 Chan, 2nd x is BW) - SERIES_6403E, //Lowest end 6000E model has less ADCs - SERIES_6x0xE, //6000 series with 8 bit resolution only - SERIES_6x2xE, //6000 series with FlexRes - - SERIES_UNKNOWN //unknown or invalid model name - }; - - enum ADCMode - { - ADC_MODE_8BIT = 0, - ADC_MODE_10BIT = 1, - ADC_MODE_12BIT = 2 - }; - bool IsDigitalPodPresent(size_t npod); bool IsDigitalPodActive(size_t npod); bool IsChannelIndexDigital(size_t i); @@ -171,6 +153,9 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope //Helpers for determining legal configurations bool Is10BitModeAvailable(); bool Is12BitModeAvailable(); + bool Is14BitModeAvailable(); + bool Is15BitModeAvailable(); + bool Is16BitModeAvailable(); size_t GetEnabledAnalogChannelCount(); size_t GetEnabledDigitalPodCount(); @@ -192,6 +177,13 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope bool CanEnableChannel6000Series8Bit(size_t i); bool CanEnableChannel6000Series10Bit(size_t i); bool CanEnableChannel6000Series12Bit(size_t i); + bool CanEnableChannel5000Series8Bit(size_t i); + bool CanEnableChannel5000Series12Bit(size_t i); + bool CanEnableChannel5000Series14Bit(size_t i); + bool CanEnableChannel5000Series15Bit(size_t i); + bool CanEnableChannel5000Series16Bit(size_t i); + bool CanEnableChannel4000Series12Bit(size_t i); + bool CanEnableChannel4000Series14Bit(size_t i); std::string GetChannelColor(size_t i); @@ -206,7 +198,6 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope //Most Pico API calls are write only, so we have to maintain all state clientside. //This isn't strictly a cache anymore since it's never flushed! std::map m_channelAttenuations; - ADCMode m_adcMode; std::map m_digitalBankPresent; std::map m_digitalThresholds; std::map m_digitalHysteresis; @@ -219,8 +210,19 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope float m_awgFrequency; FunctionGenerator::WaveShape m_awgShape; FunctionGenerator::OutputImpedance m_awgImpedance; - - Series m_series; + uint32_t m_awgBufferSize; + + //Scope features + uint32_t m_BandwidthLimitLow; + uint32_t m_BandwidthLimitHigh; + int m_adcBits; + int m_picoSeries; + int m_picoMinAdc; + bool m_picoHas50ohm; + bool m_picoHasAwg; + bool m_picoHasExttrig; + bool m_picoHasBwlimiter; + std::vector m_adcModes; ///@brief Buffers for storing raw ADC samples before converting to fp32 std::vector > > m_analogRawWaveformBuffers; From 21e8a39bfd89fac4b6543e3c4cfbc08d2d067d10 Mon Sep 17 00:00:00 2001 From: Lasse Beyer Date: Sat, 1 Nov 2025 16:06:20 +0100 Subject: [PATCH 2/7] Remove EX channel for scopes w/out ext trig input For PicoScopes that don't have an external trigger input, the creation of the 'EX' channel will be prevented to avoid clutter. --- scopehal/PicoOscilloscope.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/scopehal/PicoOscilloscope.cpp b/scopehal/PicoOscilloscope.cpp index e47b25b1..a7cd07ed 100644 --- a/scopehal/PicoOscilloscope.cpp +++ b/scopehal/PicoOscilloscope.cpp @@ -221,18 +221,21 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) m_awgChannel = nullptr; //Add the external trigger input - m_extTrigChannel = - new OscilloscopeChannel( - this, - "EX", - "#808080", - Unit(Unit::UNIT_FS), - Unit(Unit::UNIT_COUNTS), - Stream::STREAM_TYPE_TRIGGER, - m_channels.size()); - m_channels.push_back(m_extTrigChannel); - m_extTrigChannel->SetDefaultDisplayName(); - + if(m_picoHasExttrig) + { + m_extTrigChannel = + new OscilloscopeChannel( + this, + "EX", + "#808080", + Unit(Unit::UNIT_FS), + Unit(Unit::UNIT_COUNTS), + Stream::STREAM_TYPE_TRIGGER, + m_channels.size()); + m_channels.push_back(m_extTrigChannel); + m_extTrigChannel->SetDefaultDisplayName(); + } + //Configure the trigger auto trig = new EdgeTrigger(this); trig->SetType(EdgeTrigger::EDGE_RISING); @@ -539,6 +542,7 @@ void PicoOscilloscope::EnableChannel(size_t i) } RemoteBridgeOscilloscope::EnableChannel(i); + //LogDebug(" --- :%s:ON\n", m_channels[i]->GetHwname().c_str()); //Memory configuration might have changed. Update availabe sample rates and memory depths. GetSampleRatesNonInterleaved(); @@ -560,7 +564,9 @@ void PicoOscilloscope::DisableChannel(size_t i) return; } - m_transport->SendCommandQueued(":" + m_channels[i]->GetHwname() + ":OFF"); + //?//m_transport->SendCommandQueued(":" + m_channels[i]->GetHwname() + ":OFF"); + RemoteBridgeOscilloscope::DisableChannel(i); + //LogDebug(" --- :%s:OFF\n", m_channels[i]->GetHwname().c_str()); //Memory configuration might have changed. Update availabe sample rates and memory depths. GetSampleRatesNonInterleaved(); From 2a68806a98719dfeba1d39d56c7eda67411b44f7 Mon Sep 17 00:00:00 2001 From: Lasse Beyer Date: Sun, 2 Nov 2025 21:47:12 +0100 Subject: [PATCH 3/7] Fixed crash under Linux wrt. EX channel --- scopehal/PicoOscilloscope.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scopehal/PicoOscilloscope.cpp b/scopehal/PicoOscilloscope.cpp index a7cd07ed..5f0ddb86 100644 --- a/scopehal/PicoOscilloscope.cpp +++ b/scopehal/PicoOscilloscope.cpp @@ -232,10 +232,10 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) Unit(Unit::UNIT_COUNTS), Stream::STREAM_TYPE_TRIGGER, m_channels.size()); - m_channels.push_back(m_extTrigChannel); + m_channels.push_back(m_extTrigChannel); m_extTrigChannel->SetDefaultDisplayName(); } - + //Configure the trigger auto trig = new EdgeTrigger(this); trig->SetType(EdgeTrigger::EDGE_RISING); @@ -258,6 +258,7 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) vk::CommandPoolCreateInfo poolInfo( vk::CommandPoolCreateFlagBits::eTransient | vk::CommandPoolCreateFlagBits::eResetCommandBuffer, m_queue->m_family ); + m_pool = make_unique(*g_vkComputeDevice, poolInfo); vk::CommandBufferAllocateInfo bufinfo(**m_pool, vk::CommandBufferLevel::ePrimary, 1); @@ -519,7 +520,9 @@ void PicoOscilloscope::FlushConfigCache() bool PicoOscilloscope::IsChannelEnabled(size_t i) { //ext trigger should never be displayed - if(i == m_extTrigChannel->GetIndex()) + if(!m_picoHasExttrig) + return false; + else if(i == m_extTrigChannel->GetIndex()) return false; lock_guard lock(m_cacheMutex); @@ -602,7 +605,9 @@ vector PicoOscilloscope::GetAvailableCoupling double PicoOscilloscope::GetChannelAttenuation(size_t i) { - if(GetOscilloscopeChannel(i) == m_extTrigChannel) + if(!m_picoHasExttrig) + return 1; + else if(GetOscilloscopeChannel(i) == m_extTrigChannel) return 1; lock_guard lock(m_cacheMutex); From 03f629bc8322cb02ecacc51d716ae19fc90dcec7 Mon Sep 17 00:00:00 2001 From: Lasse Beyer Date: Tue, 4 Nov 2025 19:14:52 +0100 Subject: [PATCH 4/7] Add support for bandwidth limiter --- scopehal/PicoOscilloscope.cpp | 45 +++++++++++++++++++++++------------ scopehal/PicoOscilloscope.h | 1 + 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/scopehal/PicoOscilloscope.cpp b/scopehal/PicoOscilloscope.cpp index 5f0ddb86..ea0fd841 100644 --- a/scopehal/PicoOscilloscope.cpp +++ b/scopehal/PicoOscilloscope.cpp @@ -221,7 +221,7 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) m_awgChannel = nullptr; //Add the external trigger input - if(m_picoHasExttrig) + //if(m_picoHasExttrig) //TODO fix crash on linux { m_extTrigChannel = new OscilloscopeChannel( @@ -333,8 +333,8 @@ void PicoOscilloscope::IdentifyHardware() m_picoHasExttrig = false; m_picoHasBwlimiter = false; m_picoHas50ohm = false; - m_BandwidthLimitLow = 20000000; //most common setting 20MHz - m_BandwidthLimitHigh = 200000000; //and 200MHz + m_BandwidthLimitLow = 20; //most common setting 20MHz + m_BandwidthLimitHigh = 200; //and 200MHz /*if(m_model.length() < 4) { @@ -396,8 +396,10 @@ void PicoOscilloscope::IdentifyHardware() m_picoHasAwg = false; m_picoHasExttrig = false; m_picoHasBwlimiter = true; - m_BandwidthLimitLow = 100000; - m_BandwidthLimitHigh = 1000000; + //m_BandwidthLimitLow = 0.1; //100kHz currently not possible + //m_BandwidthLimitHigh = 1; //1MHz + m_BandwidthLimitLow = 1; + m_BandwidthLimitHigh = 0; m_awgBufferSize = 0; } m_adcModes = {12, 14}; @@ -475,7 +477,7 @@ void PicoOscilloscope::IdentifyHardware() m_analogChannelCount = stoi(m_transport->SendCommandQueuedWithReply("CHANS?")); LogDebug("PicoScope model \"%s\", series: %i, digital channels: %zu, BW-Limiter: %s/%u/%u, AWG: %s/%u, Ext.Trig: %s, 50 ohm: %s\n", - m_model.c_str(), m_picoSeries, m_digitalChannelCount, m_picoHasBwlimiter ? "1" : "0", m_BandwidthLimitLow, m_BandwidthLimitHigh, m_picoHasAwg ? "1" : "0", m_awgBufferSize, m_picoHasExttrig ? "1" : "0", m_picoHas50ohm ? "1" : "0"); + m_model.c_str(), m_picoSeries, m_digitalChannelCount, m_picoHasBwlimiter ? "Y" : "N", m_BandwidthLimitLow, m_BandwidthLimitHigh, m_picoHasAwg ? "Y" : "N", m_awgBufferSize, m_picoHasExttrig ? "Y" : "N", m_picoHas50ohm ? "Y" : "N"); } PicoOscilloscope::~PicoOscilloscope() @@ -520,9 +522,7 @@ void PicoOscilloscope::FlushConfigCache() bool PicoOscilloscope::IsChannelEnabled(size_t i) { //ext trigger should never be displayed - if(!m_picoHasExttrig) - return false; - else if(i == m_extTrigChannel->GetIndex()) + if(i == m_extTrigChannel->GetIndex()) return false; lock_guard lock(m_cacheMutex); @@ -605,9 +605,7 @@ vector PicoOscilloscope::GetAvailableCoupling double PicoOscilloscope::GetChannelAttenuation(size_t i) { - if(!m_picoHasExttrig) - return 1; - else if(GetOscilloscopeChannel(i) == m_extTrigChannel) + if(GetOscilloscopeChannel(i) == m_extTrigChannel) return 1; lock_guard lock(m_cacheMutex); @@ -626,13 +624,30 @@ void PicoOscilloscope::SetChannelAttenuation(size_t i, double atten) m_channelOffsets[i] *= delta; } -unsigned int PicoOscilloscope::GetChannelBandwidthLimit(size_t /*i*/) +vector PicoOscilloscope::GetChannelBandwidthLimiters(size_t i) +{ + vector ret; + + ret.push_back(0); + if(m_BandwidthLimitLow != 0) + { + ret.push_back(m_BandwidthLimitLow); + if(m_BandwidthLimitHigh != 0) + ret.push_back(m_BandwidthLimitHigh); + } + return ret; + +} + +unsigned int PicoOscilloscope::GetChannelBandwidthLimit(size_t i) { - return 0; + int ret = stoi(m_transport->SendCommandQueuedWithReply(GetOscilloscopeChannel(i)->GetHwname() + ":BWLIM?")); + return ret; } -void PicoOscilloscope::SetChannelBandwidthLimit(size_t /*i*/, unsigned int /*limit_mhz*/) +void PicoOscilloscope::SetChannelBandwidthLimit(size_t i, unsigned int limit_mhz) { + m_transport->SendCommand(GetOscilloscopeChannel(i)->GetHwname() + ":BWLIM " + to_string(limit_mhz)); } OscilloscopeChannel* PicoOscilloscope::GetExternalTrigger() diff --git a/scopehal/PicoOscilloscope.h b/scopehal/PicoOscilloscope.h index 4d280e8c..f5067f35 100644 --- a/scopehal/PicoOscilloscope.h +++ b/scopehal/PicoOscilloscope.h @@ -64,6 +64,7 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope virtual double GetChannelAttenuation(size_t i) override; virtual void SetChannelAttenuation(size_t i, double atten) override; virtual unsigned int GetChannelBandwidthLimit(size_t i) override; + virtual std::vector GetChannelBandwidthLimiters(size_t i) override; virtual void SetChannelBandwidthLimit(size_t i, unsigned int limit_mhz) override; virtual OscilloscopeChannel* GetExternalTrigger() override; virtual bool CanEnableChannel(size_t i) override; From 056f9a93713c2db4a525099b0b9afa31b613d4cd Mon Sep 17 00:00:00 2001 From: Lasse Beyer Date: Sun, 9 Nov 2025 13:53:05 +0100 Subject: [PATCH 5/7] Add support for 4000/5000 series, BW limiter --- scopehal/PicoOscilloscope.cpp | 31 +++++++++++++++++-------------- scopehal/PicoOscilloscope.h | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/scopehal/PicoOscilloscope.cpp b/scopehal/PicoOscilloscope.cpp index ea0fd841..37164e63 100644 --- a/scopehal/PicoOscilloscope.cpp +++ b/scopehal/PicoOscilloscope.cpp @@ -221,7 +221,7 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) m_awgChannel = nullptr; //Add the external trigger input - //if(m_picoHasExttrig) //TODO fix crash on linux + if(m_picoHasExttrig) { m_extTrigChannel = new OscilloscopeChannel( @@ -334,7 +334,7 @@ void PicoOscilloscope::IdentifyHardware() m_picoHasBwlimiter = false; m_picoHas50ohm = false; m_BandwidthLimitLow = 20; //most common setting 20MHz - m_BandwidthLimitHigh = 200; //and 200MHz + m_BandwidthLimitHigh = 200; //and 200MHz /*if(m_model.length() < 4) { @@ -522,8 +522,10 @@ void PicoOscilloscope::FlushConfigCache() bool PicoOscilloscope::IsChannelEnabled(size_t i) { //ext trigger should never be displayed - if(i == m_extTrigChannel->GetIndex()) + if(!m_picoHasExttrig) return false; + //if(i == m_extTrigChannel->GetIndex()) + // return false; lock_guard lock(m_cacheMutex); return m_channelsEnabled[i]; @@ -545,7 +547,6 @@ void PicoOscilloscope::EnableChannel(size_t i) } RemoteBridgeOscilloscope::EnableChannel(i); - //LogDebug(" --- :%s:ON\n", m_channels[i]->GetHwname().c_str()); //Memory configuration might have changed. Update availabe sample rates and memory depths. GetSampleRatesNonInterleaved(); @@ -567,9 +568,7 @@ void PicoOscilloscope::DisableChannel(size_t i) return; } - //?//m_transport->SendCommandQueued(":" + m_channels[i]->GetHwname() + ":OFF"); RemoteBridgeOscilloscope::DisableChannel(i); - //LogDebug(" --- :%s:OFF\n", m_channels[i]->GetHwname().c_str()); //Memory configuration might have changed. Update availabe sample rates and memory depths. GetSampleRatesNonInterleaved(); @@ -605,8 +604,12 @@ vector PicoOscilloscope::GetAvailableCoupling double PicoOscilloscope::GetChannelAttenuation(size_t i) { - if(GetOscilloscopeChannel(i) == m_extTrigChannel) - return 1; + if(m_picoHasExttrig) + { + //this will crash if m_extTrigChannel was not created, hence the prior check for m_picoHasExttrig + if(GetOscilloscopeChannel(i) == m_extTrigChannel) + return 1; + } lock_guard lock(m_cacheMutex); return m_channelAttenuations[i]; @@ -624,7 +627,7 @@ void PicoOscilloscope::SetChannelAttenuation(size_t i, double atten) m_channelOffsets[i] *= delta; } -vector PicoOscilloscope::GetChannelBandwidthLimiters(size_t i) +vector PicoOscilloscope::GetChannelBandwidthLimiters(size_t /*i*/) { vector ret; @@ -1622,7 +1625,7 @@ bool PicoOscilloscope::CanEnableChannel6000Series12Bit(size_t i) /** @brief Checks if we can enable a channel on a 5000 series scope configured for 8-bit ADC resolution */ -bool PicoOscilloscope::CanEnableChannel5000Series8Bit(size_t i) +bool PicoOscilloscope::CanEnableChannel5000Series8Bit(size_t /*i*/) { int64_t rate = GetSampleRate(); size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); @@ -1650,7 +1653,7 @@ bool PicoOscilloscope::CanEnableChannel5000Series8Bit(size_t i) /** @brief Checks if we can enable a channel on a 5000 series scope configured for 12-bit ADC resolution */ -bool PicoOscilloscope::CanEnableChannel5000Series12Bit(size_t i) +bool PicoOscilloscope::CanEnableChannel5000Series12Bit(size_t /*i*/) { int64_t rate = GetSampleRate(); size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); @@ -1679,7 +1682,7 @@ bool PicoOscilloscope::CanEnableChannel5000Series12Bit(size_t i) /** @brief Checks if we can enable a channel on a 5000 series scope configured for 14-bit ADC resolution */ -bool PicoOscilloscope::CanEnableChannel5000Series14Bit(size_t i) +bool PicoOscilloscope::CanEnableChannel5000Series14Bit(size_t /*i*/) { int64_t rate = GetSampleRate(); size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); @@ -1749,7 +1752,7 @@ bool PicoOscilloscope::CanEnableChannel5000Series16Bit(size_t i) /** @brief Checks if we can enable a channel on a 4000 series scope configured for 12-bit ADC resolution */ -bool PicoOscilloscope::CanEnableChannel4000Series12Bit(size_t i) +bool PicoOscilloscope::CanEnableChannel4000Series12Bit(size_t /*i*/) { int64_t rate = GetSampleRate(); size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); @@ -1786,7 +1789,7 @@ bool PicoOscilloscope::CanEnableChannel4000Series12Bit(size_t i) /** @brief Checks if we can enable a channel on a 4000 series scope configured for 14-bit ADC resolution */ -bool PicoOscilloscope::CanEnableChannel4000Series14Bit(size_t i) +bool PicoOscilloscope::CanEnableChannel4000Series14Bit(size_t /*i*/) { int64_t rate = GetSampleRate(); size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + GetEnabledDigitalPodCount(); diff --git a/scopehal/PicoOscilloscope.h b/scopehal/PicoOscilloscope.h index f5067f35..1455e884 100644 --- a/scopehal/PicoOscilloscope.h +++ b/scopehal/PicoOscilloscope.h @@ -2,7 +2,7 @@ * * * libscopehal v0.1 * * * -* Copyright (c) 2012-2023 Andrew D. Zonenberg and contributors * +* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * From 7bd9aa6c4cb335ebf91aa029f2b7108cfbe2285b Mon Sep 17 00:00:00 2001 From: Lasse Beyer Date: Sun, 9 Nov 2025 21:56:12 +0100 Subject: [PATCH 6/7] Add support for 4000/5000 series, BW limiter Fixed a stupid mistake where enabled channels would no longer be shown as enabled. --- scopehal/PicoOscilloscope.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scopehal/PicoOscilloscope.cpp b/scopehal/PicoOscilloscope.cpp index 37164e63..ca380299 100644 --- a/scopehal/PicoOscilloscope.cpp +++ b/scopehal/PicoOscilloscope.cpp @@ -522,10 +522,12 @@ void PicoOscilloscope::FlushConfigCache() bool PicoOscilloscope::IsChannelEnabled(size_t i) { //ext trigger should never be displayed - if(!m_picoHasExttrig) - return false; - //if(i == m_extTrigChannel->GetIndex()) - // return false; + if(m_picoHasExttrig) + { + //this will crash if m_extTrigChannel was not created, hence the prior check for m_picoHasExttrig + if(i == m_extTrigChannel->GetIndex()) + return false; + } lock_guard lock(m_cacheMutex); return m_channelsEnabled[i]; From e898527ef3b635ebf2c2c44ebf98f24f40840d58 Mon Sep 17 00:00:00 2001 From: Lasse Beyer Date: Sun, 4 Jan 2026 21:40:42 +0100 Subject: [PATCH 7/7] Add support for Pico 2000A/3000E/4000A/5000A Added support for some new Pico APIs. This update is needed together with the scopehal-pico-bridge to be able to use the new devices. --- scopehal/PicoOscilloscope.cpp | 301 +++++++++++++++++++++++++++------- scopehal/PicoOscilloscope.h | 56 ++++++- 2 files changed, 292 insertions(+), 65 deletions(-) diff --git a/scopehal/PicoOscilloscope.cpp b/scopehal/PicoOscilloscope.cpp index ca380299..d9311d22 100644 --- a/scopehal/PicoOscilloscope.cpp +++ b/scopehal/PicoOscilloscope.cpp @@ -2,7 +2,7 @@ * * * libscopehal * * * -* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors * +* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * @@ -99,6 +99,7 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) m_digitalChannelBase = m_analogChannelCount; switch(m_picoSeries) { + case 2: case 3: case 5: { @@ -126,8 +127,12 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) //Change the display name to D0...D15 chname = "D" + to_string(i); chan->SetDisplayName(chname); - //Hysteresis is fixed to 250mV for MSO models + //Hysteresis is fixed to 250mV for most MSO models SetDigitalHysteresis(chnum, 0.25); + if( m_model=="2206" || m_model=="2207" || m_model=="2208" ) + SetDigitalHysteresis(chnum, 0.2); + if( m_model=="2205MSO" || m_model=="3204MSO" || m_model=="3205MSO" || m_model=="3206MSO" ) + SetDigitalHysteresis(chnum, 0.1); SetDigitalThreshold(chnum, 0); } } @@ -166,6 +171,7 @@ PicoOscilloscope::PicoOscilloscope(SCPITransport* transport) //Set initial memory configuration. switch(m_picoSeries) { + case 2: case 3: case 5: { @@ -328,35 +334,40 @@ void PicoOscilloscope::IdentifyHardware() //Figure out device family m_picoSeries = m_model[0] - '0'; - m_picoMinAdc = 8; m_picoHasAwg = false; m_picoHasExttrig = false; m_picoHasBwlimiter = false; m_picoHas50ohm = false; - m_BandwidthLimitLow = 20; //most common setting 20MHz - m_BandwidthLimitHigh = 200; //and 200MHz - /*if(m_model.length() < 4) - { - LogWarning("Unknown PicoScope model \"%s\"\n", m_model.c_str()); - m_picoSeries = 0; - }*/ switch(m_picoSeries) { + case 2: + { + m_picoHasAwg = true; + m_picoHasBwlimiter = false; + m_awgBufferSize = 8192; + if(m_model[4] == 'B') + m_awgBufferSize = 32768; + + m_adcModes = {8}; + m_BandwidthLimits = {0}; + break; + } + case 3: { - m_BandwidthLimitHigh = 0; if(m_model[4] != 'A') m_picoHasAwg = true; - if(m_model[4] == 'D') + if( (m_model[4] == 'D') || (m_model.find("34") != string::npos) ) { m_picoHasBwlimiter = true; + m_BandwidthLimits = {0, 20}; m_awgBufferSize = 32768; } - else + + if( (m_model[4] == 'A') || (m_model[4] == 'B') ) { - m_awgBufferSize = 32768; switch(m_model[3]) { case '4': @@ -366,6 +377,9 @@ void PicoOscilloscope::IdentifyHardware() case '6': m_awgBufferSize = 16384; break; + case '7': + m_awgBufferSize = 32768; + break; } } @@ -375,10 +389,24 @@ void PicoOscilloscope::IdentifyHardware() m_picoHasExttrig = false; } else + m_picoHasExttrig = true; + + m_adcModes = {8}; + if(m_model[4] == 'E') { + m_picoHas50ohm = true; m_picoHasExttrig = true; + m_adcModes.push_back(10); + m_BandwidthLimits.push_back(50); + m_BandwidthLimits.push_back(100); + if( (m_model[3] - '0') >= 6) + m_BandwidthLimits.push_back(200); + if( (m_model[3] - '0') >= 7) + m_BandwidthLimits.push_back(350); + if( (m_model[3] - '0') == 8) + m_BandwidthLimits.push_back(500); } - m_adcModes = {8}; + break; } @@ -386,30 +414,26 @@ void PicoOscilloscope::IdentifyHardware() { m_picoHasAwg = true; m_awgBufferSize = 16384; - m_picoMinAdc = 12; m_picoHasBwlimiter = false; - m_BandwidthLimitLow = 0; - m_BandwidthLimitHigh = 0; if(m_model.find("4444") != string::npos) { m_picoHasAwg = false; m_picoHasExttrig = false; m_picoHasBwlimiter = true; - //m_BandwidthLimitLow = 0.1; //100kHz currently not possible - //m_BandwidthLimitHigh = 1; //1MHz - m_BandwidthLimitLow = 1; - m_BandwidthLimitHigh = 0; + m_BandwidthLimits = {0, 1}; //Should be 0 (=full), 100kHz and 1MHz m_awgBufferSize = 0; + m_adcModes = {12, 14}; } - m_adcModes = {12, 14}; + else + m_adcModes = {12}; break; } case 5: { - m_BandwidthLimitHigh = 0; m_picoHasBwlimiter = true; + m_BandwidthLimits = {0, 20}; if(m_model[4] == 'A') { m_awgBufferSize = 0; @@ -460,24 +484,32 @@ void PicoOscilloscope::IdentifyHardware() if(m_model.find("6428") != string::npos) { m_picoHasBwlimiter = false; + m_BandwidthLimits = {0}; } if(m_model[3] == '5' or m_model[3] == '6') - m_BandwidthLimitHigh = 0; - m_adcModes = {8, 10, 12}; + m_BandwidthLimits = {0, 20}; + else + m_BandwidthLimits = {0, 20, 200}; + + if(m_model[2] == '2') + m_adcModes = {8, 10, 12}; + else + m_adcModes = {8}; break; } default: { LogWarning("Unknown PicoScope model \"%s\"\n", m_model.c_str()); + m_picoSeries = 0; break; } } //Ask the scope how many channels it has available or enabled m_analogChannelCount = stoi(m_transport->SendCommandQueuedWithReply("CHANS?")); - LogDebug("PicoScope model \"%s\", series: %i, digital channels: %zu, BW-Limiter: %s/%u/%u, AWG: %s/%u, Ext.Trig: %s, 50 ohm: %s\n", - m_model.c_str(), m_picoSeries, m_digitalChannelCount, m_picoHasBwlimiter ? "Y" : "N", m_BandwidthLimitLow, m_BandwidthLimitHigh, m_picoHasAwg ? "Y" : "N", m_awgBufferSize, m_picoHasExttrig ? "Y" : "N", m_picoHas50ohm ? "Y" : "N"); + //LogDebug("PicoScope model \"%s\", series: %i, digital channels: %zu, BW-Limiter: %s, AWG: %s/%u, Ext.Trig: %s, 50 ohm: %s\n", + // m_model.c_str(), m_picoSeries, m_digitalChannelCount, m_picoHasBwlimiter ? "Y" : "N", m_picoHasAwg ? "Y" : "N", m_awgBufferSize, m_picoHasExttrig ? "Y" : "N", m_picoHas50ohm ? "Y" : "N"); } PicoOscilloscope::~PicoOscilloscope() @@ -580,26 +612,22 @@ void PicoOscilloscope::DisableChannel(size_t i) vector PicoOscilloscope::GetAvailableCouplings(size_t /*i*/) { vector ret; - switch(m_picoSeries) + //All models with an 'E' have 50 ohm + if(m_model[4] == 'E') { - case 6: - { - if(m_model.find("6428") == string::npos) - { - ret.push_back(OscilloscopeChannel::COUPLE_DC_1M); - ret.push_back(OscilloscopeChannel::COUPLE_AC_1M); - } - ret.push_back(OscilloscopeChannel::COUPLE_DC_50); - //ret.push_back(OscilloscopeChannel::COUPLE_GND); - break; - } - default: + if(m_model.find("6428") == string::npos) //6428 has ONLY 50 ohm and NO 1Meg { ret.push_back(OscilloscopeChannel::COUPLE_DC_1M); ret.push_back(OscilloscopeChannel::COUPLE_AC_1M); - //ret.push_back(OscilloscopeChannel::COUPLE_DC_50); - //ret.push_back(OscilloscopeChannel::COUPLE_GND); } + ret.push_back(OscilloscopeChannel::COUPLE_DC_50); + //ret.push_back(OscilloscopeChannel::COUPLE_GND); + } + else + { + ret.push_back(OscilloscopeChannel::COUPLE_DC_1M); + ret.push_back(OscilloscopeChannel::COUPLE_AC_1M); + //ret.push_back(OscilloscopeChannel::COUPLE_GND); } return ret; } @@ -631,17 +659,7 @@ void PicoOscilloscope::SetChannelAttenuation(size_t i, double atten) vector PicoOscilloscope::GetChannelBandwidthLimiters(size_t /*i*/) { - vector ret; - - ret.push_back(0); - if(m_BandwidthLimitLow != 0) - { - ret.push_back(m_BandwidthLimitLow); - if(m_BandwidthLimitHigh != 0) - ret.push_back(m_BandwidthLimitHigh); - } - return ret; - + return m_BandwidthLimits; } unsigned int PicoOscilloscope::GetChannelBandwidthLimit(size_t i) @@ -1064,10 +1082,17 @@ bool PicoOscilloscope::IsADCModeConfigurable() { switch(m_picoSeries) { - case 3: + case 2: return false; break; + case 3: + if(m_model[2] == '1') + return true; + else + return false; + break; + case 4: if(m_model.find("4444") == string::npos) return true; @@ -1098,9 +1123,20 @@ vector PicoOscilloscope::GetADCModeNames(size_t /*channel*/) switch(m_picoSeries) { + case 2: + ret.push_back("8 Bit"); + break; + + case 3: + ret.push_back("8 Bit"); + if(Is10BitModeAvailable()) + ret.push_back("10 Bit"); + break; + case 4: - if(m_model.find("4444") == string::npos) - ret.push_back("12 Bit"); + ret.push_back("12 Bit"); + if(m_model.find("4444") != string::npos) + ret.push_back("14 Bit"); break; case 5: @@ -1222,6 +1258,7 @@ void PicoOscilloscope::SetDigitalThreshold(size_t channel, float level) { switch(m_picoSeries) { + case 2: case 3: case 5: { @@ -1377,8 +1414,20 @@ bool PicoOscilloscope::CanEnableChannel(size_t i) //Fall back to the main path if we get here switch(m_picoSeries) { + case 2: + return CanEnableChannel2000Series8Bit(i); + break; + case 3: - return CanEnableChannel6000Series8Bit(i); + switch(m_adcBits) + { + case 8: + return CanEnableChannel3000Series8Bit(i); + case 10: + return CanEnableChannel3000Series10Bit(i); + default: + return false; + } break; case 4: @@ -1423,6 +1472,8 @@ bool PicoOscilloscope::CanEnableChannel(size_t i) default: return false; } + break; + default: return false; } @@ -1808,14 +1859,140 @@ bool PicoOscilloscope::CanEnableChannel4000Series14Bit(size_t /*i*/) return (EnabledChannelCount <= 3); } +/** + @brief Checks if we can enable a channel on a 3000 series scope configured for 8-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel3000Series8Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledDigitalChannelCount = GetEnabledDigitalPodCount(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + EnabledDigitalChannelCount; + + if(m_model[4] == 'E') + { + if( (IsChannelIndexDigital(i)) || (EnabledDigitalChannelCount > 0) ) + { + if(rate > RATE_1P25GSPS) + return false; + + //1.25 Gsps is allowed up to 4 total channels/pods + else if(rate >= RATE_625MSPS) + return (EnabledChannelCount <= 3); + + //Slow enough that there's no capacity limits + else + return true; + } + else + { + if(rate > RATE_5GSPS) + return false; + + //5 Gsps allows only one channel/pod + else if(rate >= RATE_2P5GSPS) + return (EnabledChannelCount == 0); + + //2.5 Gsps is allowed up to 2 total channels/pods + else if(rate >= RATE_1P25GSPS) + return (EnabledChannelCount <= 1); + + //1.25 Gsps is allowed up to 4 total channels/pods + else if(rate >= RATE_625MSPS) + return (EnabledChannelCount <= 3); + + //Slow enough that there's no capacity limits + else + return true; + } + } + else + { + if(rate > RATE_1GSPS) + return false; + + //1 Gsps allows only one channel/pod + else if(rate >= RATE_500MSPS) + return (EnabledChannelCount == 0); + + //500 Msps is allowed up to 2 total channels/pods + else if(rate >= RATE_250MSPS) + return (EnabledChannelCount <= 1); + + //250 Msps is allowed up to 4 total channels/pods + else if(rate >= RATE_125MSPS) + return (EnabledChannelCount <= 3); + + //Slow enough that there's no capacity limits + else + return true; + } +} + +/** + @brief Checks if we can enable a channel on a 3000 series scope configured for 10-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel3000Series10Bit(size_t i) +{ + int64_t rate = GetSampleRate(); + size_t EnabledDigitalChannelCount = GetEnabledDigitalPodCount(); + size_t EnabledChannelCount = GetEnabledAnalogChannelCount() + EnabledDigitalChannelCount; + + if( (IsChannelIndexDigital(i)) || (EnabledDigitalChannelCount > 0) ) + { + if(rate > RATE_1P25GSPS) + return false; + + //1.25 Gsps is allowed up to 2 total channels/pods + else if(rate >= RATE_625MSPS) + return (EnabledChannelCount <= 1); + + //625 Msps is allowed up to 4 total channels/pods + else if(rate >= RATE_625MSPS/2) + return (EnabledChannelCount <= 3); + + //Slow enough that there's no capacity limits + else + return true; + } + else + { + if(rate > RATE_2P5GSPS) + return false; + + //2.5 Gsps allows only one channel/pod + else if(rate >= RATE_1P25GSPS) + return (EnabledChannelCount == 0); + + //1.25 Gsps is allowed up to 2 total channels/pods + else if(rate >= RATE_625MSPS) + return (EnabledChannelCount <= 1); + + //625 Msps is allowed up to 4 total channels/pods + else if(rate >= RATE_625MSPS/2) + return (EnabledChannelCount <= 3); + + //Slow enough that there's no capacity limits + else + return true; + } +} + +/** + @brief Checks if we can enable a channel on a 2000 series scope configured for 8-bit ADC resolution + */ +bool PicoOscilloscope::CanEnableChannel2000Series8Bit(size_t /*i*/) +{ + return true; +} + /** @brief Checks if higher ADC resolutions are available */ bool PicoOscilloscope::Is10BitModeAvailable() { - //10-bit only available for 6x2xE models - if( !(m_picoSeries == 6) and (m_model[2] == '2') ) + //10-bit only available for 6x2xE and 3000E models + if( !( (m_model[4] == 'E') and (m_model[2] != '0') ) ) return false; int64_t rate = GetSampleRate(); @@ -1900,6 +2077,7 @@ bool PicoOscilloscope::Is12BitModeAvailable() else return (GetEnabledAnalogChannelCountAToB() <= 1) && (GetEnabledAnalogChannelCountCToD() <= 1); } + default: return false; } @@ -1923,6 +2101,7 @@ bool PicoOscilloscope::Is14BitModeAvailable() else return true; } + case 5: //14 bit mode only available at 125 Msps and below if(rate > RATE_125MSPS) @@ -1970,7 +2149,7 @@ bool PicoOscilloscope::Is16BitModeAvailable() //16 bit mode only available at 62.5 Msps and below if(rate > RATE_62P5MSPS) return false; - //62.5 Msps allows 1 channels plus one or two digital channels, but no more + //62.5 Msps allows 1 channel plus one or two digital channels, but no more else return (GetEnabledAnalogChannelCount() <= 1); diff --git a/scopehal/PicoOscilloscope.h b/scopehal/PicoOscilloscope.h index 1455e884..36a65285 100644 --- a/scopehal/PicoOscilloscope.h +++ b/scopehal/PicoOscilloscope.h @@ -2,7 +2,7 @@ * * * libscopehal v0.1 * * * -* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors * +* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * @@ -139,6 +139,51 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope virtual float GetDigitalThreshold(size_t channel) override; virtual void SetDigitalHysteresis(size_t channel, float level) override; virtual void SetDigitalThreshold(size_t channel, float level) override; +/* + enum Series //TODO + { + SERIES_2205MSO, + SERIES_2205AMSO, + SERIES_2x0x, + SERIES_2x0xA, + SERIES_2x0xB, + SERIES_2x0xBMSO, + SERIES_3x0xD, //3000 series (first x=2 or 4 Chan, 2nd x is BW) + SERIES_3x0xDMSO,//3000 series+16bits MSO(first x=2 or 4 Chan, 2nd x is BW) + SERIES_3x1xE, + SERIES_3x1xEMSO, + SERIES_4xx4, + SERIES_4xx4A, + SERIES_5x4xA, + SERIES_5x4xB, + SERIES_5x4xD, + SERIES_5x4xDMSO, + SERIES_6403E, //Lowest end 6000E model has less ADCs + SERIES_6x0xE, //6000 series with 8 bit resolution only + SERIES_6x2xE, //6000 series with FlexRes + + SERIES_UNKNOWN //unknown or invalid model name + }; + */ + + enum Series //TODO + { + SERIES_2000A, + SERIES_2000AMSO, + SERIES_3000D, //3000 series (first x=2 or 4 Chan, 2nd x is BW) + SERIES_3000DMSO,//3000 series+16bits MSO(first x=2 or 4 Chan, 2nd x is BW) + SERIES_3000E, + SERIES_3000EMSO, + SERIES_4000A, + SERIES_5000A, + SERIES_5000B, + SERIES_5000D, + SERIES_5000DMSO, + SERIES_6x0xE, //6000 series with 8 bit resolution only + SERIES_6x2xE, //6000 series with FlexRes + + SERIES_UNKNOWN //unknown or invalid model name + }; bool IsDigitalPodPresent(size_t npod); bool IsDigitalPodActive(size_t npod); @@ -185,6 +230,9 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope bool CanEnableChannel5000Series16Bit(size_t i); bool CanEnableChannel4000Series12Bit(size_t i); bool CanEnableChannel4000Series14Bit(size_t i); + bool CanEnableChannel3000Series8Bit(size_t i); + bool CanEnableChannel3000Series10Bit(size_t i); + bool CanEnableChannel2000Series8Bit(size_t i); std::string GetChannelColor(size_t i); @@ -214,16 +262,16 @@ class PicoOscilloscope : public virtual RemoteBridgeOscilloscope uint32_t m_awgBufferSize; //Scope features - uint32_t m_BandwidthLimitLow; - uint32_t m_BandwidthLimitHigh; int m_adcBits; int m_picoSeries; - int m_picoMinAdc; bool m_picoHas50ohm; bool m_picoHasAwg; bool m_picoHasExttrig; bool m_picoHasBwlimiter; std::vector m_adcModes; + std::vector m_BandwidthLimits; + + Series m_series; ///@brief Buffers for storing raw ADC samples before converting to fp32 std::vector > > m_analogRawWaveformBuffers;