<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Fri, Feb 5, 2016 at 11:28 AM, John Van Ostrand <span dir="ltr"><<a href="mailto:john@vanostrand.com" target="_blank">john@vanostrand.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div>On Fri, Feb 5, 2016 at 8:19 AM, Jef Driesen <span dir="ltr"><<a href="mailto:jef@libdivecomputer.org" target="_blank">jef@libdivecomputer.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>On 2016-01-29 10:55, <a href="mailto:john@vanostrand.com" target="_blank">john@vanostrand.com</a> wrote:<br>
</span><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>
On Wed, Jan 27, 2016 at 10:21 AM, Jef Driesen<br>
<<a href="mailto:jef@libdivecomputer.org" target="_blank">jef@libdivecomputer.org</a>> wrote:<br>
</span><span><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
The dc_device_foreach() function must return the dives in reverse<br>
chronological order. This is a requirement for the download only new<br>
dives feature. When dives are returned in reverse chronological<br>
order, an application (or libdivecomputer itself) can simply stop<br>
downloading dives as soon as a previously downloaded dive is<br>
recognized. Very simple and efficient.<br>
</blockquote>
<br>
This is my loop in dc_device_foreach.<br>
<br>
    int i;<br>
    for (i = data->dive_count - 1; i > data->fp_dive_num; i--) {<br>
</span></blockquote>
<br>
Sorry, I missed that.<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Thus if we need to return the dives in reverse chronological order,<br>
it makes sense to process (and also download) the data in this<br>
order. Otherwise you'll end up with a rather inefficient<br>
implementation.<br>
<br>
I have to read through your documentation again, but processing the<br>
dives in reverse order might also make the recovery of corrupt dives<br>
easier. If the tail of a dive is missing, then it can run at most<br>
until the start of the next dive. And due the reverse order we<br>
already have that one.<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
3. Corrupt dive handling. In some cases (like a low battery<br>
especially in<br>
cold water) the computer resets during a dive. This results in a<br>
"start-dive" block written but no valid "end-dive" block written.<br>
We know<br>
information from the start of a dive (like date/time, gasses,<br>
profile start<br>
pointer, etc.) but we don't know information accumulated during or<br>
at the<br>
end of a dive (like end-profile pointer, max depth, min temp,<br>
etc.) I've<br>
taken to guessing the end of a dive by starting with the next<br>
dive's<br>
pre-dive-profile-pointer and backing up until we think we have the<br>
previous<br>
dive's end. We haven't resolved our differences on this. It seems<br>
to down<br>
to the question: Do we present a partial or broken profile in the<br>
interest<br>
of giving the diver something or do we give nothing in the<br>
interest of<br>
being accurate?<br>
</blockquote>
<br>
That's a difficult question. In general, I prefer to be very strict<br>
and simply fail on unexpected data. Usually this is the correct<br>
thing to do, because such unexpected data often turns out to be an<br>
wrong assumption in the code. So being strict helps finding bugs.<br>
But sometimes the data is really wrong (due to a firmware bug,<br>
running out of battery during the dive, etc), and if it happens<br>
frequently, then a workaround might indeed be necessary.<br>
<br>
It also depends on where the data "corruption" is located. If the<br>
information needed to move from one dive to another is good, but we<br>
are unsure about the contents of the dive, then we can return the<br>
bogus dive to the application and let the parser deal with it. You<br>
might get incorrect data for that particular dive, or even a failure<br>
to parse the dive. This would ensure that we can still download the<br>
other dives. But if the primary structure is damaged and we can no<br>
longer safely move to the next dive, then I think we should fail.<br>
</blockquote>
<br></div></div><span>
I've been working on code to retrieve corrupt dives and it's becoming<br>
unwieldy. In working backwards from a future dive's profile I have to<br>
remove inter-dive events which vary in length. it's possible that two<br>
events might match the data and the code would have to decide which<br>
event to use. The code also needs to do range checking, like ensuring<br>
it doesn't exceed the malloc'ed memory and things like ring-buffer<br>
wrapping. I should also remove any surface time (the time the DC still<br>
stores samples in case you re-descend.)<br>
</span></blockquote>
<br>
I think the problem of recovering corrupt samples becomes a lot easier if we can defer it to the parser. At the device layer we simply use the begin/end/pre-dive pointers. If the tail of the dive is missing (e.g. end pointer equal to 0xFFFFFFFF), we assume it runs to the start of the next dive. And then it's up to the parser to deal with this.<br>
<br>
In the parser we no longer have to worry about ringbuffer wrapping, only the end of the buffer. That should makes things already easier.<span><br></span></blockquote><div><br></div></div></div><div>I agree and after sleeping on it I came the same conclusion. It also allows for another subtle feature.<br><br></div><div>You had mentioned in the past that an application could choose to download dives, store the dive blob to parse later. With that in mind I created the "dctool_export" patch so I could more easily extract dive blobs to test with the new incomplete dive parser. I've been testing various algorithms and it's easier to do outside of LDC and Subsurface.<br> <br></div><span><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
There are ways to make this better but I think I'm going to have to<br>
think about it for a bit. I'm considering pulling that code. Any ideas<br>
on what I should do with the code so it isn't lost. An #ifdef maybe?<br>
<br>
I've done some work and I may have a decent algorithm to backtrack<br>
over the inter-dive events to get to good data. It goes as far as<br>
removing good data in an effort to eliminate giving bad data.<br>
<br>
To refresh the issue: it has never been finding the beginning of data.<br>
The issue has been finding the end. ‎We do have the start of the<br>
next dive or, if no next dive, we have the ring buffer head pointer.<br>
What happens if we simply use those pointers is that interdive data,<br>
the data recorded between dives like power on events, is interpreted<br>
as profile data. The profile then looks erratic at the end. So the<br>
trick is to backtrack to find the real end of good data. Ideally wed<br>
be able to do that accurately but if we have to remove good data to be<br>
sure it would result in only loosing a few seconds of dive samples.<br>
<br>
Now that I've thought about the problem more and identified many of<br>
the issues I have a standalone test program that's doing a good job of<br>
backtracking to get to good data. I've used recursion to simplify the<br>
code and derive at an optimal solution.  In the dozen examples I'm<br>
using it's working on 11 of them well enough to remove all non profile<br>
data. The 12th leaves several seconds of non profile data.<br>
</blockquote>
<br></span>
Don't we have the pre-dive pointer to indicate where the interdive data starts? Thus if the tail of a dive is missing, then the end is not really at the start of the next dive, but at the start of the interdive data. So the risk of trying to interpret interdive data as sample data disappears. Or am I missing something here?<span><font color="#888888"><br></font></span></blockquote><br clear="all"></span></div>I wish it were that easy. The pre-dive pointer is the same on the incomplete dive as it is on the next dive. The DC stores the ring buffer head and pre-dive pointers in a config block. It seems to only update the pre-dive pointer when a dive is successfully ended. So a dive after an incomplete dive has the same pre-dive pointer but uses the updated ring-buffer head.<br><br></div><div class="gmail_extra">I've worked with several tactics to find the real dive end, to make it easier this is what an incomplete dive profile might look like:<br><br></div><div class="gmail_extra">[0 or more dive samples with dive events]<br>[0 or more surface samples, these have mostly -0 values for ascent and depth change]<br></div><div class="gmail_extra">[possibly non-sample, random memory data] (I have one dive like this)<br></div><div class="gmail_extra">[1 or more inter-dive samples]<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">The tactics are:<br><br></div><div class="gmail_extra">1. Back parse inter-dive events. This has worked well except it leaves surface samples.<br></div><div class="gmail_extra">2. Back parse surface samples. This works mostly except in the one case where random data is there.<br></div><div class="gmail_extra">3. Parse forward and watch data for reasonablenessike temp sample not changing much, reasonable depth and ascent rates, reasonable depths. Stop when it goes out of bounds.<br></div><div class="gmail_extra">3. Parse forward and look for surface samples. A diver might surface for a few minutes. So it's the last one we care about</div></div></blockquote><div><br></div><div>I've added the inter-dive backparse function and submitted a patch and added a warning when we process dives so there's some indication it's happening. This looks like it does a good enough job. The effect is that incomplete dives look reasonable, i.e. without the crazy thrashing of depth and temp at the end of dives. What does happened as expected is an immediate end to the dive which would be interpreted as an immediate ascent.<br><br></div><div>There is still a dive with an erratic appearance. It looks like the DC wrote some random memory as profile data.<br><br></div><div>I'm happy with this. Can you consider this for merging to master?  <br></div><div> <br></div></div><div><div dir="ltr"><div>John Van Ostrand<br></div><div>At large on sabbatical<br></div><br></div></div>
</div></div>