var rvals24 = [
	1, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2, 2.2, 2.4, 2.7, 3, 3.2, 3.3, 3.6,
	3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1, 10
];

var kCellHi = 1.55;
var kCellLo = 0.9;
var kCellNormal = 1.25;
var kVsenHi = 1.9;
var kVsenLo = 0.95;
var kRegDrop = 1.25;

function convert_power(value)
{
	var last = value.charAt(value.length - 1);

	if (last == 'k' || last == 'K') {
		return Number(value.substring(0, value.length - 1)) * 1000;
	}
	else if (last == 'm' || last == 'M') {
		return Number(value.substring(0, value.length - 1)) * 1000000;
	}
	else {
		return Number(value);
	}
}


function power_notation(a)
{
	if ((a / 1000000) >= 1) {
		return a / 1000000 + 'M';
	}
	else if ((a / 1000) >= 1) {
		return a / 1000 + 'K';
	}
	else {
		return a;
	}
}


function validate_speed_values(cells, capacity, hours)
{
	if (cells < 4 || cells > 20) {
		return 'ERROR: cell count should be between 4 and 20';
	}
	else if (capacity < 0.5 || capacity > 3) {
		return 'WARNING: unlikely cell capacity for PPA\n';
	}
	else if (hours < 1) {
		return 'WARNING: unsafe charge speed for NiMH cells\n';
	}
	else if (hours > 16) {
		return 'WARNING: slow charge speed\n';
	}
	else {
		return '';
	}
}


function round_1_place(val)
{
	return Math.floor(Math.round(val * 10)) / 10;
}


function round_2_places(val)
{
	return Math.floor(Math.round(val * 100)) / 100;
}


function round_3_places(val)
{
	return Math.floor(Math.round(val * 1000)) / 1000;
}


function resistor_divider(ra, rb)
{
	return rb / (ra + rb);
}


function percent_difference(a, b)
{
	// Make sure 'b' is larger than 'a'
	if (a > b) {
		var temp = b;
		b = a;
		a = temp;
	}

	return 100 * Math.abs((b / a) - 1);
}


function calculate_supply(cells, ldo, fast_charging)
{
	return (cells * kCellHi) + 				// peak drop across cells
			kRegDrop + 						// R2
			(ldo ? 1 : 2.5) + 				// U1
			(fast_charging ? 1.4 : 0.7);	// diodes in charge path
}


function calculate_r1(capacity, supply)
{
	var target_amps = capacity / 10;
	for (j = 1; j <= 1000; j *= 10) {
		for (i = 0; i < rvals24.length; ++i) {
			var r1 = rvals24[i] * j;
			var amps = (supply / r1);
			if (amps < target_amps) {
				return 'R1 = ' + round_1_place(r1) + ' Ohms or higher';
			}
		}
	}

	return 'Sorry, no R1 value under 10K will work';
}


function calculate_r2(capacity, hours)
{
	var target_amps = capacity / hours;
	for (j = 0.1; j <= 1000; j *= 10) {
		for (i = 0; i < rvals24.length; ++i) {
			var r2 = rvals24[i] * j;
			var amps = (kRegDrop / r2);
			if (amps < target_amps) {
				return r2;
			}
		}
	}

	return 0;
}


function format_divider_vals(ra, rb, v_hi, v_lo)
{
	ra = Math.round(ra * 100) / 100;
	rb = Math.round(rb * 100) / 100;
	return 'R4 = ' + power_notation(ra) +
			', R5 = ' + power_notation(rb) + '\t(Vsen: ' + 
			round_2_places(v_lo) + ' - ' +
			round_2_places(v_hi) + 'V)\n';
}


// Calculate reasonable resistor pairs for the Vsen voltage divider.
// The accepted dividers meet these conditions:
// 
// a) The midpoint of the divider must be within 5% of the Vsen pin's
//    worst-case sensitivity range under normal pack conditions.
// b) The divider draws less than 1/3600 the current required to
//    drain the pack in 1 hour.
// c) The divider will allow at least 0.1mA through it to swamp the
//    bias current of the Vsen pin.
//
// Condition b is based on the fact that 3600 hours is 150 days, 5x
// the 30-day normal self-discharge time for NiMH cells, so the
// self-discharge time dominates.  A consequence of this condition is
// that the minimum cell capacity we can return divider values for is
// 360mAh.  Below that point, we can't meet our maximum current draw 
// goal; the divider has to be hungrier than that to meet the minimum
// draw set by condition c.

