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

Reading avg don't match with real data #15

Closed lopeznegrete closed 2 years ago

lopeznegrete commented 2 years ago

I am trying to calculate average power consumption for long periods. The average power consumption of the process is around 7.5mA.

I have written the below script:

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

ppk2s_connected = PPK2_API.list_devices()
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_ampere_meter()  # set ampere meter mode
ppk2_test.toggle_DUT_power("ON")  # enable DUT power
ppk2_test.set_source_voltage(4150)  # set source voltage in mV
time.sleep(5)
ppk2_test.start_measuring()  # start measuring

# measurements are a constant stream of bytes
# multiprocessing variant starts a process in the background which constantly
# polls the device in order to prevent losing samples. It will buffer the
# last 10s (by default) of data so get_data() can be called less frequently.

for i in range(0, 1000):
    read_data = ppk2_test.get_data()
    if read_data != b'':
        samples = ppk2_test.get_samples(read_data)
        average_current_mA = (sum(samples) / len(samples)) / 1000  # measurements are in microamperes, divide by 1000
        average_power_mW = (ppk2_test.current_vdd / 1000) * average_current_mA  # divide by 1000 as source voltage is in millivolts - this gives us milliwatts
        measurement_duration_h = 10 / 3600  # duration in seconds, divide by 3600 to get hours
        average_consumption_mWh = average_power_mW * measurement_duration_h
        print(f"[{datetime.datetime.now()}] Average of {len(samples)} samples is: {average_current_mA}mA {average_consumption_mWh}mWh")

    time.sleep(60)  # lower time between sampling -> less samples read in one sampling period

ppk2_test.stop_measuring()

There are several problems I have encountered.

  1. The average sample readings over 60s are not precise. They vary a lot from real value. image

  2. It is my understanding that get_samples() function should take all the samples since the last request. As the sleep time is set to 60s and the sample rate is 100 kSamples/s(#5 ), the total number of samples should be 60M. Instead, I am getting 4096.

  3. If I reduce the sleep time between get_samples, the samples are either fix to 1024, 2048 & 4096. Should not that vary with time?

Finally a capture of my readings with PPK profiler app.

image

wlgrd commented 2 years ago
  1. You are saying that they vary a lot from the real value: looking at the chart from the PPK2 software, it looks like there are a lot of variations there as well. 4096 samples is approx 41ms, so you should zoom in a bit more and see if the PPK2 software shows the same variations. Also, the average of what you posted in 1 is ~7.8mA.
  2. Haven't tested this.
  3. I think this is also due to the USB endpoint size, so it transfers in chunks of 1024. I might be completely off here though.
lopeznegrete commented 2 years ago

Sorted looping through small readings up to 4096 samples

wlgrd commented 2 years ago

Sorted looping through small readings up to 4096 samples

Can you elaborate please?

lopeznegrete commented 2 years ago

This is my script. It takes a loop every 10ms to gather data. I want to gather avg data every 60s till the battery dies. It is working so far and the averages are in line with my theoretical calculations

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

BATTERY_SIZE_mWh = 37000
LOOP_SLEEP_TIME_S = 0.01
EXPORT_READS_TO_SHEET_TIME_S = 60

ppk2s_connected = PPK2_API.list_devices()
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_ampere_meter()  # set ampere meter mode
ppk2_test.toggle_DUT_power("ON")  # enable DUT power
ppk2_test.set_source_voltage(4150)  # set source voltage in mV
time.sleep(1)
ppk2_test.start_measuring()  # start measuring

filename = datetime.datetime.now().strftime("power_test_%Y%m%d-%H%M%S.xlsx")
workbook = openpyxl.Workbook()
sheet = workbook['Sheet']
sheet.title = 'Power Consumption'
sheet['A1'] = 'Time'
sheet['B1'] = 'Avg Current (mA)'
sheet['C1'] = 'Avg Consumption (mWh)'
sheet['D1'] = 'Time Elapsed'
sheet['E1'] = 'Acc Consumption (mWh)'
sheet['F1'] = 'Battery Level 37Wh (mWh)'
sheet['G1'] = 'Battery Level 37Wh (%)'
workbook.save(filename)

#workbook = xlsxwriter.Workbook(datetime.datetime.now().strftime("power_test_%Y%m%d-%H%M%S.xlsx"))
#sheet = workbook.add_worksheet()

# measurements are a constant stream of bytes
# multiprocessing variant starts a process in the background which constantly
# polls the device in order to prevent losing samples. It will buffer the
# last 10s (by default) of data so get_data() can be called less frequently.

loops = 0
row = 1
loop_start_time = time.time();
test_start_time = datetime.datetime.now();
accumulated_current_mA = 0;
average_current_mA = 0;
average_power_mWh = 0;
acc_consumption_mWh = 0;

while(1):
    read_data = ppk2_test.get_data()
    loops += 1;
    if read_data != b'':
        samples = ppk2_test.get_samples(read_data)
        accumulated_current_mA += ((sum(samples) / len(samples)) / 1000)  # measurements are in microamperes, divide by 1000
        if (time.time() - loop_start_time > EXPORT_READS_TO_SHEET_TIME_S):
            row += 1;
            average_current_mA = accumulated_current_mA/loops
            average_power_mWh = ((ppk2_test.current_vdd / 1000) * average_current_mA)  # divide by 1000 as source voltage is in millivolts - this gives us milliwatts
            measurement_duration_h = ((datetime.datetime.now() - test_start_time).total_seconds())  # duration in seconds, divide by 3600 to get hours
            acc_consumption_mWh = average_power_mWh * (measurement_duration_h/ 3600)
            loop_start_time = time.time();

            sheet['A'+str(row)] = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")
            sheet['B'+str(row)] = '{:.2f}'.format(average_current_mA)
            sheet['C'+str(row)] = "{:.2f}".format(average_power_mWh)
            sheet['D'+str(row)] = '{:.0f}'.format(divmod(measurement_duration_h, 3600)[0])+ 'h ' '{:.0f}'.format(divmod(measurement_duration_h, 60)[0])+ 'm '  + '{:.0f}'.format(divmod(measurement_duration_h, 60)[1]) + 's'
            sheet['E'+str(row)] = "{:.2f}".format(acc_consumption_mWh)
            sheet['F'+str(row)] = "{:.2f}".format(BATTERY_SIZE_mWh - acc_consumption_mWh)
            sheet['G'+str(row)] = "{:.2f}".format(100*(BATTERY_SIZE_mWh - acc_consumption_mWh)/BATTERY_SIZE_mWh)
            workbook.save(filename)

    time.sleep(LOOP_SLEEP_TIME_S)  # lower time between sampling -> less samples read in one sampling period

ppk2_test.stop_measuring()

print(f"Test Completed")