Commit 30d7cc1b authored by Daniel Friesel's avatar Daniel Friesel
Browse files

autoformat the code (using black(1))

parent 68dd6506
Loading
Loading
Loading
Loading
+160 −93
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ import time

opt = dict()


def running_mean(x: np.ndarray, N: int) -> np.ndarray:
    """
    Compute `N` elements wide running average over `x`.
@@ -31,28 +32,35 @@ def running_mean(x: np.ndarray, N: int) -> np.ndarray:
    cumsum = np.cumsum(boundary_array)
    return (cumsum[N:] - cumsum[:-N]) / N


def measure_data(filename, duration):
    # libmsp430.so must be available
    if not 'LD_LIBRARY_PATH' in os.environ:
        os.environ['LD_LIBRARY_PATH'] = '{}/var/projects/msp430/MSP430Flasher_1.3.15'.format(os.environ['HOME'])
    if not "LD_LIBRARY_PATH" in os.environ:
        os.environ[
            "LD_LIBRARY_PATH"
        ] = "{}/var/projects/msp430/MSP430Flasher_1.3.15".format(os.environ["HOME"])

    # https://github.com/carrotIndustries/energytrace-util must be available
    energytrace_cmd = 'energytrace'
    energytrace_cmd = "energytrace"
    if which(energytrace_cmd) is None:
        energytrace_cmd = '{}/var/source/energytrace-util/energytrace64'.format(os.environ['HOME'])
        energytrace_cmd = "{}/var/source/energytrace-util/energytrace64".format(
            os.environ["HOME"]
        )

    if filename is not None:
        output_handle = open(filename, 'w+')
        output_handle = open(filename, "w+")
    else:
        output_handle = tempfile.TemporaryFile('w+')
        output_handle = tempfile.TemporaryFile("w+")

    energytrace = subprocess.Popen([energytrace_cmd, str(duration)], stdout = output_handle, universal_newlines = True)
    energytrace = subprocess.Popen(
        [energytrace_cmd, str(duration)], stdout=output_handle, universal_newlines=True
    )

    try:
        if duration:
            time.sleep(duration)
        else:
            print('Press Ctrl+C to stop measurement')
            print("Press Ctrl+C to stop measurement")
            while True:
                time.sleep(3600)
    except KeyboardInterrupt:
@@ -66,8 +74,10 @@ def measure_data(filename, duration):

    return output


def show_help():
    print('''msp430-etv - MSP430 EnergyTrace Visualizer
    print(
        """msp430-etv - MSP430 EnergyTrace Visualizer

USAGE