function calculate_divider(cells, capacity)
{
	var v_hi = kCellHi * cells;
	var v_lo = kCellLo * cells;
	var v_normal = kCellNormal * cells;
	var min_hours = 3600;
	var a_hi = capacity / min_hours;
	var a_lo = 0.0001;	// 0.1mA

	// Ensure sanity of the current draw goals
	if (a_lo > a_hi) {
		return 'ERROR: Cells must be over ' + (min_hours / 10) + 
				'mAh to meet divider current draw goals.\n';
	}

	var ret = '';
	var found = false;
	var seen = new Object();
	var ra, rb, a_div, div, pct_diff, pair, div_hi, div_lo;
	for (target_div = 1; target_div < 100; target_div += 0.1) {
		if (((v_hi / target_div) > kVsenHi) ||
				((v_lo / target_div) < kVsenLo)) {
			continue;
		}

		// Found a division factor that puts the Vsen within the its
		// worst-case sensitivity range for normal pack voltages.  Now
		// find divider pairs that give this division factor, within 5%.
		for (j = 0; j < rvals24.length; ++j) {
			for (ix = 1; ix <= 100; ix *= 10) {
				for (i = 0; i < rvals24.length; ++i) {
					ra = rvals24[i] * ix * 1000;	// 1K to 1M
					rb = rvals24[j] * 1000;			// 1K to 10K
					a_div = v_normal / (ra + rb);
					div = resistor_divider(ra, rb);
					div_hi = v_hi * div;
					div_lo = v_lo * div;
					pct_diff = percent_difference(div, 1 / target_div);
					pair = ra + ':' + rb;
					if ((a_div > a_lo) && (a_div < a_hi) &&
							(div_hi < kVsenHi) && (div_lo > kVsenLo) &&
							(pct_diff <= 5.0) && !seen[pair]) {
						seen[pair] = 1;
						ret += format_divider_vals(ra, rb, div_hi, div_lo);
						found = true;
						ix = 1000;
						break;
					}
				}
			}
		}
	}
	
	if (!found) {
		ret += 'No suitable divider values could be found!\n';
	}

	return ret;
}


function calculate_time_backup(hours)
{
	var mins = hours * 60;
	if (mins > 283) {
		return 'ERROR: not fast-charging!\n';
	}
	else if (mins > 247) {
		return 'R6 open, R7 open, R8 open (283 mins)\n';
	}
	else if (mins > 212) {
		return 'R6 short, R7 open, R8 open (247 mins)\n';
	}
	else if (mins > 177) {
		return 'R6 open, R7 short, R8 open (212 mins)\n';
	}
	else if (mins > 141) {
		return 'R6 short, R7 short, R8 open (177 mins)\n';
	}
	else if (mins > 106) {
		return 'R6 open, R7 open, R8 short (141 mins)\n';
	}
	else if (mins > 71) {
		return 'R6 short, R7 open, R8 short (106 mins)\n';
	}
	return 'R6 open, R7 short, R8 short (71 mins)\n';
}


function speed_select_handler(form)
{
	var cells = Number(form.cells.value);
	var capacity = Number(form.capacity.value) / 1000;
	var hours = Number(form.hours.value);
	var ldo = form.ldo[1].checked;
	var time_backup = form.backup[0].checked;
	var fast_charging = (hours < 10);

	form.answer.value = validate_speed_values(cells, capacity, hours);
	if (form.answer.value.substr(0, 5) == 'ERROR') {
		return false;
	}

	var supply = calculate_supply(cells, ldo, fast_charging);
	supply = Math.round(supply * 100) / 100;
	form.answer.value += 'Supply >= ' + supply + 'V\n\n';

	form.answer.value += 'U1 = ' + (ldo ? 'LM1086' : 'LM317') + '\n';
	
	if (supply > 20) {
		form.answer.value += 'Add U2 and U4\n';
	}
	
	if (fast_charging) {
		form.answer.value += 'Add U3\n';
		form.answer.value += '\n' + calculate_r1(capacity, supply) + '\n';
	}
	else {
		form.answer.value += '\nR1, R4 and R5 = open\n';
	}

	var r2 = calculate_r2(capacity, hours);
	if ((r2 > 0) && fast_charging) {
		var amps = kRegDrop / r2;
		form.answer.value += 'R2 = ' + round_1_place(r2) + ' Ohms (' +
				round_2_places(capacity * 60 / amps);
		form.answer.value += ' min. fast charging, ' +
				round_2_places(amps * amps * r2) + 'W, ' +
				round_2_places(amps) + 'A)\n';
		form.answer.value += '\n' + calculate_divider(cells, capacity) +
				'\n';
		if (time_backup) {
			form.answer.value += calculate_time_backup(hours);
		}
		else {
			form.answer.value +=
					'Sorry, can\'t calculate R6 and R8 values yet\n';
		}
	}
	else if (r2 > 0) {
		var amps = kRegDrop / r2;
		form.answer.value += 'R2 = ' + round_1_place(r2) + ' Ohms (' +
				round_1_place(capacity / amps);
		form.answer.value += ' hours charging time, ' +
				round_2_places(amps * amps * r2) + 'W, ' +
				round_1_place(amps * 1000) + 'mA)\n';
	}
	else {
		form.answer.value += 'Sorry, no R2 value under 10K will work';
	}

	return false;	// make browser avoid actual "submit" operation
}


