Position Tracking using a SinCos Sensor on a Linear Motor

Hi everyone, I am struggling with tracking the position of a permanent magnet slider in a linear motor.


The linear motor in question is a Linmot PS02-23Sx80F, which is a 2 phase closed-loop motor with linear hall effect sensor built in for position tracking / commutation. The datasheet for said motor could be found on this page.


It appears that someone has previously driven a similar motor from the same brand using a stepper motor controller and an arduino though it is not clear if position tracking was ever implemented from the video.

To my understanding the two linear hall effect sensors are separated by 5 mm (90 Degrees Electrically). My current approach is based on the method used in this paper I found online, which probably explained it better than I could. (Screenshot of the relevant section attached)

Paper on Linear Motor Tracking


I have been able to successfully track the slider from ~0 mm to ~20 mm (the distance of 2 PI Electrically / the Pole Pitch), however I have been struggling to come up with logic which could effectively count above 20 (or below 0) as the value becomes -19.99 immediately after 20 (when moving in the same direction). Presumably this is due to the nature of atan2() function and to solve it I would somehow need to make it track beyond -pi and pi.

I have been looking at other’s question on tracking atan2() beyond -pi and pi but I can’t seem to find a consistent solution.

I have been struggling for so long on how I could solve it with a counter of sorts, which works for position from between 0 - 40 mm but jumps back to 20 when real position is > 40 mm.

I am a novice in electronics and programming so any help would be much appreciated.

float sinSensor = 0;
float cosSensor = 0;
int maxSinVal = 0;
int minSinVal = 1023;
int maxCosVal = 0;
int minCosVal = 1023;

int polePitch = 20; // 20mm Pole Pitch, Pole Pitch being the distance between N - N on mover

int counter = 1;
float moverPosition = 0;
float previousPosition = 0;
float actualPosition = 0;

void setup(){

void loop() {

  sinSensor = analogRead(A0); // Raw Sin Sensor input
  cosSensor = analogRead(A1); // Raw Cos Sensor input

  // Logging Max and Min SinCos Sensor Value
  if(sinSensor > maxSinVal){
    maxSinVal = sinSensor;

  if(sinSensor < minSinVal){
    minSinVal = sinSensor;

  if(cosSensor > maxCosVal){
    maxCosVal = cosSensor;

  if(cosSensor < minCosVal){
    minCosVal = cosSensor;

  // Amp = Max - Min / 2
  // vShift = (Sensor - Min - Amp )/ Amp 
  float sinAmp = (maxSinVal - minSinVal)/2;
  float cosAmp = (maxCosVal - minCosVal)/2;
  float sinProcessed= (sinSensor - minSinVal - sinAmp)/ sinAmp;
  float cosProcessed = (cosSensor - minCosVal - sinAmp)/ cosAmp;
  float electricalAngle = atan2(sinProcessed, cosProcessed);
  float moverPosition = (polePitch * electricalAngle) / PI;


  //The Counter below is not working 100%

  if(previousPosition - moverPosition > polePitch){
    counter = counter + 1;
  if(previousPosition - moverPosition < polePitch * -1){
    counter = counter - 1;

  actualPosition = moverPosition + (counter * polePitch);

  previousPosition = moverPosition;

  //Serial Output

  Serial.print(" Mover Position: ");
  Serial.print(" Actual Position: ");
  Serial.print(" counter: ");

Presumably this is due to the nature of atan2() function and to solve it I would somehow need to make it track beyond -pi and pi.

if you really believe there is a problem with atan2(), i would suggest you confirm it by looking at its values. One approach is to capture the max/min outputs and display them.

perhaps you could count the “jumps” (counter++ from 19.99 to -19.99, counter-- from -19.99 to 19.99). then, multiply the jumps by 20 (or 40) and add the CurrentAtanCalculation …with a little modification for the negatives (half circles)