Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ngscopeclient/scopehal-pico-bridge
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6e6f13be2e33
Choose a base ref
...
head repository: ngscopeclient/scopehal-pico-bridge
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 9296b8ece446
Choose a head ref
  • 1 commit
  • 3 files changed
  • 1 contributor

Commits on Jan 28, 2021

  1. Implemented single-shot trigger and setting of timebase. No validity …

    …checking for what sample rates are legal under a given channel configuration.
    azonenberg committed Jan 28, 2021
    Copy the full SHA
    9296b8e View commit details
Showing with 121 additions and 33 deletions.
  1. +66 −12 src/ps6000d/ScpiServerThread.cpp
  2. +45 −21 src/ps6000d/WaveformServerThread.cpp
  3. +10 −0 src/ps6000d/ps6000d.h
78 changes: 66 additions & 12 deletions src/ps6000d/ScpiServerThread.cpp
Original file line number Diff line number Diff line change
@@ -57,9 +57,15 @@
RATES?
Returns a comma separated list of sampling rates (in femtoseconds)
RATE [num]
Sets sample rate
START
Arms the trigger
SINGLE
Arms the trigger in one-shot mode
STOP
Disarms the trigger
@@ -72,6 +78,7 @@

#include "ps6000d.h"
#include <string.h>
#include <math.h>

using namespace std;

@@ -86,16 +93,23 @@ map<size_t, PICO_CONNECT_PROBE_RANGE> g_range;
map<size_t, double> g_roundedRange;
map<size_t, double> g_offset;
map<size_t, PICO_BANDWIDTH_LIMITER> g_bandwidth;
size_t g_memDepth = 100000;
size_t g_memDepth = 1000000;
int64_t g_sampleInterval = 0; //in fs

//Copy of state at timestamp of last arm event
map<size_t, bool> g_channelOnDuringArm;
int64_t g_sampleIntervalDuringArm = 0;
size_t g_captureMemDepth = 0;

uint32_t g_timebase = 0;

volatile bool g_triggerArmed = false;
volatile bool g_triggerOneShot = false;

void UpdateChannel(size_t chan);

std::mutex g_mutex;

/**
@brief Sends a SCPI reply (terminated by newline)
*/
@@ -142,8 +156,6 @@ void ScpiServerThread()
g_bandwidth[i] = PICO_BW_FULL;
}

size_t maxTimebases = 100;

