IRNAS / ppk2-api-python

Power Profiling Kit 2 unofficial python api.
http://irnas.eu
GNU General Public License v2.0
145 stars 37 forks source link

Values returned by ppk2-api not matching those of NRF Connect #7

Closed StanKatsyukCT closed 3 years ago

StanKatsyukCT commented 3 years ago

Hello team! Firstly, thanks for spending time on this API, really appreciate all your effort!

I am running into an issue where I can't seem to get the same current readings from the ppk2-api as I do from NRF connect.

My NRF connect setup: image

Note the reading of around 52uA (which is expected)

The example.py code im running in python is:

"""
Basic usage of PPK2 Python API.
The basic ampere mode sequence is:
1. read modifiers
2. set use_source_meter
3. read stream of data
"""
import time
from src.ppk2_api import PPK2_API

ppk2s_connected = PPK2_API.list_devices()
ppk2_port = "/dev/tty.usbmodemD325C91691932"
boot_up_sleep_time = 5
test_time = 10 # time to run test in mins
sleep_time = 60 # time to sleep between polling cycles
cycle_count = lambda x : x * 60 // sleep_time
sample_size = 4

if(len(ppk2s_connected) == 1):
    ppk2_port = ppk2s_connected[0]
    print(f'Found PPK2 at {ppk2_port}')
else:
    print(f'Too many connected PPK2\'s: {ppk2s_connected}')
    exit()

ppk2_test = PPK2_API(ppk2_port)
ppk2_test.get_modifiers()
ppk2_test.use_source_meter()  # set source meter mode
ppk2_test.toggle_DUT_power("OFF") # ensure DUT power is disabled before starting
ppk2_test.toggle_DUT_power("ON") # enable DUT power
time.sleep(boot_up_sleep_time) # sleep to allow device to fully boot

ppk2_test.start_measuring()
print(f'running test for {cycle_count(test_time)} cycles')
for i in range(cycle_count(test_time)):
    read_data = ppk2_test.get_data()
    if read_data != b'':
        samples = ppk2_test.get_samples(read_data)
        print(f'samples are: {samples}\n')
        print('\n')
        print(f"Average of {len(samples)} samples is: {sum(samples)/len(samples)}uA")
    time.sleep(sleep_time)  # lower time between sampling -> less samples read in one sampling period

ppk2_test.stop_measuring()

The output of the code is

