What you've shown certainly is a zero-crossing detector. A couple things come to mind that might improve your situation:
If you have noise that is outside the band of your signal (which is almost certainly the case, since your input is a pure tone), then you can improve the signal-to-noise ratio by applying a bandpass filter around the signal of interest. The passband width of the filter should be chosen based on how precisely you know the sinusoid frequency a priori. By reducing the amount of noise present on the sinusoid, the number of false zero crossings and their jitter about the correct crossing times will be reduced.
- As a side note, if you don't have good information ahead of time, you could use a more sophisticated technique known as an adaptive line enhancer, which, as its name implies, is an adaptive filter that will enhance a periodic input signal. However, this is a somewhat advanced topic, and you typically have a good enough idea of your signal's frequency that this sort of approach isn't needed.
With respect to the zero-crossing detector itself, you might add some hysteresis to the process. This would prevent the generation of extra spurious measured crossings around the correct crossing instant. Adding hysteresis to the detector might look something like this:
if ((state == POSITIVE) && (sample[i - 1] > -T) && (sample[i] < -T))
{
// handle negative zero-crossing
state = NEGATIVE;
}
else if ((state == NEGATIVE) && (sample[i - 1] < T) && (sample[i] > T))
{
// handle positive zero-crossing
state = POSITIVE;
}
Effectively, you add some state to your zero-crossing detector. If you believe the input signal to have a positive value, you require that the signal dip down below a chosen threshold value -T in order to declare a real zero crossing. Likewise, you require that the signal rise back up above the threshold T in order to declare that the signal has oscillated back to positive again.
You could choose the thresholds to be whatever you want, but for a balanced signal like a sinusoid, it makes sense to have them be symmetric about zero. This approach can help give you a cleaner-looking output, but it will add some time delay due to the fact that you're actually measuring non-zero threshold crossings instead of zero crossings.
As pichenettes suggested in his answer, a phase-locked loop would be most likely the best way to go, as a PLL does pretty much exactly what you're trying to do. In short, you run a square wave generator that runs in parallel with the input sinusoid. The PLL makes periodic phase measurements on the sinusoid, then filters that stream of measurements in order to steer the instantaneous frequency of the square wave generator. At some point, the loop will (hopefully) lock, at which point the square wave should be locked in frequency and phase with the sinusoid of the input (with some amount of error, of course; nothing in engineering is perfect).