while(true)
{
Socket client = g_scpiSocket.Accept();
@@ -188,8 +200,18 @@ void ScpiServerThread()
{
string ret = "";

lock_guard<mutex> lock(g_mutex);

//Enumerate timebases
for(size_t i=0; i<maxTimebases; i++)
//Don't report every single legal timebase as there's way too many, the list box would be huge!
//Report the first nine, then go to larger steps
size_t vec[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
14, 29, 54, 104, 129, 254, 504, 629, 1254, 2504, 3129, 5004, 6254, 15629, 31254,
62504, 156254, 312504, 625004, 1562504
};
for(auto i : vec)
{
double intervalNs;
size_t maxSamples;
@@ -211,17 +233,20 @@ void ScpiServerThread()

else if(cmd == "ON")
{
lock_guard<mutex> lock(g_mutex);
g_channelOn[channelId] = true;
UpdateChannel(channelId);
}
else if(cmd == "OFF")
{
lock_guard<mutex> lock(g_mutex);
g_channelOn[channelId] = false;
UpdateChannel(channelId);
}

else if(cmd == "COUP")
else if( (cmd == "COUP") && (args.size() == 1) )
{
lock_guard<mutex> lock(g_mutex);
if(args[0] == "DC1M")
g_coupling[channelId] = PICO_DC;
else if(args[0] == "AC1M")
@@ -232,16 +257,22 @@ void ScpiServerThread()
UpdateChannel(channelId);
}

else if(cmd == "OFFS")
else if( (cmd == "OFFS") && (args.size() == 1) )
{
lock_guard<mutex> lock(g_mutex);
g_offset[channelId] = stod(args[0]);
UpdateChannel(channelId);
}

else if(cmd == "RANGE")
else if( (cmd == "RANGE") && (args.size() == 1) )
{
lock_guard<mutex> lock(g_mutex);
auto range = stod(args[0]);

//If 50 ohm coupling, cap hardware voltage range to 5V
if(g_coupling[channelId] == PICO_DC_50OHM)
range = min(range, 5.0);

if(range > 100)
{
g_range[channelId] = PICO_X1_PROBE_200V;
@@ -313,19 +344,39 @@ void ScpiServerThread()
g_roundedRange[channelId] = 0.01;
}

//LogDebug("Requested %f V full-scale range. Got %f\n", range, g_roundedRange[channelId]);

UpdateChannel(channelId);
}

else if(cmd == "START")
else if( (cmd == "RATE") && (args.size() == 1) )
{
//Convert sample rate to sample period
auto rate = stoull(args[0]);
g_sampleInterval = 1e15 / rate;
double period_ns = 1e9 / rate;

//Find closest timebase setting
double clkdiv = period_ns / 0.2;
int timebase;
if(period_ns < 5)
timebase = round(log(clkdiv)/log(2));
else
timebase = round(clkdiv) + 4;

g_timebase = timebase;
}

else if( (cmd == "START") || (cmd == "SINGLE") )
{
lock_guard<mutex> lock(g_mutex);

if(g_triggerArmed)
{
LogVerbose("Ignoring START command because trigger is already armed\n");
continue;
}

LogDebug("arming trigger\n");

bool anyChannels = false;
for(size_t i=0; i<g_numChannels; i++)
{
@@ -341,22 +392,25 @@ void ScpiServerThread()

g_channelOnDuringArm = g_channelOn;
g_captureMemDepth = g_memDepth;
g_sampleIntervalDuringArm = g_sampleInterval;

//Start the capture
auto status = ps6000aRunBlock(g_hScope, g_memDepth/2, g_memDepth/2, 2, NULL, 0, NULL, NULL);
auto status = ps6000aRunBlock(g_hScope, g_memDepth/2, g_memDepth/2, g_timebase, NULL, 0, NULL, NULL);
if(status != PICO_OK)
LogFatal("ps6000aRunBlock failed, code %d\n", status);

g_triggerArmed = true;
g_triggerOneShot = (cmd == "SINGLE");
}

else if(cmd == "STOP")
{
lock_guard<mutex> lock(g_mutex);

ps6000aStop(g_hScope);
g_triggerArmed = false;
}

//TODO: range
//TODO: bandwidth limiter

//Unknown
66 changes: 45 additions & 21 deletions src/ps6000d/WaveformServerThread.cpp
Original file line number Diff line number Diff line change
@@ -39,8 +39,6 @@ using namespace std;

volatile bool g_waveformThreadQuit = false;

//TODO: mutexing (driver is NOT thread safe!)

void WaveformServerThread()
{
Socket client = g_dataSocket.Accept();
@@ -55,6 +53,8 @@ void WaveformServerThread()
map<size_t, int16_t*> waveformBuffers;
for(size_t i=0; i<g_numChannels; i++)
{
lock_guard<mutex> lock(g_mutex);

//Allocate memory if needed
waveformBuffers[i] = new int16_t[g_memDepth];
memset(waveformBuffers[i], 0x00, g_memDepth * sizeof(int16_t));
@@ -68,38 +68,50 @@ void WaveformServerThread()
}
}

size_t numSamples = 0;
uint16_t numchans;
while(!g_waveformThreadQuit)
{
int16_t status;
ps6000aIsReady(g_hScope, &status);
{
lock_guard<mutex> lock(g_mutex);
ps6000aIsReady(g_hScope, &status);
}

if( (status == 0) || (!g_triggerArmed) )
{
usleep(1000);
continue;
}

//Stop the trigger
ps6000aStop(g_hScope);
{
lock_guard<mutex> lock(g_mutex);

//Download the data from the scope
size_t numSamples = g_captureMemDepth;
int16_t overflow = 0;
status = ps6000aGetValues(g_hScope, 0, &numSamples, 1, PICO_RATIO_MODE_RAW, 0, &overflow);
if(PICO_OK != status)
LogFatal("ps6000aGetValues (code %d)\n", status);
//Stop the trigger
ps6000aStop(g_hScope);

//Figure out how many channels are active in this capture
uint16_t numchans = 0;
for(size_t i=0; i<g_numChannels; i++)
{
if(g_channelOnDuringArm[i])
numchans ++;
//Download the data from the scope
numSamples = g_captureMemDepth;
int16_t overflow = 0;
status = ps6000aGetValues(g_hScope, 0, &numSamples, 1, PICO_RATIO_MODE_RAW, 0, &overflow);
if(PICO_OK != status)
LogFatal("ps6000aGetValues (code %d)\n", status);

//Figure out how many channels are active in this capture
numchans = 0;
for(size_t i=0; i<g_numChannels; i++)
{
if(g_channelOnDuringArm[i])
numchans ++;
}
}

//Send the channel count to the client
client.SendLooped((uint8_t*)&numchans, sizeof(numchans));

//Send sample rate to the client
client.SendLooped((uint8_t*)&g_sampleIntervalDuringArm, sizeof(g_sampleIntervalDuringArm));

//TODO: send overflow flags to client

//Send data for each channel to the client
@@ -118,9 +130,21 @@ void WaveformServerThread()
}
}

//Restart
status = ps6000aRunBlock(g_hScope, g_captureMemDepth/2, g_captureMemDepth/2, 2, NULL, 0, NULL, NULL);
if(status != PICO_OK)
LogFatal("ps6000aRunBlock failed, code %d\n", status);
//Re-arm the trigger if doing repeating triggers
if(g_triggerOneShot)
g_triggerArmed = false;
else
{
lock_guard<mutex> lock(g_mutex);

g_channelOnDuringArm = g_channelOn;
g_captureMemDepth = g_memDepth;
g_sampleIntervalDuringArm = g_sampleInterval;

//Restart
status = ps6000aRunBlock(g_hScope, g_captureMemDepth/2, g_captureMemDepth/2, g_timebase, NULL, 0, NULL, NULL);
if(status != PICO_OK)
LogFatal("ps6000aRunBlock in WaveformServerThread failed, code %d\n", status);
}
}
}
10 changes: 10 additions & 0 deletions src/ps6000d/ps6000d.h
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@
#include "../../lib/xptools/Socket.h"
#include <thread>
#include <map>
#include <mutex>

#include "/opt/picoscope/include/libps6000a/ps6000aApi.h"
#include "/opt/picoscope/include/libps6000a/PicoStatus.h"
@@ -61,7 +62,16 @@ extern volatile bool g_waveformThreadQuit;
extern size_t g_captureMemDepth;
extern size_t g_memDepth;
extern std::map<size_t, bool> g_channelOnDuringArm;
extern std::map<size_t, bool> g_channelOn;
extern std::map<size_t, double> g_roundedRange;

extern uint32_t g_timebase;
extern int64_t g_sampleInterval;
extern int64_t g_sampleIntervalDuringArm;

extern volatile bool g_triggerArmed;
extern volatile bool g_triggerOneShot;

extern std::mutex g_mutex;

#endif