tools: corrected channels and data reported in plot.py

corrected the channel mask for which measurement channels are
currently in use on gonk.  Also correected tools to report voltages,
currents, and power values instead of raw adc counts.  Added option
to plot.py for dumping a csv file of measurements.

Change-Id: I789d498680e1ac1aa2fba25cdb99b1c13b982ecd
Reviewed-on: https://pigweed-review.googlesource.com/c/gonk/+/217711
Reviewed-by: Anthony DiGirolamo <tonymd@google.com>
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Commit-Queue: Eric Holland <hollande@google.com>
Reviewed-by: Adam Perry <adamperry@google.com>
diff --git a/.gitignore b/.gitignore
index 8561777..49ff46d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,5 @@
 # Other
 logfile.txt
 pw_console-*logs.txt
+*.svg
+gonk*logs.txt
diff --git a/applications/fpga_config/main.cc b/applications/fpga_config/main.cc
index 1699362..ebbebca 100644
--- a/applications/fpga_config/main.cc
+++ b/applications/fpga_config/main.cc
@@ -171,7 +171,7 @@
     fpga_adc.InitAdcs();
     fpga_adc.CheckAllAdcs();
     // Select the first five ADC channels.
-    fpga_adc.SelectContinuousReadAdcs(0b11111);
+    fpga_adc.SelectContinuousReadAdcs(0b011100111001);
 
     fpga_adc.SetContinuousReadMode();
   } else {
diff --git a/tools/gonk_tools/plot.py b/tools/gonk_tools/plot.py
index 0c38ca2..c783e99 100644
--- a/tools/gonk_tools/plot.py
+++ b/tools/gonk_tools/plot.py
@@ -24,9 +24,88 @@
 import matplotlib.pyplot as plt
 import numpy as np
 
-
 _LOG = logging.getLogger(__package__)
-ADC_COUNT = 5
+
+# CHANNEL_MASK should be set to same value used by
+#   SelectContinuousReadAdcs in the MCU
+CHANNEL_MASK = 0b011100111001
+ADC_COUNT = CHANNEL_MASK.bit_count()
+
+channel_names = [
+    "VDD_EE",
+    "VCC18",
+    "VDDIOAO18",
+    "VDDCPU_B",
+    "DCIN",
+    "VDDQ(DDR)",
+    "VCC33",
+    "VCC5V",
+    "VDDCPU_A",
+    "VSYS3V3",
+    "EMMC18",
+]
+shunt_resistances = [
+    0.03,
+    0.50,
+    0.50,
+    0.10,
+    0.05,
+    0.08,
+    0.13,
+    0.05,
+    0.027,
+    0.03,
+    0.5,
+]
+
+# From INA229 Datasheet
+VBUS_VOLTS_PER_COUNT = 195.3125e-6
+VSHUNT_VOLTS_PER_COUNT = 312.5e-9
+
+
+def get_channel_names(channel_mask):
+    """returns list of channel names based on channel_mask."""
+    retval = list()
+
+    for name in channel_names:
+        if channel_mask & 0x01:
+            retval.append(name)
+        channel_mask = channel_mask >> 1
+    return retval
+
+
+def get_resistances(channel_mask):
+    retval = list()
+
+    for ohms in shunt_resistances:
+        if channel_mask & 0x01:
+            retval.append(ohms)
+        channel_mask = channel_mask >> 1
+    return retval
+
+
+def get_shunt_currents(channel_mask, adc_measurements):
+    retvals = list()
+    resistances = get_resistances(channel_mask)
+    for i, resistance in enumerate(resistances):
+        retvals.append(
+            VSHUNT_VOLTS_PER_COUNT * adc_measurements[i] / resistance
+        )
+    return retvals
+
+
+def get_bus_voltages(adc_measurements):
+    retvals = list()
+    for measurement in adc_measurements:
+        retvals.append(VBUS_VOLTS_PER_COUNT * measurement)
+    return retvals
+
+
+def calc_power(voltages, currents):
+    retvals = list()
+    for i, volts in enumerate(voltages):
+        retvals.append(volts * currents[i])
+    return retvals
 
 
 def _parse_args():
@@ -45,23 +124,37 @@
         default=Path('plot.svg'),
         help='Output svg file.',
     )
+    parser.add_argument(
+        '-c',
+        '--output-csv',
+        type=Path,
+        default=Path(''),
+        help='Output csv file.',
+    )
     return parser.parse_args()
 
 
 def main(
     input_text: Path,
     output_svg: Path,
+    output_csv: Path,
 ) -> int:
     """Plot ADC values."""
     # pylint: disable=too-many-locals