@@ -117,7 +127,9 @@ For data measurements (i.e., any invocation not using --load),
energytrace-util <https://github.com/carrotIndustries/energytrace-util>
must be available in $PATH and libmsp430.so must be located in the
LD library search path (e.g. LD_LIBRARY_PATH=../MSP430Flasher).
    ''')
    """
    )


def peak_search(data, lower, upper, direction_function):
    while upper - lower > 1e-6:
@@ -134,6 +146,7 @@ def peak_search(data, lower, upper, direction_function):
            upper = bs_test
    return None


def peak_search2(data, lower, upper, check_function):
    for power in np.arange(lower, upper, 1e-6):
        peakcount = itertools.groupby(data, lambda x: x >= power)
@@ -143,61 +156,62 @@ def peak_search2(data, lower, upper, check_function):
            return power
    return None

if __name__ == '__main__':

if __name__ == "__main__":
    try:
        optspec = ('help load= save= skip= threshold= threshold-peakcount= plot stat histogram=')
        raw_opts, args = getopt.getopt(sys.argv[1:], "", optspec.split(' '))
        optspec = "help load= save= skip= threshold= threshold-peakcount= plot stat histogram="
        raw_opts, args = getopt.getopt(sys.argv[1:], "", optspec.split(" "))

        for option, parameter in raw_opts:
            optname = re.sub(r'^--', '', option)
            optname = re.sub(r"^--", "", option)
            opt[optname] = parameter

        if 'help' in opt:
        if "help" in opt:
            show_help()
            sys.exit(0)

        if not 'load' in opt:
        if not "load" in opt:
            duration = int(args[0])

        if not 'save' in opt:
            opt['save'] = None
        if not "save" in opt:
            opt["save"] = None

        if 'skip' in opt:
            opt['skip'] = int(opt['skip'])
        if "skip" in opt:
            opt["skip"] = int(opt["skip"])
        else:
            opt['skip'] = 0
            opt["skip"] = 0

        if 'threshold' in opt and opt['threshold'] != 'mean':
            opt['threshold'] = float(opt['threshold'])
        if "threshold" in opt and opt["threshold"] != "mean":
            opt["threshold"] = float(opt["threshold"])

        if 'threshold-peakcount' in opt:
            opt['threshold-peakcount'] = int(opt['threshold-peakcount'])
        if "threshold-peakcount" in opt:
            opt["threshold-peakcount"] = int(opt["threshold-peakcount"])

    except getopt.GetoptError as err:
        print(err)
        sys.exit(2)
    except IndexError:
        print('Usage: msp430-etv <duration>')
        print("Usage: msp430-etv <duration>")
        sys.exit(2)
    except ValueError:
        print('Error: duration or skip is not a number')
        print("Error: duration or skip is not a number")
        sys.exit(2)

    if 'load' in opt:
        with open(opt['load'], 'r') as f:
    if "load" in opt:
        with open(opt["load"], "r") as f:
            log_data = f.read()
    else:
        log_data = measure_data(opt['save'], duration)
        log_data = measure_data(opt["save"], duration)

    lines = log_data.split('\n')
    data_count = sum(map(lambda x: len(x) > 0 and x[0] != '#', lines))
    data_lines = filter(lambda x: len(x) > 0 and x[0] != '#', lines)
    lines = log_data.split("\n")
    data_count = sum(map(lambda x: len(x) > 0 and x[0] != "#", lines))
    data_lines = filter(lambda x: len(x) > 0 and x[0] != "#", lines)

    data = np.empty((data_count - opt['skip'], 4))
    data = np.empty((data_count - opt["skip"], 4))

    for i, line in enumerate(data_lines):
        if i >= opt['skip']:
            fields = line.split(' ')
        if i >= opt["skip"]:
            fields = line.split(" ")
            if len(fields) == 4:
                timestamp, current, voltage, total_energy = map(int, fields)
            elif len(fields) == 5:
@@ -205,24 +219,28 @@ if __name__ == '__main__':
                timestamp, current, voltage, total_energy = map(int, fields[1:])
            else:
                raise RuntimeError('cannot parse line "{}"'.format(line))
            data[i - opt['skip']] = [timestamp, current, voltage, total_energy]
            data[i - opt["skip"]] = [timestamp, current, voltage, total_energy]

    m_duration_us = data[-1, 0] - data[0, 0]
    m_energy_nj = data[-1, 3] - data[0, 3]
    # mV * nA * us = aJ (1e-18 J) -> use factor 1e-6 to get pJ (1e-12 J)

    print('{:d} measurements in {:.2f} s = {:.0f} Hz sample rate'.format(
        data_count, m_duration_us * 1e-6, data_count / (m_duration_us * 1e-6)))
    print(
        "{:d} measurements in {:.2f} s = {:.0f} Hz sample rate".format(
            data_count, m_duration_us * 1e-6, data_count / (m_duration_us * 1e-6)
        )
    )

    print('Reported energy: E = {:f} J'.format(m_energy_nj * 1e-9))
    print("Reported energy: E = {:f} J".format(m_energy_nj * 1e-9))

    # nJ / us = mW -> (nJ * 1e-9) / (us * 1e-6) = W
    # Do not use power = data[:, 1] * data[:, 2] * 1e-12 here: nA values provided by the EnergyTrace library in data[:, 1] are heavily filtered and mostly
    # useless for visualization and calculation. They often do not agree with the nJ values in data[:, 3].
    power = ((data[1:, 3] - data[:-1, 3]) * 1e-9) / ((data[1:, 0] - data[:-1, 0]) * 1e-6)

    power = ((data[1:, 3] - data[:-1, 3]) * 1e-9) / (
        (data[1:, 0] - data[:-1, 0]) * 1e-6
    )

    if 'threshold-peakcount' in opt:
    if "threshold-peakcount" in opt:
        bs_mean = np.mean(power)

        # Finding the correct threshold is tricky. If #peaks < peakcont, our
@@ -238,42 +256,59 @@ if __name__ == '__main__':
        # #peaks != peakcount and threshold >= mean, we go down.
        # If that doesn't work, we fall back to a linear search in 1 µW steps
        def direction_function(peakcount, power):
            if peakcount == opt['threshold-peakcount']:
                return 0;
            if peakcount == opt["threshold-peakcount"]:
                return 0
            if power < bs_mean:
                return 1;
            return -1;
                return 1
            return -1

        threshold = peak_search(power, np.min(power), np.max(power), direction_function)
        if threshold == None:
            threshold = peak_search2(power, np.min(power), np.max(power), direction_function)
            threshold = peak_search2(
                power, np.min(power), np.max(power), direction_function
            )

        if threshold != None:
            print('Threshold set to {:.0f} µW         : {:.9f}'.format(threshold * 1e6, threshold))
            opt['threshold'] = threshold
            print(
                "Threshold set to {:.0f} µW         : {:.9f}".format(
                    threshold * 1e6, threshold
                )
            )
            opt["threshold"] = threshold
        else:
            print('Found no working threshold')
            print("Found no working threshold")

    if 'threshold' in opt:
        if opt['threshold'] == 'mean':
            opt['threshold'] = np.mean(power)
            print('Threshold set to {:.0f} µW         : {:.9f}'.format(opt['threshold'] * 1e6, opt['threshold']))
    if "threshold" in opt:
        if opt["threshold"] == "mean":
            opt["threshold"] = np.mean(power)
            print(
                "Threshold set to {:.0f} µW         : {:.9f}".format(
                    opt["threshold"] * 1e6, opt["threshold"]
                )
            )

        baseline_mean = 0
        if np.any(power < opt['threshold']):
            baseline_mean = np.mean(power[power < opt['threshold']])
            print('Baseline mean: {:.0f} µW           : {:.9f}'.format(
                baseline_mean * 1e6, baseline_mean))
        if np.any(power >= opt['threshold']):
            print('Peak mean: {:.0f} µW               : {:.9f}'.format(
                np.mean(power[power >= opt['threshold']]) * 1e6,
                np.mean(power[power >= opt['threshold']])))
        if np.any(power < opt["threshold"]):
            baseline_mean = np.mean(power[power < opt["threshold"]])
            print(
                "Baseline mean: {:.0f} µW           : {:.9f}".format(
                    baseline_mean * 1e6, baseline_mean
                )
            )
        if np.any(power >= opt["threshold"]):
            print(
                "Peak mean: {:.0f} µW               : {:.9f}".format(
                    np.mean(power[power >= opt["threshold"]]) * 1e6,
                    np.mean(power[power >= opt["threshold"]]),
                )
            )

        peaks = []
        peak_start = -1
        for i, dp in enumerate(power):
            if dp >= opt['threshold'] and peak_start == -1:
            if dp >= opt["threshold"] and peak_start == -1:
                peak_start = i
            elif dp < opt['threshold'] and peak_start != -1:
            elif dp < opt["threshold"] and peak_start != -1:
                peaks.append((peak_start, i))
                peak_start = -1

@@ -282,41 +317,73 @@ if __name__ == '__main__':
        for peak in peaks:
            duration = data[peak[1] - 1, 0] - data[peak[0], 0]
            total_energy += np.mean(power[peak[0] : peak[1]]) * duration
            delta_energy += (np.mean(power[peak[0] : peak[1]]) - baseline_mean) * duration
            print('{:.2f}ms peak ({:f} -> {:f})'.format(duration * 1000,
                data[peak[0], 0], data[peak[1]-1, 0]))
            print('    {:f} µJ / mean {:f} µW'.format(
            delta_energy += (
                np.mean(power[peak[0] : peak[1]]) - baseline_mean
            ) * duration
            print(
                "{:.2f}ms peak ({:f} -> {:f})".format(
                    duration * 1000, data[peak[0], 0], data[peak[1] - 1, 0]
                )
            )
            print(
                "    {:f} µJ / mean {:f} µW".format(
                    np.mean(power[peak[0] : peak[1]]) * duration * 1e6,
                np.mean(power[peak[0] : peak[1]]) * 1e6 ))
        print('Peak energy mean: {:.0f} µJ         : {:.9f}'.format(
            total_energy * 1e6 / len(peaks), total_energy / len(peaks)))
        print('Average per-peak energy (delta over baseline): {:.0f} µJ         : {:.9f}'.format(
            delta_energy * 1e6 / len(peaks), delta_energy / len(peaks)))

    power_from_energy = ((data[1:, 3] - data[:-1, 3]) * 1e-9) / ((data[1:, 0] - data[:-1, 0]) * 1e-6)
                    np.mean(power[peak[0] : peak[1]]) * 1e6,
                )
            )
        print(
            "Peak energy mean: {:.0f} µJ         : {:.9f}".format(
                total_energy * 1e6 / len(peaks), total_energy / len(peaks)
            )
        )
        print(
            "Average per-peak energy (delta over baseline): {:.0f} µJ         : {:.9f}".format(
                delta_energy * 1e6 / len(peaks), delta_energy / len(peaks)
            )
        )

    power_from_energy = ((data[1:, 3] - data[:-1, 3]) * 1e-9) / (
        (data[1:, 0] - data[:-1, 0]) * 1e-6
    )
    mean_power = running_mean(power_from_energy, 10)

    if 'stat' in opt:
    if "stat" in opt:
        mean_voltage = np.mean(data[:, 2] * 1e-3)
        mean_current = np.mean(data[:, 1] * 1e-9)
        mean_power = np.mean(data[:, 1] * data[:, 2] * 1e-12)
        print('Mean voltage: {:.2f} V       : {:.9f}'.format(mean_voltage, mean_voltage))
        print('Mean current: {:.0f} µA       : {:.9f}'.format(mean_current * 1e6, mean_current))
        print('Mean power: {:.0f} µW       : {:.9f}'.format(mean_power * 1e6, mean_power))
        print('Total energy: {:f} J       : {:.9f}'.format(m_energy_nj * 1e-9, m_energy_nj * 1e-9))

    if 'plot' in opt:
        print(
            "Mean voltage: {:.2f} V       : {:.9f}".format(mean_voltage, mean_voltage)
        )
        print(
            "Mean current: {:.0f} µA       : {:.9f}".format(
                mean_current * 1e6, mean_current
            )
        )
        print(
            "Mean power: {:.0f} µW       : {:.9f}".format(mean_power * 1e6, mean_power)
        )
        print(
            "Total energy: {:f} J       : {:.9f}".format(
                m_energy_nj * 1e-9, m_energy_nj * 1e-9
            )
        )

    if "plot" in opt:
        # nA * mV = pW
        energyhandle, = plt.plot(data[1:, 0] * 1e-6, power_from_energy, 'b-', label='P=ΔE/Δt', markersize=1)
        meanhandle, = plt.plot(data[1:, 0] * 1e-6, mean_power, 'r-', label='mean(P, 10)', markersize=1)
        (energyhandle,) = plt.plot(
            data[1:, 0] * 1e-6, power_from_energy, "b-", label="P=ΔE/Δt", markersize=1
        )
        (meanhandle,) = plt.plot(
            data[1:, 0] * 1e-6, mean_power, "r-", label="mean(P, 10)", markersize=1
        )
        plt.legend(handles=[energyhandle, meanhandle])
        plt.xlabel('Time [s]')
        plt.ylabel('Power [W]')
        plt.xlabel("Time [s]")
        plt.ylabel("Power [W]")
        plt.grid(True)
        if 'load' in opt:
            plt.title(opt['load'])
        if "load" in opt:
            plt.title(opt["load"])
        plt.show()

    if 'histogram' in opt:
        plt.hist((data[1:, 3] - data[:-1, 3]) * 1e-9, bins=int(opt['histogram']))
    if "histogram" in opt:
        plt.hist((data[1:, 3] - data[:-1, 3]) * 1e-9, bins=int(opt["histogram"]))
        plt.show()