From a5e17d1429f65cd4ab4d1d46a0706f8503cf4890 Mon Sep 17 00:00:00 2001
From: fredzo <fborry@free.fr>
Date: Sat, 31 Aug 2024 20:01:13 +0200
Subject: [PATCH 1/5] Fixed DHO1000 and DHO4000 detection. Fixed bandwidth
 calculation for DHO80x.

---
 scopehal/RigolOscilloscope.cpp | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/scopehal/RigolOscilloscope.cpp b/scopehal/RigolOscilloscope.cpp
index 4f5129d9..07a9c109 100644
--- a/scopehal/RigolOscilloscope.cpp
+++ b/scopehal/RigolOscilloscope.cpp
@@ -107,16 +107,27 @@ RigolOscilloscope::RigolOscilloscope(SCPITransport* transport)
 
 		m_transport->SendCommandQueued("CHAN1:BWL " + originalBandwidthLimit);
 	}
-	else if(1 == sscanf(m_model.c_str(), "DHO%d", &m_modelNumber) && (m_modelNumber < 1000))
-	{
+	else if(1 == sscanf(m_model.c_str(), "DHO%d", &m_modelNumber) && (m_modelNumber < 5000))
+	{	// Model numbers are :
+		// - DHO802 (70MHz), DHO804 (70Mhz), DHO812 (100MHz),DHO814 (100MHz)
+	    // - DHO914/DHO914S (125MHz), DHO924/DHO924S (250MHz)
+		// - DHO1072 (70MHz), DHO1074 (70MHz), DHO1102 (100MHz), DHO1104 (100MHz), DHO1202 (200MHz), DHO1204 (200MHz)
+		// - DHO4204 (200MHz), DHO4404 (400 MHz), DHO4804 (800MHz)
 		m_protocol = DHO;
 
 		int model_multiplicator = 100;
-		if(m_modelNumber > 900)	   // special handling of DHO900 series
-		{
+		int model_modulo = 100;
+		if(m_modelNumber > 1000)
+		{	// DHO1000 and 4000
+			model_multiplicator = 10;
+			model_modulo = 1000;
+		}
+		else if(m_modelNumber > 900)
+		{	// special handling of DHO900 series
 			model_multiplicator = 125;
 		}
-		m_bandwidth = m_modelNumber % 100 / 10 * model_multiplicator;	 // should also work for DHO1000/DHO4000
+		m_bandwidth = m_modelNumber % model_modulo / 10 * model_multiplicator;
+		if(m_bandwidth == 0) m_bandwidth = 70; // Fallback for DHO80x models
 
 		m_opt200M = false;	  // does not exist in 800/900 series
 	}

From 5112c561f68bf4f4b04119095e829a41274accc8 Mon Sep 17 00:00:00 2001
From: fredzo <fborry@free.fr>
Date: Sun, 1 Sep 2024 01:32:42 +0200
Subject: [PATCH 2/5] Fixed dho support for high memory depth by using
 pagination. Added live mode for dho with memory depth = 1k for better
 performance.

---
 scopehal/RigolOscilloscope.cpp | 56 +++++++++++++++++++++++++++++++---
 scopehal/RigolOscilloscope.h   |  4 +++
 2 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/scopehal/RigolOscilloscope.cpp b/scopehal/RigolOscilloscope.cpp
index 07a9c109..02b76650 100644
--- a/scopehal/RigolOscilloscope.cpp
+++ b/scopehal/RigolOscilloscope.cpp
@@ -49,6 +49,7 @@ RigolOscilloscope::RigolOscilloscope(SCPITransport* transport)
 	, m_triggerArmed(false)
 	, m_triggerWasLive(false)
 	, m_triggerOneShot(false)
+	, m_liveMode(false)
 {
 	//Last digit of the model number is the number of channels
 	if(1 == sscanf(m_model.c_str(), "DS%d", &m_modelNumber))
@@ -664,6 +665,9 @@ void RigolOscilloscope::SetChannelOffset(size_t i, size_t /*stream*/, float offs
 
 Oscilloscope::TriggerMode RigolOscilloscope::PollTrigger()
 {
+	if(m_liveMode)
+		return TRIGGER_MODE_TRIGGERED;
+
 	auto stat = Trim(m_transport->SendCommandQueuedWithReply(":TRIG:STAT?"));
 
 	if(stat != "STOP")
@@ -719,6 +723,11 @@ bool RigolOscilloscope::AcquireData()
 		maxpoints = 8192;	 // FIXME
 	else if(m_protocol == MSO5)
 		maxpoints = GetSampleDepth();	 //You can use 250E6 points too, but it is very slow
+	else if(m_protocol == DHO && !m_liveMode)
+	{	// DHO models need to set raw mode off and on again to reset the number of points according to the current memory depth
+		m_transport->SendCommandQueued(":WAV:MODE NORM");
+		m_transport->SendCommandQueued(":WAV:MODE RAW");
+	}
 	unsigned char* temp_buf = new unsigned char[maxpoints + 1];
 	map<int, vector<UniformAnalogWaveform*>> pending_waveforms;
 	for(size_t i = 0; i < m_analogChannelCount; i++)
@@ -765,8 +774,12 @@ bool RigolOscilloscope::AcquireData()
 				&yincrement,
 				&yorigin,
 				&yreference);
+			if(sec_per_sample == 0)
+			{	// Sometimes the scope might return a null value for xincrement => replace it with a dummy value to prenvent an Arithmetic exception in WaveformArea::RasterizeAnalogOrDigitalWaveform 
+				sec_per_sample = 0.001;
+			}
 			fs_per_sample = round(sec_per_sample * FS_PER_SECOND);
-			//LogDebug("X: %d points, %f origin, ref %f fs/sample %ld\n", npoints, xorigin, xreference, fs_per_sample);
+			//LogDebug("X: %d points, %f origin, ref %f fs/sample %ld\n", (int) npoints, xorigin, xreference, (long int) fs_per_sample);
 			//LogDebug("Y: %f inc, %f origin, %f ref\n", yincrement, yorigin, yreference);
 		}
 
@@ -786,7 +799,7 @@ bool RigolOscilloscope::AcquireData()
 		//Downloading the waveform is a pain in the butt, because we can only pull 250K points at a time! (Unless you have a MSO5)
 		for(size_t npoint = 0; npoint < npoints;)
 		{
-			if(m_protocol == MSO5 || m_protocol == DHO)
+			if(m_protocol == MSO5)
 			{
 				//Ask for the data block
 				m_transport->SendCommandQueued("*WAI");
@@ -910,8 +923,11 @@ bool RigolOscilloscope::AcquireData()
 		}
 		else
 		{
-			m_transport->SendCommandQueued(":SING");
-			m_transport->SendCommandQueued("*WAI");
+			if(!m_liveMode)
+			{
+				m_transport->SendCommandQueued(":SING");
+				m_transport->SendCommandQueued("*WAI");
+			}
 		}
 		m_triggerArmed = true;
 	}
@@ -921,6 +937,15 @@ bool RigolOscilloscope::AcquireData()
 	return true;
 }
 
+void RigolOscilloscope::StopLiveMode()
+{
+	if(m_liveMode)
+	{	// Stop live mode
+		m_transport->SendCommandQueued(":WAV:MODE RAW");
+		m_liveMode = false;
+	}
+}
+
 void RigolOscilloscope::Start()
 {
 	//LogDebug("Start single trigger\n");
@@ -929,6 +954,24 @@ void RigolOscilloscope::Start()
 		m_transport->SendCommandQueued(":TRIG:EDGE:SWE SING");
 		m_transport->SendCommandQueued(":RUN");
 	}
+	else if(m_protocol == DHO)
+	{	// Check for memory depth : if it is 1k, switch to live mode for better performance
+		m_mdepthValid = false;
+		GetSampleDepth();
+		m_liveMode = (m_mdepth == 1000);
+		if(m_liveMode)
+		{
+			m_transport->SendCommandQueued(":WAV:MODE NORM");
+			m_transport->SendCommandQueued(":RUN");
+			m_transport->SendCommandQueued("*WAI");
+		}
+		else
+		{
+			m_transport->SendCommandQueued(":WAV:MODE RAW");
+			m_transport->SendCommandQueued(":SING");
+			m_transport->SendCommandQueued("*WAI");
+		}
+	}
 	else
 	{
 		m_transport->SendCommandQueued(":SING");
@@ -947,6 +990,7 @@ void RigolOscilloscope::StartSingleTrigger()
 	}
 	else
 	{
+		StopLiveMode();
 		m_transport->SendCommandQueued(":SING");
 		m_transport->SendCommandQueued("*WAI");
 	}
@@ -957,6 +1001,7 @@ void RigolOscilloscope::StartSingleTrigger()
 void RigolOscilloscope::Stop()
 {
 	m_transport->SendCommandQueued(":STOP");
+	StopLiveMode();
 	m_triggerArmed = false;
 	m_triggerOneShot = true;
 }
@@ -964,7 +1009,10 @@ void RigolOscilloscope::Stop()
 void RigolOscilloscope::ForceTrigger()
 {
 	if(m_protocol == DS || m_protocol == DHO)
+	{
+		StopLiveMode();
 		m_transport->SendCommandQueued(":TFOR");
+	}
 	else
 		LogError("RigolOscilloscope::ForceTrigger not implemented for this model\n");
 }
diff --git a/scopehal/RigolOscilloscope.h b/scopehal/RigolOscilloscope.h
index 3d3c21b3..982d3256 100644
--- a/scopehal/RigolOscilloscope.h
+++ b/scopehal/RigolOscilloscope.h
@@ -125,6 +125,8 @@ class RigolOscilloscope : public virtual SCPIOscilloscope
 	bool m_triggerWasLive;
 	bool m_triggerOneShot;
 
+	bool m_liveMode;
+
 	int m_modelNumber;
 	unsigned int m_bandwidth;
 	bool m_opt200M;
@@ -133,6 +135,8 @@ class RigolOscilloscope : public virtual SCPIOscilloscope
 	void PushEdgeTrigger(EdgeTrigger* trig);
 	void PullEdgeTrigger();
 
+	void StopLiveMode();
+
 public:
 	static std::string GetDriverNameInternal();
 	OSCILLOSCOPE_INITPROC(RigolOscilloscope)

From 6b11d295004cef62afcae16ab51676122ec89348 Mon Sep 17 00:00:00 2001
From: fredzo <fborry@free.fr>
Date: Sun, 1 Sep 2024 11:35:00 +0200
Subject: [PATCH 3/5] Fixed live mode / memory mode switch management for dho
 models

---
 scopehal/RigolOscilloscope.cpp | 54 ++++++++++++++++++----------------
 scopehal/RigolOscilloscope.h   |  2 +-
 2 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/scopehal/RigolOscilloscope.cpp b/scopehal/RigolOscilloscope.cpp
index 02b76650..35c3e87e 100644
--- a/scopehal/RigolOscilloscope.cpp
+++ b/scopehal/RigolOscilloscope.cpp
@@ -723,11 +723,7 @@ bool RigolOscilloscope::AcquireData()
 		maxpoints = 8192;	 // FIXME
 	else if(m_protocol == MSO5)
 		maxpoints = GetSampleDepth();	 //You can use 250E6 points too, but it is very slow
-	else if(m_protocol == DHO && !m_liveMode)
-	{	// DHO models need to set raw mode off and on again to reset the number of points according to the current memory depth
-		m_transport->SendCommandQueued(":WAV:MODE NORM");
-		m_transport->SendCommandQueued(":WAV:MODE RAW");
-	}
+
 	unsigned char* temp_buf = new unsigned char[maxpoints + 1];
 	map<int, vector<UniformAnalogWaveform*>> pending_waveforms;
 	for(size_t i = 0; i < m_analogChannelCount; i++)
@@ -779,6 +775,10 @@ bool RigolOscilloscope::AcquireData()
 				sec_per_sample = 0.001;
 			}
 			fs_per_sample = round(sec_per_sample * FS_PER_SECOND);
+			if(m_protocol == DHO)
+			{	// DHO models return page size instead of memory depth when paginating
+				npoints = GetSampleDepth();
+			}
 			//LogDebug("X: %d points, %f origin, ref %f fs/sample %ld\n", (int) npoints, xorigin, xreference, (long int) fs_per_sample);
 			//LogDebug("Y: %f inc, %f origin, %f ref\n", yincrement, yorigin, yreference);
 		}
