colinoflynn / pico-python

PicoScope Python Interface
Other
102 stars 79 forks source link

Cannot get trigger to work #128

Closed hadisajj2 closed 6 years ago

hadisajj2 commented 6 years ago

I am using Picoscope 3406 D (uses p3000a sdk) I was able set a trigger and pickup a signal from my FPGA using the C code provided by pico tech.

I am trying to do the same in the Python code with the provided example (test_ps3000.py). However, the code doesn't seem to pick up any trigger. In fact, it should wait a timeout period and do an auto-trigger, but that seems to be faulty as well. In the end, I am trying to set a trigger on Channel A, and read power from Channel B. Even if I just want to do both trigger and read from Channel A, I can't.

The call to ps.waitReady() just keeps on waiting.

Anything I'm missing or doing wrong?

Code:

import matplotlib.pyplot as plt
import numpy as np
import time

from picoscope import ps3000a

#SERIAL_NUM = 'AR911/011\x00'
#ps = ps3000a.PS3000a(SERIAL_NUM)
ps = ps3000a.PS3000a()

# now = time.strftime("%Y%m%d_%H%M%S")
# filename = "sweep_" + now + ".swp"
# output_file = open(filename, "wb")

c = 3e8

# rapid block mode

ps.setChannel(channel="A", coupling="DC", VRange=1)
#ps.setChannel(channel="B", coupling="AC", VRange=1)  --> Commented out for now, but would need # #to get Channel B's readings

n_captures = 2300 * 3  # int(600 * 1.4)
sample_interval = 5 / 3e8
sample_duration = 1e3 * 2 / 3e8

ps.setSamplingInterval(sample_interval, sample_duration)

ps.setSimpleTrigger("A", threshold_V=0.1)
#ps.lib.ps3000SetTriggerChannelProperties(ps.handle, )

samples_per_segment = ps.memorySegments(n_captures)
ps.setNoOfCaptures(n_captures)

data = np.zeros((n_captures, samples_per_segment), dtype=np.int16)

t1 = time.time()

ps.runBlock()

ps.waitReady()

t2 = time.time()
print("Time to get sweep: " + str(t2 - t1))

ps.getDataRawBulk(data=data)

t3 = time.time()
print("Time to read data: " + str(t3 - t2))
plt.imshow(data[:, 0:ps.noSamples], aspect='auto', interpolation='none',
           cmap=plt.cm.hot)
plt.colorbar()
plt.show()

ps.close()
hmaarrfk commented 6 years ago

I'm sorry, I haven't used the picoscope in a while.

I have a feeling that you want to call the function getDataV.

hadisajj2 commented 6 years ago

Thanks for the response.

Are you suggesting to use getDataV in lieu of getDataRawBulk()? The test code (test_ps3000a.py) doesn't reach that far, it gets stuck waiting for a trigger.

hmaarrfk commented 6 years ago

Yeah, getDataRawBulk doesn't seem like something you should be doing the first time you use the library.

Maybe we can make that a hidden function or something.

hadisajj2 commented 6 years ago

Okay I will try it, thanks

hadisajj2 commented 6 years ago

Sorry to bug you again, but I tried getDataV and still have the same issue. The issue arises before the code hits the data collection line (getDataV or getDataRawBulk). The issue is that the trigger is never picked up, even though I was able to do it in C code with the same hardware/pico setup.

Perhaps there is sample code that works for other picoscopes? Maybe that will point me to the right direction.

(see code in bold, ps.waitReady())

Updated code

import matplotlib.pyplot as plt
import numpy as np
import time

from picoscope import ps3000a

ps = ps3000a.PS3000a()
c = 3e8

#rapid block mode

print(ps.handle)
print(ps.getAllUnitInfo())

ps.setChannel(channel="A", coupling="DC", VRange=1)
ps.setChannel(channel="B", coupling="AC", VRange=1)

n_captures = 2300 * 3  # int(600 * 1.4)
sample_interval = 5 / 3e8
sample_duration = 1e3 * 2 / 3e8

ps.setSamplingInterval(sample_interval, sample_duration)

ps.setSimpleTrigger("A", threshold_V=0.1)

samples_per_segment = ps.memorySegments(n_captures)
ps.setNoOfCaptures(n_captures)

data = np.zeros((n_captures, samples_per_segment), dtype=np.int16)

t1 = time.time()

ps.runBlock()

**ps.waitReady()** # execution waits here for a trigger. The trigger has proven to get other code (C code #to trigger)  

t2 = time.time()

print("Time to get sweep: " + str(t2 - t1))

ps.getDataV(channel = 'B', data=data)

t3 = time.time()
print("Time to read data: " + str(t3 - t2))

