DIY Optical Encoder - Interrupt Issues

TL;DR
I am trying to record the speed and position of a rotating shaft in time. Unfortunately, I have issues with getting expected measurements when using the CHANGE interrupt.

I am trying to record the movement of a rotating shaft. More specifically the time it takes for the shaft to move from one position to the next and the location of the shaft at those moments. I need this data to control external equipment that depends on the location of the shaft and the change in speed from previous to the current position. The shaft rotates at around 1-2 times per second. Rotation is only in one direction.
Unfortunately, I have issues with getting expected measurements when using the CHANGE interrupt. More specifically when I use the code below to record the time between the slits, the values show that every second measurement is roughly twice as long as the one before it.
For example: 10064-5408-10160-5444.
If I replace the CHANGE interrupt to RISING, then the doubling of the values goes away and the results are as expected. I know I could just redesign the multi-slit encoder to have enough slits for my applications while using the RISING interrupt instead of the CHANGE, but I want to understand why this happens, so I could use the CHANGE if I need even more resolution.

Info about the encoder discs
The encoder discs are 3D printed with PETG and are made so all slits are identical and evenly spread. (See attached photo) The individual slits are 4mm wide to give the Photo Interrupter the best chance of recording the slit. The multi-slit encoder has 15 slits, this means with the CHANGE interrupt I get 30 measurements and with the RISING/FALLING I get 15. I am not missing any slit counts, as seen from the provided data.

Encoder with only one slit keeps track of full rotations and gives me a reference point for the location. The multi-slit encoder is used to provide the individual positions during the rotation. The time between the positions is calculated based on the times that I record when the Photo Interrupter output changes.

To detect the slits I am using the Photo Interrupter - GP1A57HRJ00F, that is soldered to a breakout board.
The breakout board outputs are connected to the Arduino interrupt pins 2 and 3.

The code (See at the end of the post) is made only to test this issue and is as barebones as I could make it. It keeps track of the shaft rotation and notes the time when a slit is detected by the interrupt routine. The gap between is calculated outside of the code by use of LibreOffice Calc, to remove any programming issues that I might have. The gap is calculated like this:
Latest time - Previous time = Gap

I have added the Calc file (Unable to upload this file, saved it as PDF now) that has the data collected from the serial monitor in both the RISING and CHANGE interrupts. The measurements are done by rotating the shaft by hand and letting it come to a stop on its own.
Also added is a photo of the encoder disks and their placement.

I hope someone can help me understand what is going on and if this is due to my design or something else.

// Pin allocations
#define interruptPinS 3     //Single slit encoder pin
#define interruptPinM 2     //Multi slit encoder pin

// Variables inside interrupts can be changed at any moment. needs to be volatile
volatile unsigned long endTime;      
volatile int slitcount = 0;            //counter for seeing how many slits have passed since the zeroing

void setup() {
  Serial.begin(115200);
  //Pin Setup
  pinMode(interruptPinS, INPUT_PULLUP);
  pinMode(interruptPinM, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt (interruptPinS), ChangeSingle, RISING);    // attach interrupt handler RISING
  attachInterrupt(digitalPinToInterrupt (interruptPinM), ChangeMulti, CHANGE);     // attach interrupt handler CHANGE or RISING
  endTime = micros ();
}

void ChangeSingle() {        //interrupt section that checks if the single slit encoder has passed
  slitcount = 0;             // zeroes the slitcount after one full rotation
}

void ChangeMulti() {                  //interrupt section that checks if the multi slit encoder has passed
  endTime = micros ();              //Recording the time when the interrupt happened.
  slitcount++;                      //add one to slitcount
  }
  
void loop (){ 
  if (endTime)                    //If there was a new recorded time in he ChangeMulti()
  {
    Serial.print (endTime);       //Print the value of the recorded time of multislit passing
    Serial.print (", ");
    Serial.println (slitcount);   //Print the value of the slits that have passed
    endTime = 0;                  //Zeroes the endTime so the loop will only activate when theres new data
  }  
}  // end of loop

20-05-17 - Measurement data.pdf (24.7 KB)

With change interrupt you will record both high and low signals and looking at your encoder wheel picture the long duration would be the slit and the short duration the land between slits.

Thanks for the reply.
Yes, I was operating on the premise that while using the CHANGE interrupt I will catch both the rising and the falling edges of the slits. And it might look on the photo that the slit and the land between the slits are different but I measured them and they are identical. So in my mind, there should be no difference between the recorded times. Because the rising edge (end of slit) is the same distance from the previous falling edge (start of slit) as it is to the next falling edge (start of the next slit). Please correct me if I am misunderstanding.

The problem might not be the slit/land difference but even if the land/slit distances you measure is the same that will only be relevant to a point light-source and detector and no light bleed around the slit edges to prolong the slot duration.

Which optos are you using? You may not need pullups, they may be interfering with signal edge detection.
EDIT: Looks like opto already has a 15k pullup, did you try without INPUT_PULLUP and FALLING instead of RISING?
GP1A57HRJ00F.png

GP1A57HRJ00F.png

Have a look at the code in this link. It is derived from a program I used to control the speed of a small DC motor. Note, especially, how the code in loop() deals with the data from the ISR. You must pause interrupts when reading a multi-byte variable.

...R