+    print("Input %s, Output %s" % (input_text, output_svg))
+    if output_csv != Path(""):
+        print("Output csv file: %s" % output_csv)
 
     start_time: Optional[datetime] = None
     time_values = []
-    vbus_values: list[list[int]] = []
-    vshunt_values: list[list[int]] = []
+    vbus_values: list[list[float]] = []
+    vshunt_values: list[list[float]] = []
+    power_values: list[list[float]] = []
+
     for i in range(ADC_COUNT):
         vbus_values.append([])
         vshunt_values.append([])
+        power_values.append([])
 
     with input_text.open() as f:
         current_time = datetime.now()
@@ -89,21 +182,30 @@
 
             # Save the timestamp and vbus vshunt values for plotting.
             time_values.append((current_time - start_time).total_seconds())
-            vbus = list(int(i) for i in parts[12].split(','))
-            vshunt = list(int(i) for i in parts[14].split(','))
+            # vshunt and vbus fields appear to be flipped in the log stream
+            #  coming from the gonk.  These two fields (12,14) are flipped here
+            #  to compensate. b/349079209
+            vshunt = list(int(i) for i in parts[12].split(','))
+            vbus = list(int(i) for i in parts[14].split(','))
 
-            for i in range(ADC_COUNT):
-                vbus_values[i].append(vbus[i])
-                vshunt_values[i].append(vshunt[i])
+            ishunt: list[float] = get_shunt_currents(CHANNEL_MASK, vshunt)
+            bus_voltages = get_bus_voltages(vbus)
+            power: list[float] = calc_power(bus_voltages, ishunt)
+
+            for i, voltages in enumerate(bus_voltages):
+                vbus_values[i].append(voltages)
+                vshunt_values[i].append(ishunt[i])
+                power_values[i].append(power[i])
 
     # Plot vbus and vshunt values.
-    _fig, (ax1, ax2) = plt.subplots(
-        2, 1, layout='constrained', figsize=[11.67, 8.27]
+    _fig, (ax1, ax2, ax3) = plt.subplots(
+        3, 1, layout='constrained', figsize=[11.67, 8.27]
     )
 
     times = np.asarray(time_values)
     ax1.set_xlabel('Time (s)')
     ax2.set_xlabel('Time (s)')
+    ax3.set_xlabel('Time (s)')
 
     linewidth = 0.7
     ax1.set_ylabel('vbus')
@@ -111,18 +213,27 @@
         ax1.plot(
             times,
             np.asarray(vbus_values[i]),
-            label=f'vbus-{i}',
-            linestyle='dotted',
+            label=channel_names[i],
+            linestyle='solid',
             linewidth=linewidth,
         )
 
-    ax2.set_ylabel('vshunt')
+    ax2.set_ylabel('Ishunt')
     for i in range(ADC_COUNT):
         ax2.plot(
             times,
             np.asarray(vshunt_values[i]),
-            label=f'vshunt-{i}',
-            linestyle='dotted',
+            label=channel_names[i],
+            linestyle='solid',
+            linewidth=linewidth,
+        )
+    ax3.set_ylabel('Power')
+    for i in range(ADC_COUNT):
+        ax3.plot(
+            times,
+            np.asarray(power_values[i]),
+            label=channel_names[i],
+            linestyle='solid',
             linewidth=linewidth,
         )
 
@@ -130,10 +241,35 @@
     ax1.grid(True)
     ax2.legend()
     ax2.grid(True)
-
+    ax3.legend()
+    ax3.grid(True)
     plt.savefig(output_svg)
+
+    if output_csv != Path(""):
+        with output_csv.open("w+") as fd:
+            fd.write("ts")
+            for i in range(0, ADC_COUNT):
+                fd.write(
+                    ", %s_Volts, %s_Current, %s_Power"
+                    % (channel_names[i], channel_names[i], channel_names[i])
+                )
+            fd.write("\n")
+            for i, t in enumerate(time_values):
+                fd.write("%f" % t)
+                for j in range(0, ADC_COUNT):
+                    fd.write(
+                        ", %f, %f, %f"
+                        % (
+                            vbus_values[j][i],
+                            vshunt_values[j][i],
+                            power_values[j][i],
+                        )
+                    )
+                fd.write("\n")
+
     return 0
 
 
 if __name__ == '__main__':
+    print(_parse_args())
     sys.exit(main(**vars(_parse_args())))