Found PPK2 at /dev/cu.usbmodemD325C91691932
running test for 600 cycles
samples are: [7429.81725013246, 7474.651303825936, 7564.356197340542, 7474.651303825936, 7340.185928873162, 7429.81725013246, 7474.651303825936, 7429.81725013246, 7295.388661307342, 7429.81725013246, 7250.6036557840725, 7295.388661307342, 7205.830912303355, 7295.388661307342, 6982.151125538042, 7161.070430865188, 7026.8625588059995, 7295.388661307342, 7205.830912303355, 7116.322211469574, 7205.830912303355, 7071.586254116512, 7116.322211469574, 7161.070430865188, 7250.6036557840725, 7026.8625588059995, 6848.090397989471, 6669.514429853771, 6357.478574254529, 6357.478574254529, 6848.090397989471, 7833.765166905603, 9096.843757130464, 10232.716101593121, 10734.927817581916, 10917.918121036211, 10415.166875175144, 10050.461520691928, 9868.40313247156, 9868.40313247156, 9504.874934073301, 9278.06832645732, 9323.405123895414, 9504.874934073301, 9641.106043153512, 9459.489088465003, 9096.843757130464, 8509.218862520134, 8509.218862520134, 8599.487809992117, 8283.761079584834, 8373.9074066313, 8283.761079584834, 8103.615570002522, 8013.6163874666745, 7968.63518926258, 7968.63518926258, 7968.63518926258, 7833.765166905603, 7654.110139025358, 7340.185928873162, 7161.070430865188, 6892.765045129777, 6892.765045129777, 8077.324640368932, 8077.324640368932, 15960.843768505314, 102626.39081491636, 77972.88661900496, 59381.22876599401, 49001.99736054694, 43081.354269218355, 32738.33485501537, 28312.66653293696, 22418.359435240436, 32933.69075672683, 28839.310551910723, 25555.84114402563, 10415.166875175144, 10278.310401924802, 9868.40313247156, 9731.988092753076, 9822.919190522849, 10643.506238110083, 12250.465208440795, 12851.161440411175, 12619.879187263843, 11973.91973011068, 11789.801318741638, 11422.15307404603, 10963.696352006164, 10643.506238110083, 10050.461520691928, 9868.40313247156, 9368.754183376057, 9006.305044722345, 9187.431517708788, 9278.06832645732, 9187.431517708788, 8870.588941429305, 8689.805805634305, 8554.34720523485, 8373.9074066313, 8373.9074066313, 7878.709578982044, 8013.6163874666745, 8103.615570002522, 7878.709578982044, 7833.765166905603, 7654.110139025358, 7340.185928873162, 7564.356197340542, 7609.227037161674, 7743.9131288803765, 7429.81725013246, 7340.185928873162, 7429.81725013246, 7609.227037161674, 7519.497619561963, 7429.81725013246, 7384.995458481536, 7474.651303825936, 7384.995458481536, 7205.830912303355, 7205.830912303355, 7205.830912303355, 6803.428012891719, 6803.428012891719, 6848.090397989471, 7205.830912303355, 6982.151125538042, 7026.8625588059995, 7295.388661307342, 7923.666253101036, 9051.568269905127, 10278.310401924802, 10872.152152108809, 10963.696352006164, 10780.657000381665, 10689.210896824723, 10506.465834221463, 10050.461520691928, 9550.273041724153, 9641.106043153512, 9187.431517708788, 8870.588941429305, 8734.983196519228, 8689.805805634305, 8599.487809992117, 8780.172849446702, 8870.588941429305, 8780.172849446702, 8825.374764416729, 8734.983196519228, 8328.82811208679, 8193.663800708575, 8554.34720523485, 8418.99896321836, 7878.709578982044, 37317.895215582816, 10496.380552421188, 10496.380552421188, 16699.08900666612, 84683.8718703108, 66809.07405218767, 50483.33384738052, 43081.354269218355, 37168.23574749893, 30524.97162267553, 23154.736322552, 20945.958374817732, 30401.60266060324, 26762.998313089378, 23705.599168644294, 9550.273041724153, 9459.489088465003, 9187.431517708788, 8644.640676791936, 8418.99896321836, 8238.706309125428, 8238.706309125428, 8328.82811208679, 8238.706309125428, 8418.99896321836, 8554.34720523485, 9232.74379106178, 10460.810223677028, 11927.871734204595, 11927.871734204595, 11743.802371005755, 11192.7714374942, 10917.918121036211, 10415.166875175144, 10050.461520691928, 9777.447510616686, 9641.106043153512, 9368.754183376057, 8915.815380484433, 8780.172849446702, 8915.815380484433, 8689.805805634305, 8554.34720523485, 8554.34720523485, 8238.706309125428, 8193.663800708575, 8328.82811208679, 8418.99896321836, 8238.706309125428, 8238.706309125428, 7699.005502931591, 7743.9131288803765, 7743.9131288803765, 7788.833016871714, 7699.005502931591, 7699.005502931591, 7654.110139025358, 7340.185928873162, 7474.651303825936, 7161.070430865188, 7161.070430865188, 7026.8625588059995, 7071.586254116512, 6982.151125538042, 7116.322211469574, 6937.4519543126335, 7071.586254116512, 7295.388661307342, 7699.005502931591, 7474.651303825936, 7250.6036557840725, 7205.830912303355, 7250.6036557840725, 6982.151125538042, 6937.4519543126335, 7205.830912303355, 7340.185928873162, 8283.761079584834, 9187.431517708788, 10597.813841437992, 10917.918121036211, 10552.13370680845, 10506.465834221463, 10004.928530573006, 9368.754183376057, 9323.405123895414, 8961.054081582113, 8870.588941429305, 8644.640676791936, 8509.218862520134, 8418.99896321836, 8238.706309125428, 8283.761079584834, 7968.63518926258, 8283.761079584834, 8609.38199833042]

Average of 255 samples is: 11661.089451332276uA

As you can see, the uA I am getting is much higher than the readings I get via NRF connect. Am I missing some param I need to adjust to get the readings to be more accurate?

Thank you in advance,

NejcKle commented 3 years ago

Hi @StanKatsyukCT, please try adding ppk2_test.set_source_voltage(3300) to device setup. I think this might be causing your issues.

Like this:

ppk2_test = PPK2_API(ppk2_port)
ppk2_test.get_modifiers()
ppk2_test.use_source_meter()  # set source meter mode
ppk2_test.set_source_voltage(3300)  # set source voltage to 3.3V
ppk2_test.toggle_DUT_power("OFF") # ensure DUT power is disabled before starting
ppk2_test.toggle_DUT_power("ON") # enable DUT power

Thank you and please report back!

StanKatsyukCT commented 3 years ago

Hi @NejcKle , thanks for the suggestion. I tried this but im still not getting the same readings as NRF Connect.

In your experience, what should I set the following to?:

