When it sends the 3 bytes, are there any other's being sent (CR LF)? If not, I am interested in how you handle making sure you are in sync?
I had been timing the end of the frame, looking for when there was no data. It's not not a clean setup so I moved onto just pulling in what ever is on the serial port and placing it into a FIFO. I assume the first reading is in sync and pull however much data I am expecting (depending on ASCII/Binary and trigger type). If the converted data is not within bounds, I then index through the FIFO one location at a time, testing each combination for an in-range value. Once I find it, I consider it back in sync, until the another reading falls out of range. It's really only a problem in the one mode where there are no CRs ( I always disable the LF). For these, I just search for the CR to resync. Once it's locked, it seems fairly reliable. I was a little concerned after adding the jumper to allow for 19.2K that the error rates may be unreasonable but I've ran the meter for several hours at a time without missing.
Readings that are found to be in-range are placed into another FIFO that is read by the main thread. The lowest level FIFO can store about 5 seconds of data. The second layer after conversion to floating point will hold about 6 seconds. The main thread runs fairly slow which updates the screen.
****
Just to add to this, the lowest level thread executes every 50ms. At 19.2K 8,N,1 it's 521us/byte, so every 50ms it can have up to 100 bytes loaded into the FIFO, which is 10,000 bytes deep. (math is lacking 10K/100samp*50ms, yeah 5 seconds )
I still measure the data rate the same way, measuring how often an in-range reading occurs but you can imagine there is a fair bit of error in the measurement running the threads this slow and letting the hardware / OS buffer things. So I now also look at the over all data rate as well, seeing over the course of how ever long I run, how much data has been collected. It just made the code cleaner to architect it this way.