# getting a smoother real-time frequency estimate using the phase for a triangle wave

I'm using a phototransistor and an optical chopper to measure the angular velocity of a wheel, much like how mechanical mice used to work. The signal looks like a triangle wave with some noise in it, and I'd like to get smooth and fairly low-latency measurements, between 1 and 100 hertz. This is for a user interface, so low-latency here means a few milliseconds.

I started by counting zero crossings, which works well for higher speeds, but at low speeds there are few crossings. So I'd like to extract phase information from the signal to get a better estimates at low speeds.

When counting zero crossings, I get two ticks per cycle and no information in between. I hacked up a way to get four ticks per cycle, somewhat unevenly spaced:

  enum Phase { night, morning, day, evening };
const int phasesPerCycle = evening + 1;

Phase phase = night;

Phase findPhase(float v) {
// To avoid noise jiggling the phase back and forth without cycling, always move forward.
// (Also, the threshold for ending day or night is closer to zero.)
switch (phase) {
case morning:
phase = (v > threshold) ? day : morning;
break;
case day:
phase = (v < threshold * 0.9) ? evening : day;
break;
case evening:
phase = (v < -threshold) ? night : evening;
break;
case night:
phase = (v > -threshold * 0.9) ? morning : night;
break;
}
return phase;
}



This gives me a bit more information, but I'm wondering if there's a way to do better. Is there a way to extract a continuous estimate of the phase, as a floating point number that increases from 0 to 1 for each cycle? What sort of search terms could I use to look into research about this?

An alternate approach would be to change the hardware by adding gearing to increase the speed of the optical chopper wheel, or increase the size of the wheel. However, I'm wondering what can be done just using better signal processing with the signal I have.

(Edit: in the end I decided that having two phototransistors ninety degrees out of phase is better than what I was trying to do here. Leaving the question as is, though, for reference.)

• What is the value of threshold? Or what is the acceptable range of values for threshold? This thing looks kinda like a 4-quadrant quantizer with feedback, which gives it hysteresis. I understand that this puts in a little "slop" or "dead zone", which is a non-linearity with hysteresis. Apr 8, 2022 at 5:52
• Currently it's 150, where I expect a peak-to-peak amplitude of about 500, but quite a bit shorter for faster frequencies. (Chosen empirically.) Apr 8, 2022 at 22:42
• Do you know the amplitude range of the triangle wave? If so, can the problem be reduced to noise-robust estimating the slope of the triangle, taking care of «wrap around» and infering instantaneous frequency from that? May 11, 2022 at 8:14
• Yes, I had been thinking of something like that. The slope seems noisy though. Taking the slope is basically a high-pass filter. May 12, 2022 at 14:41

A phase detector is created by taking the product of the signal with a sinusoidal reference, and low pass filtering to remove the sum frequency term: since $$2\cos(\alpha)\cos(\beta) = \cos(\alpha+\beta) + cos(\alpha-\beta)$$; with two signals at same frequency but different phase, we would get twice the frequency and the difference which is the phase alone). The FFT does all this for us, with each frequency bin as a reference.