As I understand it, the sample_size param is to set how many bytes are read from serial read? Is this the same value that NRF is hardcoded to do at 10k?

NejcKle commented 3 years ago

Hi @StanKatsyukCT, the ppk2 actually streams data at a fixed rate of 100k samples per second. As the sample rate is fixed, the sleep_time between reads should be set to 10ms or 1ms, so no samples are missed.

We are doing averaging over a fixed sized window to get the same readings as the nRF Connect app reports. Please check out https://github.com/IRNAS/ppk2-api-python/issues/5 for more information.

The sample_size param seen in code is the size of each sampled value in bytes and should not be changed!

Hope this helps!

StanKatsyukCT commented 3 years ago

@NejcKle thanks for the suggestion. Is the window_size variable in your code the same as the "Sampling Parameters" in NRF Connects Power Profiler app?

I am currently experimenting with a 1000 window size, but I'm still not able to get the same readings as I do via the NRF app.

My updated code:

` """ Basic usage of PPK2 Python API. The basic ampere mode sequence is:

  1. read modifiers
  2. set use_source_meter
  3. read stream of data """ import time from src.ppk2_api import PPK2_API

ppk2s_connected = PPK2_API.list_devices() ppk2_port = "/dev/tty.usbmodemD325C91691932" boot_up_sleep_time = 45 window_size = 1000

if(len(ppk2s_connected) == 1): ppk2_port = ppk2s_connected[0] print(f'Found PPK2 at {ppk2_port}') else: print(f'Too many connected PPK2\'s: {ppk2s_connected}') exit()

ppk2_test = PPK2_API(ppk2_port) ppk2_test.get_modifiers() ppk2_test.use_source_meter() # set source meter mode ppk2_test.set_source_voltage(3300) # set source voltage to 3.3V ppk2_test.toggle_DUT_power("OFF") # ensure DUT power is disabled before starting ppk2_test.toggle_DUT_power("ON") # enable DUT power

print(f'Sleeping for {boot_up_sleep_time} seconds') time.sleep(boot_up_sleep_time) # sleep to allow device to fully boot ppk2_test.start_measuring()

def _average_samples(list, window_size): """Average samples based on window size""" chunks = [list[val:val + window_size] for val in range(0, len(list), window_size)] avgs = [] for chunk in chunks: avgs.append(sum(chunk) / len(chunk)) return avgs

while True: read_data = ppk2_test.get_data() if read_data != b'': samples = _average_samples(ppk2_test.get_samples(read_data), 1000) # optionally average samples print(samples[0]) time.sleep(.01) # lower time between sampling -> less samples read in one sampling period

ppk2_test.stop_measuring() `

For reference, the NRF Power Profiler shows about ≈ 50uA but via the python code above i get ≈ 20uA

Thanks again for all your insight

NejcKle commented 3 years ago

@StanKatsyukCT yes, the window_size variable is the same as the "Sampling Parameters" in the nRF Connect App.

Could you try averaging samples in a time window? In the screenshot you provided the data gets averaged over a period of 7 seconds. Something like this:

start_time = time.time()
sampling_duration = 7  # set window size in seconds
sampling_period_samples = []
while True:
    read_data = ppk2_test.get_data()
    if read_data != b'':
        samples = _average_samples(ppk2_test.get_samples(read_data), 1000) # optionally average samples
        if time.time() - start_time < sampling_duration:
            sampling_period_samples += samples  # append to list
        else:
            print(f"Average of sampling period: {sum(sampling_period_samples) / len(sampling_period_samples)}")
            sampling_period_samples = []  # clear list
            start_time = time.time()
    time.sleep(.01) # lower time between sampling -> less samples read in one sampling period
StanKatsyukCT commented 3 years ago

@NejcKle Im still not getting the same readings :/

Let me run everything im doing by you just to make sure im not missing something:

The most recent code I have is:

"""
Basic usage of PPK2 Python API.
The basic ampere mode sequence is:
1. read modifiers
2. set use_source_meter
3. read stream of data
"""
import time
from src.ppk2_api import PPK2_API

ppk2s_connected = PPK2_API.list_devices()
ppk2_port = "/dev/tty.usbmodemD325C91691932"
boot_up_sleep_time = 45

if(len(ppk2s_connected) == 1):
    ppk2_port = ppk2s_connected[0]
    print(f'Found PPK2 at {ppk2_port}')
else:
    print(f'Too many connected PPK2\'s: {ppk2s_connected}')
    exit()

ppk2_test = PPK2_API(ppk2_port)
ppk2_test.get_modifiers()
ppk2_test.use_source_meter()  # set source meter mode
ppk2_test.set_source_voltage(3100)  # set source voltage to 3.3V
ppk2_test.toggle_DUT_power("OFF") # ensure DUT power is disabled before starting
ppk2_test.toggle_DUT_power("ON") # enable DUT power

