Have you thought of doing it a slightly different way? Most sensors have some jitter, so averaging out several samples gives a better result. Also, the duty cycle often isn't constant. To be really accurate, you have to take width of a positive half-cycle and add it to the width of a negative-half cyle.

I had enormous problems measuring the speeds of small fans using PULSIN. In order to get a stable, accurate result I had to average so many samples together the routine was taking too long.

Below is an ISR written in assembly that does the job perfectly.
It is run on a timer interrupt, so you can use any pin. The interrupt period is adjusted to be less than the period of the smallest half-cycle you need to measure.
In the example below, it is 1mSec. TPE is a flag that gets set after a certain number of interrrupts (1000 in this case), so it will measure over 1 second.

To use it, you clear Fanclock and Fan1Counter and TPE

Your main loop checks for TPE being set. If it is, it transfers the contents of Fan1Counter to another variable (call it SPEED), clears FanClock, Fan1Counter and TPE.

Now the variable SPEED always has the number of transitions (twice the pulse rate). It runs totally in the background. The rest of your program really doesn't need to know your timer exists. All it has to do is read SPEED and clear the variables.

Since you are getting ~4K pulses / mile, you will get a little less than 1 pulse per foot of travel. 60MPH = 88 ft/sec. So you could count for a little less than 1/2 second (change 3E8 to something a little smaller than 1F4) and you will get an answer in MPH. No fancy math required.


Code:
ReadTach
 
         movlw   0xEC              ; Reload TMR0 with 65535 - 10000
         movwf   TMR0H
         movlw   0x7E
         movwf   TMR0L
 
         bcf     INTCON,2           ; Clear the TIMER0 interrupt flag
 
 
 
 
         btfss   TPE,0
         bra     CheckFans
         bra     DoneForNow
 
CheckFans         
         infsnz  FanClock        
         incf    FanClock + 1
 
         movlw   0x03 - 1          ; 1000 = 0x3E8, but must have one less
         cpfsgt  FanClock + 1      ; to compare with greater than
         bra     FanRoutine
         movlw   0xE8 - 1          ; Again, subtract one
         cpfsgt  FanClock
         bra     FanRoutine
         bsf     TPE,0
         clrf    FanClock
         clrf    FanClock + 1
         bra     DoneForNow
 
FanRoutine
 
         movf    PORTB,0,0
         movwf   Temp,0
         xorwf   OldPortB,0,0    ' XOR current with old to detect change
         movwf   changedB,0
         movff   Temp,OldPortB
 
Fan1       
         btfss   changedB,0   ' Fan is connected to PortB.0
         bra     DoneForNow
         infsnz  Fan1Counter
         incf    Fan1Counter+1  ; increment the counter
 
DoneForNow
 
INT_RETURN