@@ -937,12 +937,21 @@ bool RigolOscilloscope::AcquireData()
 	return true;
 }
 
-void RigolOscilloscope::StopLiveMode()
+void RigolOscilloscope::PrepareStart()
 {
-	if(m_liveMode)
-	{	// Stop live mode
-		m_transport->SendCommandQueued(":WAV:MODE RAW");
-		m_liveMode = false;
+	if(m_protocol == DHO)
+	{
+		// DHO models need to set raw mode off and on again or vice versa to reset the number of points according to the current memory depth
+		if(m_liveMode)
+		{
+			m_transport->SendCommandQueued(":WAV:MODE RAW");
+			m_transport->SendCommandQueued(":WAV:MODE NORM");
+		}
+		else
+		{
+			m_transport->SendCommandQueued(":WAV:MODE NORM");
+			m_transport->SendCommandQueued(":WAV:MODE RAW");
+		}
 	}
 }
 
@@ -959,18 +968,9 @@ void RigolOscilloscope::Start()
 		m_mdepthValid = false;
 		GetSampleDepth();
 		m_liveMode = (m_mdepth == 1000);
-		if(m_liveMode)
-		{
-			m_transport->SendCommandQueued(":WAV:MODE NORM");
-			m_transport->SendCommandQueued(":RUN");
-			m_transport->SendCommandQueued("*WAI");
-		}
-		else
-		{
-			m_transport->SendCommandQueued(":WAV:MODE RAW");
-			m_transport->SendCommandQueued(":SING");
-			m_transport->SendCommandQueued("*WAI");
-		}
+		PrepareStart();
+		m_transport->SendCommandQueued(m_liveMode ? ":RUN" : ":SING");
+		m_transport->SendCommandQueued("*WAI");
 	}
 	else
 	{
@@ -983,6 +983,9 @@ void RigolOscilloscope::Start()
 
 void RigolOscilloscope::StartSingleTrigger()
 {
+	m_liveMode = false;
+	m_mdepthValid = false; // Memory depth might have been changed on scope
+	PrepareStart();
 	if(m_protocol == DS_OLD)
 	{
 		m_transport->SendCommandQueued(":TRIG:EDGE:SWE SING");
@@ -990,7 +993,6 @@ void RigolOscilloscope::StartSingleTrigger()
 	}
 	else
 	{
-		StopLiveMode();
 		m_transport->SendCommandQueued(":SING");
 		m_transport->SendCommandQueued("*WAI");
 	}
@@ -1001,16 +1003,18 @@ void RigolOscilloscope::StartSingleTrigger()
 void RigolOscilloscope::Stop()
 {
 	m_transport->SendCommandQueued(":STOP");
-	StopLiveMode();
+	m_liveMode = false;
 	m_triggerArmed = false;
 	m_triggerOneShot = true;
 }
 
 void RigolOscilloscope::ForceTrigger()
 {
+	m_liveMode = false;
+	m_mdepthValid = false; // Memory depth might have been changed on scope
+	PrepareStart();
 	if(m_protocol == DS || m_protocol == DHO)
 	{
-		StopLiveMode();
 		m_transport->SendCommandQueued(":TFOR");
 	}
 	else
diff --git a/scopehal/RigolOscilloscope.h b/scopehal/RigolOscilloscope.h
index 982d3256..4fe139de 100644
--- a/scopehal/RigolOscilloscope.h
+++ b/scopehal/RigolOscilloscope.h
@@ -135,7 +135,7 @@ class RigolOscilloscope : public virtual SCPIOscilloscope
 	void PushEdgeTrigger(EdgeTrigger* trig);
 	void PullEdgeTrigger();
 
-	void StopLiveMode();
+	void PrepareStart();
 
 public:
 	static std::string GetDriverNameInternal();

From 2c9c148d64453efd9201b689fe39b0d8e984eea4 Mon Sep 17 00:00:00 2001
From: fredzo <fborry@free.fr>
Date: Sun, 1 Sep 2024 12:13:17 +0200
Subject: [PATCH 4/5] Added LogWarn on null sec_per_sample value.

---
 scopehal/RigolOscilloscope.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/scopehal/RigolOscilloscope.cpp b/scopehal/RigolOscilloscope.cpp
index 35c3e87e..600a7bbb 100644
--- a/scopehal/RigolOscilloscope.cpp
+++ b/scopehal/RigolOscilloscope.cpp
@@ -772,6 +772,7 @@ bool RigolOscilloscope::AcquireData()
 				&yreference);
 			if(sec_per_sample == 0)
 			{	// Sometimes the scope might return a null value for xincrement => replace it with a dummy value to prenvent an Arithmetic exception in WaveformArea::RasterizeAnalogOrDigitalWaveform 
+				LogWarning("Got null sec_per_sample value from the scope, forcing it to a dummy non null value to prevent Arithmetic exception.\n");
 				sec_per_sample = 0.001;
 			}
 			fs_per_sample = round(sec_per_sample * FS_PER_SECOND);
@@ -1014,9 +1015,7 @@ void RigolOscilloscope::ForceTrigger()
 	m_mdepthValid = false; // Memory depth might have been changed on scope
 	PrepareStart();
 	if(m_protocol == DS || m_protocol == DHO)
-	{
 		m_transport->SendCommandQueued(":TFOR");
-	}
 	else
 		LogError("RigolOscilloscope::ForceTrigger not implemented for this model\n");
 }

From 2d3df11ec35cb39b447a2dbe9632f46c215c5253 Mon Sep 17 00:00:00 2001
From: fredzo <fborry@free.fr>
Date: Sun, 1 Sep 2024 22:15:51 +0200
Subject: [PATCH 5/5] Limit the use of live mode to one channel setup to
 prevent getting waveforms from different triggers on several channels.

---
 scopehal/Oscilloscope.cpp      | 12 ++++++++++++
 scopehal/Oscilloscope.h        |  7 +++++++
 scopehal/RigolOscilloscope.cpp | 14 +++++++++++---
 3 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/scopehal/Oscilloscope.cpp b/scopehal/Oscilloscope.cpp
index b2e476e0..0609a91e 100644
--- a/scopehal/Oscilloscope.cpp
+++ b/scopehal/Oscilloscope.cpp
@@ -119,6 +119,18 @@ bool Oscilloscope::CanEnableChannel(size_t /*i*/)
 	return true;
 }
 