plt.imshow(data[:, 0:ps.noSamples], aspect='auto', interpolation='none',
           cmap=plt.cm.hot)

plt.colorbar()
plt.show()

ps.close()
hmaarrfk commented 6 years ago

Can you show us your C code? Most of what our code does is make the C code "pythonic".

Also please use tripple backticks "```" to correctly format code.

hadisajj2 commented 6 years ago

Yes, I will upload it in my git and provide you with a link and explanation in a bit. It is basically from picotech itself.

hadisajj2 commented 6 years ago

The C code is able to pick up a trigger from my FPGA when invoked through ChipWhisperer.

Under https://github.com/hadisajj2/ps3000a/blob/master/ps3000acon.c. In the main function, select voltage ranges for channels A and B (or just one channel to see the trigger).

Then select 'T' (collectBlockTriggered).

Going into the function calls, you will arrive at the setTrigger function. Here, I override picotech's code and with one simple line:

ps3000aSetSimpleTrigger(unit->handle, 1, 0, 0, 2, 0, 5000);

It basically looks at channel A rising with a 5 second wait.

Like 684 under blockDataHandler is where the code waits for a trigger. Through my ChipWhisperer's GUI, I run the code on my FPGA and my picoscope picks up the voltage variation and the C code fires a trigger.

Your help is much appreciated.

hmaarrfk commented 6 years ago

Sorry for not being able to be of much help, but you need to recreate a much smaller sample code that shows the underlying issue. Ideally, it would be only 10 lines long. For example, the python code you showed could obviate alot of the matplotlib calls as they aren't really necessary for reproducing your bug.

Are you actually trying to use Rapid Block Mode? If so, you actually need getDataRawBulk. Sorry for the misdirection.

From what I remember, Rapid Block Mode is much harder to use. Try with normal acquisition first. Look at https://github.com/colinoflynn/pico-python/blob/master/examples/sigGetBuiltIndemo.py Ignore the AWG part if you want, but it uses simple acquisition which is much easier to use!

hadisajj2 commented 6 years ago

Okay thanks, I will produce a smaller C code shortly!

hadisajj2 commented 6 years ago

Thanks again. I have the C code below. I've omitted some of the struct details, but take my word that it works! Below should give you a rough pseudo-real code of what I'm trying to achieve. The code below correctly identifies a trigger. This is not really code I wrote, but I took Pico-tech's C SDK examples and tried my best to water it down to as few lines as possible.


....define Unit...
...set BOOL g_ready= false..

UNIT unit; // this holds unit information such as handle etc.

openDevice(&unit);  //

setVoltages(&unit); // sets channel ranges , in this case only Channel A is set to a range of 200 mV

ps3000aSetSimpleTrigger(unit.handle, 1, 0, 0.2, 2, 0, 5000);

int16_t * buffers[PS3000A_MAX_CHANNEL_BUFFERS];

int32_t timeInterval;
int32_t sampleCount = BUFFER_SIZE;
int32_t maxSamples;
int32_t timeIndisposed;

PS3000A_RATIO_MODE ratioMode = PS3000A_RATIO_MODE_NONE;

buffers[0 * 2] = (int16_t*)calloc(sampleCount, sizeof(int16_t));
buffers[0 * 2 + 1] = (int16_t*)calloc(sampleCount, sizeof(int16_t));

ps3000aSetDataBuffers(unit.handle, (PS3000A_CHANNEL)0, buffers[0 * 2], buffers[0 * 2 + 1], sampleCount, 0, ratioMode);

/* Find the maximum number of samples and the time interval (in nanoseconds) */
while (ps3000aGetTimebase(unit.handle, timebase, sampleCount, &timeInterval, oversample, &maxSamples, 0))
{
    timebase++;
}

/* Start the device collecting, then wait for completion*/
  g_ready = FALSE;

ps3000aRunBlock(unit.handle, 0, sampleCount, timebase, oversample, &timeIndisposed, 0, callBackBlock, NULL); //callBackBlock merely checks to see if the pico_scope is not in "PICO_CANCELLED") state and sets g_ready = TRUE

printf("Waiting for trigger...Press a key to abort\n");

while (!g_ready && !_kbhit())
{
    Sleep(0);
}

