So, here's two versions of the stop time calculation. The closed form is definitely faster, but has all those ugly problem cases that never seem to show up in real life. If I could demonstrate mathematically that they can't happen I'd be a lot happier.

Closed-form solution to stop time

/* time at Pamb before it becomes safe to ascend to Ptarget (anything from zero to INFINITY) */

static float stop_old(diver_t& diver, const gas_t gas, const float Pamb, const float Ptarget, const float gf) {

const float Palv = gas.PalvN2(Pamb); // alveolar gas pressure (bar) during interval

float t = 0.0f;

for (uint8_t i = 0; i < TISSUE_COUNT; ++i) {

float Pigtol = diver.Pigtol(Ptarget, i, gf);

if (diver.Pt[i] <= Pigtol /* tissue is already safe.. */) {

if (Palv >= Pigtol /* PROBLEM CASE */)

{

// ..but is on-gassing and will eventually reach saturation

}

continue; // tissue is already safe to ascent to Ptarget, so assume that it'll carry on being safe for ever

}

if (Palv >= diver.Pt[i] /* tissue is on-gassing.. */) {

if (Palv >= Pigtol /* PROBLEM CASE */)

{

// ..and will eventually, but not immediately, reach saturation

}

return INFINITY; // tissue is on-gassing, so assume that if it's not already safe it'll never become so.

}

if (Palv >= Pigtol /* tissue can never off-gas enough */) {

return INFINITY;

}

// solve haldane to find out how long it will take for the tissue to become safe

const float Tt = (-1.0f / constants.k_N2[i]) * LOG((Palv - Pigtol) / (Palv - diver.Pt[i]));

t = MAX(t, Tt);

}

t = CEIL(t);

diver.loadInert(gas, Pamb, Pamb, t);

return t;

}

Iterative solution to stop time

/* wait at Pamb until it becomes safe to ascend to Ptarget, return the time waited */

static float stop(diver_t& diver, const gas_t gas, const float Pamb, const float Ptarget, const float gf) {

const float Palv = gas.PalvN2(Pamb);

// check if not already off-gassed enough to ascend to Ptarget(gf) and make sure that

// off-gassing enough is even a possibility. save Pigtol(Ptarget, gf) for future

// reference.

float Pigtol[16];

bool safe = true;

uint16_t Tstop = 0;

for (uint8_t i = 0; i < TISSUE_COUNT; ++i) {

Pigtol[i] = diver.Pigtol(Ptarget, i, gf);

safe = safe && diver.Pt[i] <= Pigtol[i];

if (Palv > Pigtol[i] /* off-gassing to Pigtol isn't going to be happening */) {

return INFINITY;

}

}

// run simulation in 1min steps until diver is safe at Ptarget

while (!safe) {

safe = true;

for (uint8_t i = 0; i < TISSUE_COUNT; ++i) {

diver.Pt[i] = Palv - (Palv - diver.Pt[i]) * EXP(-constants.k_N2[i]); // haldane

safe = safe && diver.Pt[i] <= Pigtol[i];

}

++Tstop;

}

// all tissues are now safe at Ptarget(gf) so assume that they're safe

// at all points during the ascent. no idea if this is a good assumption or not.

return float(Tstop);

}