+int Oscilloscope::GetEnabledChannelCount()
+{
+	int result = 0;
+	for(size_t i=0; i<GetChannelCount(); i++)
+	{
+		if(IsChannelEnabled(i))
+			result++;
+	}
+	return result;
+}
+
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Triggering helpers
 
diff --git a/scopehal/Oscilloscope.h b/scopehal/Oscilloscope.h
index da35c3b6..a6575cc6 100644
--- a/scopehal/Oscilloscope.h
+++ b/scopehal/Oscilloscope.h
@@ -132,6 +132,13 @@ class Oscilloscope	: public virtual Instrument
 	 */
 	virtual void DisableChannel(size_t i) =0;
 
+	/**
+		@brief Returns the number of enabled channels for this oscilloscope.
+
+		@return the number of enabled channels for this oscilloscope.
+	 */
+	int GetEnabledChannelCount();
+
 	/**
 		@brief Gets a channel given the hardware name
 	 */
diff --git a/scopehal/RigolOscilloscope.cpp b/scopehal/RigolOscilloscope.cpp
index 600a7bbb..7ab02403 100644
--- a/scopehal/RigolOscilloscope.cpp
+++ b/scopehal/RigolOscilloscope.cpp
@@ -966,9 +966,17 @@ void RigolOscilloscope::Start()
 	}
 	else if(m_protocol == DHO)
 	{	// Check for memory depth : if it is 1k, switch to live mode for better performance
-		m_mdepthValid = false;
-		GetSampleDepth();
-		m_liveMode = (m_mdepth == 1000);
+		// Limit live mode to one channel setup to prevent grabbing waveforms from to different triggers on seperate channels
+		if(GetEnabledChannelCount()==1)
+		{
+			m_mdepthValid = false;
+			GetSampleDepth();
+			m_liveMode = (m_mdepth == 1000);
+		}
+		else
+		{
+			m_liveMode = false;
+		}
 		PrepareStart();
 		m_transport->SendCommandQueued(m_liveMode ? ":RUN" : ":SING");
 		m_transport->SendCommandQueued("*WAI");