<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div class="moz-cite-prefix">Attached is the first 32 seconds of the
      driver output after applying the patch which fixes the battery
      scaling problem for this UPS.<br>
      <pre class="moz-signature" cols="72">Matthew Stapleton
Email: <a class="moz-txt-link-abbreviated" href="mailto:matthew4196@gmail.com">matthew4196@gmail.com</a>
</pre>
      On 10/08/14 01:27, Charles Lepple wrote:<br>
    </div>
    <blockquote
      cite="mid:3A6A5927-F9DC-40AD-8C89-F37DE9DF1E6A@gmail.com"
      type="cite">
      <pre wrap="">On Aug 8, 2014, at 9:15 AM, Charles Lepple <a class="moz-txt-link-rfc2396E" href="mailto:clepple@gmail.com"><clepple@gmail.com></a> wrote:

</pre>
      <blockquote type="cite">
        <pre wrap="">On Aug 7, 2014, at 10:52 PM, Charles Lepple <a class="moz-txt-link-rfc2396E" href="mailto:clepple@gmail.com"><clepple@gmail.com></a> wrote:

</pre>
        <blockquote type="cite">
          <pre wrap="">On Aug 7, 2014, at 10:18 PM, Matthew Stapleton <a class="moz-txt-link-rfc2396E" href="mailto:matthew4196@gmail.com"><matthew4196@gmail.com></a> wrote:

</pre>
          <blockquote type="cite">
            <pre wrap="">I just got a Cyberpower SOHO Value 1200 ELCD UPS and even with nut 2.7.2, it appears to report battery voltage too low due to the battery scaling function (In drivers/cps-hid.c). Even though the ups has usb id: 0764:0501, UPS.PowerSummary.Voltage reports 26.6 for the 24V batteries so when the 0.667 battery scale is applied that goes down to 17.7V.
</pre>
          </blockquote>
          <pre wrap="">
I'm wondering if that scale factor is only needed for that one Dynex UPS model. We should be able to do a string match, which is ugly, but should work (especially if we default to no scaling).

Logged: <a class="moz-txt-link-freetext" href="https://github.com/networkupstools/nut/issues/142">https://github.com/networkupstools/nut/issues/142</a>
</pre>
        </blockquote>
        <pre wrap="">
It's not just Dynex models - there are some early "UPS VALUE" and "CP 1000D" units that seem to need the correction factor. I'll try to add a check that looks to see if the battery voltage makes sense.
</pre>
      </blockquote>
      <pre wrap="">
Matthew, can you try the attached patch, and run the driver it with -DDDD?

<a class="moz-txt-link-freetext" href="https://github.com/networkupstools/nut/commit/c8950dee9c91ce45d05d8f220ea26891fb92329e">https://github.com/networkupstools/nut/commit/c8950dee9c91ce45d05d8f220ea26891fb92329e</a>

Also, if anyone else has a Cyberpower UPS with the 0501 productID, I'd appreciate any additional testing.

Thanks,

</pre>
      <br>
      <fieldset class="mimeAttachmentHeader"><legend
          class="mimeAttachmentHeaderName">cps_hid_scale.patch</legend></fieldset>
      <br>
      <pre wrap="">commit c8950dee9c91ce45d05d8f220ea26891fb92329e
Author: Charles Lepple <a class="moz-txt-link-rfc2396E" href="mailto:clepple+nut@gmail.com"><clepple+nut@gmail.com></a>
Date:   Sat Aug 9 11:19:27 2014 -0400

    usbhid-ups (CPS): determine battery.voltage scale factor at runtime
    
    If the battery.voltage reading is greater than 1.4x battery.voltage.nominal,
    apply a scale factor of 2/3 to bring the voltage back in line.
    
    Closes networkupstools/nut#142

diff --git a/drivers/cps-hid.c b/drivers/cps-hid.c
index a6d64ba..78c952f 100644
--- a/drivers/cps-hid.c
+++ b/drivers/cps-hid.c
@@ -28,29 +28,36 @@
 #include "cps-hid.h"
 #include "usb-common.h"
 
-#define CPS_HID_VERSION      "CyberPower HID 0.3"
+#define CPS_HID_VERSION      "CyberPower HID 0.4"
 
 /* Cyber Power Systems */
 #define CPS_VENDORID 0x0764
 
-/*
+/*! Battery voltage scale factor.
  * For some devices, the reported battery voltage is off by factor
  * of 1.5 so we need to apply a scale factor to it to get the real
  * battery voltage. By default, the factor is 1 (no scaling).
  */
 static double  battery_scale = 1;
+static int     might_need_battery_scale = 0;
+static int     battery_scale_checked = 0;
+
+/*! If the ratio of the battery voltage to the nominal battery voltage exceeds
+ * this factor, we assume that the battery voltage needs to be scaled by 2/3.
+ */
+static const double battery_voltage_sanity_check = 1.4;
 
 static void *cps_battery_scale(USBDevice_t *device)
 {
-       battery_scale = 0.667;
+       might_need_battery_scale = 1;
        return NULL;
 }
 
 /* USB IDs device table */
 static usb_device_id_t cps_usb_device_table[] = {
-       /* 900AVR/BC900D, CP1200AVR/BC1200D */
+       /* 900AVR/BC900D */
        { USB_DEVICE(CPS_VENDORID, 0x0005), NULL },
-       /* Dynex DX-800U? */
+       /* Dynex DX-800U?, CP1200AVR/BC1200D, CP825AVR-G, CP1000AVRLCD, CP1000PFCLCD, CP1500C, CP550HG, etc. */
        { USB_DEVICE(CPS_VENDORID, 0x0501), &cps_battery_scale },
        /* OR2200LCDRM2U, OR700LCDRM1U, PR6000LCDRTXL5U */
        { USB_DEVICE(CPS_VENDORID, 0x0601), NULL },
@@ -59,12 +66,48 @@ static usb_device_id_t cps_usb_device_table[] = {
        { -1, -1, NULL }
 };
 
+/*! Adjusts @a battery_scale if voltage is well above nominal.
+ */
+static void cps_adjust_battery_scale(double batt_volt)
+{
+       const char *batt_volt_nom_str;
+       double batt_volt_nom;
+
+       if(battery_scale_checked) {
+               return;
+       }
+
+       batt_volt_nom_str = dstate_getinfo("battery.voltage.nominal");
+       if(!batt_volt_nom_str) {
+               upsdebugx(2, "%s: 'battery.voltage.nominal' not available yet; skipping scale determination", __func__);
+               return;
+       }
+
+       batt_volt_nom = strtod(batt_volt_nom_str, NULL);
+       if(batt_volt_nom == 0) {
+               upsdebugx(3, "%s: 'battery.voltage.nominal' is %s", __func__, batt_volt_nom_str);
+               return;
+       }
+
+       if( (batt_volt / batt_volt_nom) > battery_voltage_sanity_check ) {
+               upslogx(LOG_INFO, "%s: battery readings will be scaled by 2/3", __func__);
+               battery_scale = 2.0/3;
+       }
+
+       battery_scale_checked = 1;
+}
+
 /* returns statically allocated string - must not use it again before
    done with result! */
 static const char *cps_battvolt_fun(double value)
 {
        static char     buf[8];
 
+       if(might_need_battery_scale) {
+               cps_adjust_battery_scale(value);
+       }
+
+       upsdebugx(5, "%s: battery_scale = %.3f", __func__, battery_scale);
        snprintf(buf, sizeof(buf), "%.1f", battery_scale * value);
 
        return buf;
</pre>
    </blockquote>
    <br>
  </body>
</html>