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
Iterative solution to stop time
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;
}
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);
}
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);
}
Comment