def setup():
    print(f'Sleeping for {boot_up_sleep_time} seconds')
    time.sleep(boot_up_sleep_time) # sleep to allow device to fully boot
    print('starting test')
    ppk2_test.start_measuring()

def _average_samples(list, window_size):
    """Average samples based on window size"""
    chunks = [list[val:val + window_size] for val in range(0, len(list), window_size)]
    avgs = []
    for chunk in chunks:
        avgs.append(sum(chunk) / len(chunk))
    return avgs

def run_test():
    setup()
    start_time = time.time()
    sampling_duration = 7  # set window size in seconds
    sampling_period_samples = []
    while True:
        read_data = ppk2_test.get_data()
        if read_data != b'':
            samples = _average_samples(ppk2_test.get_samples(read_data), 1000) # optionally average samples
            if time.time() - start_time < sampling_duration:
                sampling_period_samples += samples  # append to list
            else:
                print(f"Average of sampling period: {sum(sampling_period_samples) / len(sampling_period_samples)}")
                sampling_period_samples = []  # clear list
                start_time = time.time()
        time.sleep(.01) # lower time between sampling -> less samples read in one sampling period

run_test()

However, that outputs two values that look promising and then it seems like it outputs junk data

Found PPK2 at /dev/cu.usbmodemD325C91691932
Sleeping for 45 seconds
starting test
Average of sampling period: 71.21186607958448
Average of sampling period: 51.604008113477775
Average of sampling period: 7117199.324881784
Average of sampling period: 10396420.503219703
Average of sampling period: 10398523.612243453
Average of sampling period: 10396456.484739268
Average of sampling period: 10397220.137242936

Im not sure if I am overloading the PPK2 with serial reads and it locks up?

also, maybe you can help me understand the variables a bit more:

  1. Is the window_size the number of serial reads we are doing?
  2. is sampling_duration the time for which we are sampling for?

If #2 is true, do we still need sleeps in between samples?

Thanks in advance

NejcKle commented 3 years ago

Hi @StanKatsyukCT, sorry for the late response, we've been busy with urgent matters.

  1. The window_size variable is not the number of serial reads we are doing, it is the number of samples we are averaging. So, for a window_size of 1000 you would get n/1000 average values. The nRF Connect App does exactly this when displaying the graph, you will notice how the graph smooths out as the window_size increases.
  2. The sampling_duration is the period in minutes/seconds/milliseconds/etc. in which we average all datapoints. This is equivalent to the size of the window in the nRF Connect App, 7 seconds in your example image. So the average value displayed on your image is the average of 7 seconds of readings. This is what the sampling_duration variable specifies.

Could you log the ppk2_test.get_samples(read_data) samples directly to a file and include it here?

StanKatsyukCT commented 3 years ago

I was able to get it working, there was an issue in the logic for processing the buffer output of the PPK2. Thanks for all your help @NejcKle and team.

wlgrd commented 3 years ago

Would love to see both the issue, and the solution =)

VitaliChar commented 1 year ago

I have the same problem. If you remove the sleep to obtain the maximum polling frequency, then zero values ​​appear, which distort the final result.

    ppk2_test = PPK2_API("/dev/ttyACM0")  # serial port will be different for you
    ppk2_test.get_modifiers()
    ppk2_test.use_source_meter()  # set source meter mode
    ppk2_test.set_source_voltage(3300)  # set source voltage in mV
    ppk2_test.toggle_DUT_power('ON')
    ppk2_test.start_measuring()  # start measuring

    # read measured values in a for loop like this:
    start_time = time.time()
    while time.time() - start_time <= 1:
        read_data = ppk2_test.get_data()
        samples = ppk2_test.get_samples(read_data)
        ave = sum(samples) / len(samples)
        print(len(samples), ave)

    ppk2_test.stop_measuring()

result fragment: 1 -1.2596387693469735 1 -1.2596387693469735 1 41.078069146083955 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735 7 41.15273882494715 1 -1.2596387693469735 1 -1.2596387693469735 1 -1.2596387693469735

Lalit2612 commented 5 months ago

I was able to get it working, there was an issue in the logic for processing the buffer output of the PPK2. Thanks for all your help @NejcKle and team.

Hi @StanKatsyukCT, I request you to upload your final code, just for a reference.

Thanks in advance.

RehabAbdAllah commented 2 months ago

Hi @StanKatsyukCT .. what was the issue in the logic for processing the buffer output of the PPK2 ? I am having the same issue where on the python script I get a very high value compared to what I get on gui ?