/*
Copyright (C) 2011 The University of Michigan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Please send inquiries to powertutor@umich.edu
*/
package edu.umich.PowerTutor.phone;
import edu.umich.PowerTutor.components.*;
import edu.umich.PowerTutor.components.LCD.LcdData;
import edu.umich.PowerTutor.components.OLED.OledData;
import edu.umich.PowerTutor.components.CPU.CpuData;
import edu.umich.PowerTutor.components.Audio.AudioData;
import edu.umich.PowerTutor.components.GPS.GpsData;
import edu.umich.PowerTutor.components.Wifi.WifiData;
import edu.umich.PowerTutor.components.Threeg.ThreegData;
import edu.umich.PowerTutor.components.Sensors.SensorData;
import android.content.Context;
public class DreamPowerCalculator implements PhonePowerCalculator {
protected PhoneConstants coeffs;
public DreamPowerCalculator(Context context) {
this(new DreamConstants(context));
}
protected DreamPowerCalculator(PhoneConstants coeffs) {
this.coeffs = coeffs;
}
public double getLcdPower(LcdData data) {
return data.screenOn ?
coeffs.lcdBrightness() * data.brightness + coeffs.lcdBacklight() : 0;
}
public double getOledPower(OledData data) {
throw new RuntimeException("getOledPower() should not be called for Dream");
}
public double getCpuPower(CpuData data) {
/* Find the two nearest cpu frequency and linearly interpolate
* the power ratio for that frequency.
*/
double[] powerRatios = coeffs.cpuPowerRatios();
double[] freqs = coeffs.cpuFreqs();
double ratio;
if(powerRatios.length == 1) {
ratio = powerRatios[0];
} else {
double sfreq = data.freq;
if(sfreq < freqs[0]) sfreq = freqs[0];
if(sfreq > freqs[freqs.length - 1]) sfreq = freqs[freqs.length - 1];
int ind = upperBound(freqs, sfreq);
if(ind == 0) ind++;
if(ind == freqs.length) ind--;
ratio = powerRatios[ind - 1] + (powerRatios[ind] - powerRatios[ind - 1]) /
(freqs[ind] - freqs[ind - 1]) *
(sfreq - freqs[ind - 1]);
}
return Math.max(0, ratio * (data.usrPerc + data.sysPerc));
}
public double getAudioPower(AudioData data) {
return data.musicOn ? coeffs.audioPower() : 0;
}
public double getGpsPower(GpsData data) {
double result = 0;
double statePower[] = coeffs.gpsStatePower();
for(int i = 0; i < GPS.POWER_STATES; i++) {
result += data.stateTimes[i] * statePower[i];
}
return result;
}
public double getWifiPower(WifiData data) {
if(!data.wifiOn) {
return 0;
} else if(data.powerState == Wifi.POWER_STATE_LOW) {
return coeffs.wifiLowPower();
} else if(data.powerState == Wifi.POWER_STATE_HIGH) {
double[] linkSpeeds = coeffs.wifiLinkSpeeds();
double[] linkRatios = coeffs.wifiLinkRatios();
double ratio;
if(linkSpeeds.length == 1) {
/* If there is only one set speed we have to use its ratio as we have
* nothing else to go on.
*/
ratio = linkRatios[0];
} else {
/* Find the two nearest speed/ratio pairs and linearly interpolate
* the ratio for this link speed.
*/
int ind = upperBound(linkSpeeds, data.linkSpeed);
if(ind == 0) ind++;
if(ind == linkSpeeds.length) ind--;
ratio = linkRatios[ind - 1] + (linkRatios[ind] - linkRatios[ind - 1]) /
(linkSpeeds[ind] - linkSpeeds[ind - 1]) *
(data.linkSpeed - linkSpeeds[ind - 1]);
}
return Math.max(0, coeffs.wifiHighPower() + ratio * data.uplinkRate);
}
throw new RuntimeException("Unexpected power state");
}
public double getThreeGPower(ThreegData data) {
if(!data.threegOn) {
return 0;
} else {
switch(data.powerState) {
case Threeg.POWER_STATE_IDLE:
return coeffs.threegIdlePower(data.oper);
case Threeg.POWER_STATE_FACH:
return coeffs.threegFachPower(data.oper);
case Threeg.POWER_STATE_DCH:
return coeffs.threegDchPower(data.oper);
}
}
return 0;
}
public double getSensorPower(SensorData data) {
double result = 0;
double[] powerUse = coeffs.sensorPower();
for(int i = 0; i < Sensors.MAX_SENSORS; i++) {
result += data.onTime[i] * powerUse[i];
}
return result;
}
/* Returns the largest index y such that if x were inserted into A (which
* should already be sorted) at y then A would remain sorted.
*/
protected static int upperBound(double[] A, double x) {
int lo = 0;
int hi = A.length;
while(lo < hi) {
int mid = lo + (hi - lo) / 2;
if(A[mid] <= x) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}
}