diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..70abd31e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "mutex": "cpp" + } +} \ No newline at end of file diff --git a/scopehal/RigolOscilloscope.cpp b/scopehal/RigolOscilloscope.cpp index 5adadec6..cf000de4 100644 --- a/scopehal/RigolOscilloscope.cpp +++ b/scopehal/RigolOscilloscope.cpp @@ -35,23 +35,31 @@ using namespace std; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Construction / destruction -RigolOscilloscope::RigolOscilloscope(SCPITransport* transport) - : SCPIOscilloscope(transport) - , m_triggerArmed(false) - , m_triggerOneShot(false) +RigolOscilloscope::RigolOscilloscope(SCPITransport *transport) + : SCPIOscilloscope(transport), m_triggerArmed(false), m_triggerOneShot(false) { //Last digit of the model number is the number of channels - int model_number; - if(1 != sscanf(m_model.c_str(), "DS%d", &model_number)) + if (1 != sscanf(m_model.c_str(), "DS%d", &model_number)) { - if(1 != sscanf(m_model.c_str(), "MSO%d", &model_number)) + if (1 != sscanf(m_model.c_str(), "MSO%d", &model_number)) { LogError("Bad model number\n"); return; } + else + { + model_name = "MSO"; + protocol = MSO5; + } + } + else + { + model_name = "DS"; + protocol = MSO5; } + int nchans = model_number % 10; - for(int i=0; iSendCommand("WAV:FORM BYTE"); m_transport->SendCommand("WAV:MODE RAW"); + if (protocol == MSO5) + for (int i = 0; i < 4; i++) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":VERN ON"); } RigolOscilloscope::~RigolOscilloscope() @@ -141,23 +152,23 @@ void RigolOscilloscope::FlushConfigCache() bool RigolOscilloscope::IsChannelEnabled(size_t i) { //ext trigger should never be displayed - if(i == m_extTrigChannel->GetIndex()) + if (i == m_extTrigChannel->GetIndex()) return false; //TODO: handle digital channels, for now just claim they're off - if(i >= m_analogChannelCount) + if (i >= m_analogChannelCount) return false; lock_guard lock(m_cacheMutex); - if(m_channelsEnabled.find(i) != m_channelsEnabled.end()) + if (m_channelsEnabled.find(i) != m_channelsEnabled.end()) return m_channelsEnabled[i]; lock_guard lock2(m_mutex); m_transport->SendCommand(m_channels[i]->GetHwname() + ":DISP?"); string reply = m_transport->ReadReply(); - if(reply == "0") + if (reply == "0") { m_channelsEnabled[i] = false; return false; @@ -188,9 +199,9 @@ OscilloscopeChannel::CouplingType RigolOscilloscope::GetChannelCoupling(size_t i m_transport->SendCommand(m_channels[i]->GetHwname() + ":COUP?"); string reply = m_transport->ReadReply(); - if(reply == "AC") + if (reply == "AC") return OscilloscopeChannel::COUPLE_AC_1M; - else if(reply == "DC") + else if (reply == "DC") return OscilloscopeChannel::COUPLE_DC_1M; else /* if(reply == "GND") */ return OscilloscopeChannel::COUPLE_GND; @@ -199,22 +210,22 @@ OscilloscopeChannel::CouplingType RigolOscilloscope::GetChannelCoupling(size_t i void RigolOscilloscope::SetChannelCoupling(size_t i, OscilloscopeChannel::CouplingType type) { lock_guard lock(m_mutex); - switch(type) + switch (type) { - case OscilloscopeChannel::COUPLE_AC_1M: - m_transport->SendCommand(m_channels[i]->GetHwname() + ":COUP AC"); - break; + case OscilloscopeChannel::COUPLE_AC_1M: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":COUP AC"); + break; - case OscilloscopeChannel::COUPLE_DC_1M: - m_transport->SendCommand(m_channels[i]->GetHwname() + ":COUP DC"); - break; + case OscilloscopeChannel::COUPLE_DC_1M: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":COUP DC"); + break; - case OscilloscopeChannel::COUPLE_GND: - m_transport->SendCommand(m_channels[i]->GetHwname() + ":COUP GND"); - break; + case OscilloscopeChannel::COUPLE_GND: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":COUP GND"); + break; - default: - LogError("Invalid coupling for channel\n"); + default: + LogError("Invalid coupling for channel\n"); } } @@ -230,9 +241,95 @@ double RigolOscilloscope::GetChannelAttenuation(size_t i) return atten; } -void RigolOscilloscope::SetChannelAttenuation(size_t /*i*/, double /*atten*/) +void RigolOscilloscope::SetChannelAttenuation(size_t i, double atten) { - //FIXME + lock_guard lock(m_mutex); + switch ((int)(atten * 10000 + 0.1)) // + 0.1 in case atten is for example 0.049999 or so, to round it to 0.05 which turns to an int of 500 + { + case 1: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.0001"); + break; + case 2: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.0002"); + break; + case 5: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.0005"); + break; + case 10: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.001"); + break; + case 20: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.002"); + break; + case 50: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.005"); + break; + case 100: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.01"); + break; + case 200: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.02"); + break; + case 500: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.05"); + break; + case 1000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.1"); + break; + case 2000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.2"); + break; + case 5000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 0.5"); + break; + case 10000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 1"); + break; + case 20000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 2"); + break; + case 50000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 5"); + break; + case 100000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 10"); + break; + case 200000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 20"); + break; + case 500000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 50"); + break; + case 1000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 100"); + break; + case 2000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 200"); + break; + case 5000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 500"); + break; + case 10000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 1000"); + break; + case 20000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 2000"); + break; + case 50000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 5000"); + break; + case 100000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 10000"); + break; + case 200000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 20000"); + break; + case 500000000: + m_transport->SendCommand(m_channels[i]->GetHwname() + ":PROB 50000"); + break; + default: + LogError("Invalid attenuation for channel\n"); + } } int RigolOscilloscope::GetChannelBandwidthLimit(size_t i) @@ -241,43 +338,94 @@ int RigolOscilloscope::GetChannelBandwidthLimit(size_t i) m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL?"); string reply = m_transport->ReadReply(); - if(reply == "20M") + if (reply == "20M") return 20; else return 0; } -void RigolOscilloscope::SetChannelBandwidthLimit(size_t /*i*/, unsigned int /*limit_mhz*/) +void RigolOscilloscope::SetChannelBandwidthLimit(size_t i, unsigned int limit_mhz) { //FIXME + lock_guard lock(m_mutex); + + if (model_name == "MSO" & (model_number / 1000) == 5) + { + switch (model_number % 1000 / 10) + { + case 7: + case 10: + if (limit_mhz <= 20) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL 20M"); + else + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL OFF"); + break; + case 20: + if (limit_mhz <= 20) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL 20M"); + else if (limit_mhz <= 100) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL 100M"); + else + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL OFF"); + break; + case 35: + if (limit_mhz <= 20) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL 20M"); + else if (limit_mhz <= 100) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL 100M"); + else if (limit_mhz <= 200) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL 200M"); + else + m_transport->SendCommand(m_channels[i]->GetHwname() + ":BWL OFF"); + break; + default: + LogError("Invalid model number\n"); + } + } + else + LogError("Invalid model for Bandwidth Limit\n"); } double RigolOscilloscope::GetChannelVoltageRange(size_t i) { { lock_guard lock(m_cacheMutex); - if(m_channelVoltageRanges.find(i) != m_channelVoltageRanges.end()) + if (m_channelVoltageRanges.find(i) != m_channelVoltageRanges.end()) return m_channelVoltageRanges[i]; } lock_guard lock2(m_mutex); - m_transport->SendCommand(m_channels[i]->GetHwname() + ":RANGE?"); + if (protocol == DS) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":RANGE?"); + else if (protocol == MSO5) + m_transport->SendCommand(m_channels[i]->GetHwname() + ":SCALE?"); string reply = m_transport->ReadReply(); double range; sscanf(reply.c_str(), "%lf", &range); lock_guard lock(m_cacheMutex); + if (protocol == MSO5) + range = 8 * range; m_channelVoltageRanges[i] = range; return range; } -void RigolOscilloscope::SetChannelVoltageRange(size_t /*i*/, double /*range*/) +void RigolOscilloscope::SetChannelVoltageRange(size_t i, double range) { + lock_guard lock(m_mutex); + char buf[128]; + if (protocol == DS) + snprintf(buf, sizeof(buf), "%s:RANGE %f", m_channels[i]->GetHwname().c_str(), range); + else if (protocol == MSO5) + snprintf(buf, sizeof(buf), "%s:SCALE %f", m_channels[i]->GetHwname().c_str(), range / 8); + m_transport->SendCommand(buf); + m_channelVoltageRanges[i] = range; + //FIXME } -OscilloscopeChannel* RigolOscilloscope::GetExternalTrigger() +OscilloscopeChannel *RigolOscilloscope::GetExternalTrigger() { //FIXME return NULL; @@ -288,7 +436,7 @@ double RigolOscilloscope::GetChannelOffset(size_t i) { lock_guard lock(m_cacheMutex); - if(m_channelOffsets.find(i) != m_channelOffsets.end()) + if (m_channelOffsets.find(i) != m_channelOffsets.end()) return m_channelOffsets[i]; } @@ -304,9 +452,12 @@ double RigolOscilloscope::GetChannelOffset(size_t i) return offset; } -void RigolOscilloscope::SetChannelOffset(size_t /*i*/, double /*offset*/) +void RigolOscilloscope::SetChannelOffset(size_t i, double offset) { - //FIXME + lock_guard lock(m_mutex); + char buf[128]; + snprintf(buf, sizeof(buf), "%s:OFFS %f", m_channels[i]->GetHwname().c_str(), offset); + m_transport->SendCommand(buf); } void RigolOscilloscope::ResetTriggerConditions() @@ -324,20 +475,20 @@ Oscilloscope::TriggerMode RigolOscilloscope::PollTrigger() m_transport->SendCommand("TRIG:STAT?"); string stat = m_transport->ReadReply(); - if(stat == "TD") + if (stat == "TD") return TRIGGER_MODE_TRIGGERED; - else if(stat == "RUN") + else if (stat == "RUN") return TRIGGER_MODE_RUN; - else if(stat == "WAIT") + else if (stat == "WAIT") return TRIGGER_MODE_WAIT; - else if(stat == "AUTO") + else if (stat == "AUTO") return TRIGGER_MODE_AUTO; else { //The "TD" state is not sticky on Rigol scopes, unlike the equivalent LeCroy status register bit. //The scope will go from "run" to "stop" state on trigger with only a momentary pass through "TD". //If we armed the trigger recently and we're now stopped, this means we must have triggered. - if(m_triggerArmed) + if (m_triggerArmed) { m_triggerArmed = false; return TRIGGER_MODE_TRIGGERED; @@ -372,14 +523,18 @@ bool RigolOscilloscope::AcquireData(bool toQueue) double yincrement; double yorigin; double yreference; - size_t maxpoints = 250*1000; - unsigned char* temp_buf = new unsigned char[maxpoints]; - map > pending_waveforms; - for(size_t i=0; i> pending_waveforms; + for (size_t i = 0; i < m_analogChannelCount; i++) { - if(!enabled[i]) + if (!enabled[i]) { - if(!toQueue) + if (!toQueue) m_channels[i]->SetData(NULL); continue; } @@ -393,22 +548,22 @@ bool RigolOscilloscope::AcquireData(bool toQueue) string reply = m_transport->ReadReply(); //LogDebug("Preamble = %s\n", reply.c_str()); sscanf(reply.c_str(), "%d,%d,%d,%d,%lf,%lf,%lf,%lf,%lf,%lf", - &unused1, - &unused2, - &npoints, - &unused3, - &sec_per_sample, - &xorigin, - &xreference, - &yincrement, - &yorigin, - &yreference); + &unused1, + &unused2, + &npoints, + &unused3, + &sec_per_sample, + &xorigin, + &xreference, + &yincrement, + &yorigin, + &yreference); int64_t ps_per_sample = round(sec_per_sample * 1e12f); //LogDebug("X: %d points, %f origin, ref %f ps/sample %ld\n", npoints, xorigin, xreference, ps_per_sample); //LogDebug("Y: %f inc, %f origin, %f ref\n", yincrement, yorigin, yreference); //Set up the capture we're going to store our data into - AnalogWaveform* cap = new AnalogWaveform; + AnalogWaveform *cap = new AnalogWaveform; cap->m_timescale = ps_per_sample; cap->m_triggerPhase = 0; cap->m_startTimestamp = time(NULL); @@ -416,14 +571,14 @@ bool RigolOscilloscope::AcquireData(bool toQueue) cap->m_startPicoseconds = (t - floor(t)) * 1e12f; //Downloading the waveform is a pain in the butt, because we can only pull 250K points at a time! - for(size_t npoint=0; npoint < maxpoints; npoint += maxpoints) + for (size_t npoint = 0; npoint < maxpoints; npoint += maxpoints) { //Ask for the data char tmp[128]; - snprintf(tmp, sizeof(tmp), "WAV:STAR %zu", npoint + 1); //ONE based indexing WTF + snprintf(tmp, sizeof(tmp), "WAV:STAR %zu", npoint + 1); //ONE based indexing WTF m_transport->SendCommand(tmp); size_t end = npoint + maxpoints; - if(end > npoints) + if (end > npoints) end = npoints; snprintf(tmp, sizeof(tmp), "WAV:STOP %zu", end + 1); m_transport->SendCommand(tmp); @@ -439,25 +594,25 @@ bool RigolOscilloscope::AcquireData(bool toQueue) //size_t blocksize = end - npoints; //LogDebug("Block size = %zu\n", blocksize); size_t header_blocksize; - sscanf((char*)header, "#9%zu", &header_blocksize); + sscanf((char *)header, "#9%zu", &header_blocksize); //LogDebug("Header block size = %zu\n", header_blocksize); //Read actual block content and decode it //Scale: (value - Yorigin - Yref) * Yinc - m_transport->ReadRawData(header_blocksize + 1, temp_buf); //why is there a trailing byte here?? + m_transport->ReadRawData(header_blocksize + 1, temp_buf); //why is there a trailing byte here?? double ydelta = yorigin + yreference; cap->Resize(cap->m_samples.size() + header_blocksize); - for(size_t j=0; j(temp_buf[j]) - ydelta) * yincrement; //LogDebug("V = %.3f, temp=%d, delta=%f, inc=%f\n", v, temp_buf[j], ydelta, yincrement); - cap->m_offsets[npoint+j] = npoint+j; - cap->m_durations[npoint+j] = 1; - cap->m_samples[npoint+j] = v; + cap->m_offsets[npoint + j] = npoint + j; + cap->m_durations[npoint + j] = 1; + cap->m_samples[npoint + j] = v; } //Done, update the data - if(npoint == 0 && !toQueue) + if (npoint == 0 && !toQueue) m_channels[i]->SetData(cap); else pending_waveforms[i].push_back(cap); @@ -467,14 +622,14 @@ bool RigolOscilloscope::AcquireData(bool toQueue) //Now that we have all of the pending waveforms, save them in sets across all channels m_pendingWaveformsMutex.lock(); size_t num_pending = 0; - if(toQueue) //if saving to queue, the 0'th segment counts too - num_pending ++; - for(size_t i=0; iSendCommand("TRIG_MODE SINGLE"); + if (protocol == DS) + m_transport->SendCommand("TRIG_MODE SINGLE"); + else if (protocol == MSO5) + m_transport->SendCommand("SING"); m_triggerArmed = true; } @@ -531,7 +689,7 @@ size_t RigolOscilloscope::GetTriggerChannelIndex() { //Check cache //No locking, worst case we return a result a few seconds old - if(m_triggerChannelValid) + if (m_triggerChannelValid) return m_triggerChannel; lock_guard lock(m_mutex); @@ -539,13 +697,13 @@ size_t RigolOscilloscope::GetTriggerChannelIndex() //This is nasty because there are separate commands to see what the trigger source is //depending on what the trigger type is!!! //FIXME: For now assume edge - m_transport->SendCommand("TRIG:EDG:SOUR?"); + m_transport->SendCommand("TRIG:EDGE:SOUR?"); string ret = m_transport->ReadReply(); LogDebug("Trigger source: %s\n", ret.c_str()); - for(size_t i=0; iGetHwname() == ret) + if (m_channels[i]->GetHwname() == ret) { m_triggerChannel = i; m_triggerChannelValid = true; @@ -566,7 +724,7 @@ float RigolOscilloscope::GetTriggerVoltage() { //Check cache. //No locking, worst case we return a just-invalidated (but still fresh-ish) result. - if(m_triggerLevelValid) + if (m_triggerLevelValid) return m_triggerLevel; lock_guard lock(m_mutex); @@ -574,7 +732,7 @@ float RigolOscilloscope::GetTriggerVoltage() //This is nasty because there are separate commands to see what the trigger source is //depending on what the trigger type is!!! //FIXME: For now assume edge - m_transport->SendCommand("TRIG:EDG:LEV?"); + m_transport->SendCommand("TRIG:EDGE:LEV?"); string ret = m_transport->ReadReply(); double level; @@ -600,7 +758,7 @@ void RigolOscilloscope::SetTriggerType(Oscilloscope::TriggerType type) //FIXME } -void RigolOscilloscope::SetTriggerForChannel(OscilloscopeChannel* /*channel*/, vector /*triggerbits*/) +void RigolOscilloscope::SetTriggerForChannel(OscilloscopeChannel * /*channel*/, vector /*triggerbits*/) { //unimplemented, no LA support } diff --git a/scopehal/RigolOscilloscope.h b/scopehal/RigolOscilloscope.h index cf6801cc..449f8644 100644 --- a/scopehal/RigolOscilloscope.h +++ b/scopehal/RigolOscilloscope.h @@ -87,6 +87,11 @@ class RigolOscilloscope : public SCPIOscilloscope virtual void SetSampleRate(uint64_t rate); protected: + enum protocol_version { + MSO5, // MSO5000 series + DS, + }; + OscilloscopeChannel* m_extTrigChannel; //Mutexing for thread safety @@ -108,6 +113,10 @@ class RigolOscilloscope : public SCPIOscilloscope bool m_triggerArmed; bool m_triggerOneShot; + char *model_name; + int model_number; + protocol_version protocol; + public: static std::string GetDriverNameInternal(); OSCILLOSCOPE_INITPROC(RigolOscilloscope)