if (g_ready)
{
    ps3000aGetValues(unit.handle, 0, (uint32_t*)&sampleCount, 1, ratioMode, 0, NULL);
}```
hmaarrfk commented 6 years ago

I would step through the python code, and see exactly what the program is sending to the ps3000aSetSimpleTrigger. you are sending in a hard number 5000, which may or may not be equal to 0.1V

hadisajj2 commented 6 years ago

Okay thanks, I will re-look into it. The 5000 is the trigger timeout, in the code above the voltage range is set to 1.0 V

hmaarrfk commented 6 years ago

so is the 0.2 the threshold that is being set by the c code?

your python examples had 0.1 set.

hadisajj2 commented 6 years ago

Yes, the C code sets it.

hadisajj2 commented 6 years ago

I changed it to 0.2 because it at time picking up noise and not a manual trigger from my end.

hmaarrfk commented 6 years ago

I really don't know. Sorry.

You can probably try an older version of the library, but I don't think it will make a big difference.

I assume you have access to the programming guide: https://www.picotech.com/download/manuals/PicoScope3000SeriesAApiProgrammersGuide.pdf

Just make sure to follow 3.7.1.1 Using block mode as it is the most straightforward way to get connected to the scope.

try removing and just using the fact that you will have a single capture

samples_per_segment = ps.memorySegments(n_captures)
ps.setNoOfCaptures(n_captures)
hadisajj2 commented 6 years ago

Okay thanks. I will figure it out and post my solution. Once again thanks for all the help

hmaarrfk commented 6 years ago

I think your solution would make a great first example!

Those examples definitely need to be renamed as I don't even know what they do anymore ;)

hadisajj2 commented 6 years ago

Sorry to bug again, but I can't find, at least for ps3000a, anything except runBlock()? The pico sdk has ps3000aRunStreaming, but the python code doesn't have a wrapper for this or am I missing something?

hmaarrfk commented 6 years ago

Get dataV and getDataRaw wrap a few very low level function calls, take a look at their implementation. They were made for this kind of simple 1 shot slow acquisition.

Are those what you are looking for?

On Thu, Jun 14, 2018 at 3:59 PM hadisajj2 notifications@github.com wrote:

Sorry to bug again, but I can't find, at least for ps3000a, anything except runBlock()? The pico sdk has ps3000aRunStreaming, but the python code doesn't have a wrapper for this or am I missing something?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/colinoflynn/pico-python/issues/128#issuecomment-397463337, or mute the thread https://github.com/notifications/unsubscribe-auth/AAFfmJlAKW6OKntoZsuOveZl8_VJ3ElIks5t8uq1gaJpZM4UjOHH .

hadisajj2 commented 6 years ago

Well yes and no.

Based on the code, my understanding is that getDataV and/or getDataRaw collect data once you've told the oscilloscope how to collect data (e.g. start collecting streamed OR block). In the pico-api, this is like this:

ps3000aRunStreaming --> tells the oscilloscope to start collecting streamed data. Note you can still set triggers on channel.

ps3000aRunBlock --> tells the oscilloscope to start collecting Block data.

I understand that getDataV and getDataRaw are the equivalents (or at least somewhat equivalent) of

ps3000aGetValuesBulk ps3000aGetStreamingLatestValues

If you take a look at picobase.py for ps3000a, getDataV says:

        def getDataV(self, channel, numSamples=0, startIndex=0, downSampleRatio=1,
        downSampleMode=0, segmentIndex=0, returnOverflow=False,
        exceptOverflow=False, dataV=None, dataRaw=None,
        dtype=np.float64):
        Return the data as an array of voltage values.

        it returns (dataV, overflow) if returnOverflow = True
        else, it returns returns dataV
        dataV is an array with size numSamplesReturned
        overflow is a flag that is true when the signal was either too large
                 or too small to be properly digitized

        if exceptOverflow is true, an IOError exception is raised on overflow
        if returnOverflow is False. This allows you to detect overflows at
        higher layers w/o complicated return trees. You cannot however read the
        'good' data, you only get the exception information then.

No mention of telling the pico-scope to start collecting data, albeit there might be a trigger.

Perhaps I'm missing something fundamental here, I apologize if that is the case!

hmaarrfk commented 6 years ago

Block, Bulk, Streaming are all different modes. Stick to Block for the beginning.

The library we wrote isn't magic. it mostly just wraps the C library for python. The documentation is sparse because we expect you to read the picoscope programming guide. We don't add much magic, and most of the variable names are 'pythonified' C names.

image

You should be able to dive into the code, and notice that the function getDataV mostly calls getDataRaw and performs scaling. This is from the code itself, and not from the documentation (though the documentation should give you an indication that that is happening).

Finally, you should see 3 important calls in getDataRaw:

  1. setDataBuffer
  2. getValues
  3. clearDataBuffer

You are in charge of following the other instructions to ensure that all function calls they describe are set.

hadisajj2 commented 6 years ago

Sorry for the late reply.

I got it to work for a simple trigger by removing ps.memorySegments(n_captures) and setting the number of captures, then using getDataRaw.

hmaarrfk commented 6 years ago

Great to hear! yeah the picoscope is definitely powerful, but kinda annoying to get working.

It is really optimized for high throughput applications!

hadisajj2 commented 6 years ago

Thanks for all your help!