# Motor degree movement function

I want to control a motor which rotates a device over a gear.

Input values for the device are from 0 to 360 degrees. The motor should always rotate the device the shorter way around. For example, if the position is 30° and target position will be 340°, it should rotate over 0° counter clockwise.

The motor controller internally has so called qc unit system. One turnaround of the the device for example constitutes to 2938500, so there is a translation of values necessary. The internal position (in qc) could become negative or positive values like an integer.

I ended up with that code and some tests to be sure it should work. Did I catch all cases? Can I still simplify the move function?

import unittest

qc360 = 2938500

def qc2deg(qc):
return (qc / float(qc360)) * 360.0

def deg2qc(deg):
return int(round((deg/360.0) * qc360 ))

def move(is_qc, target_deg):
is_deg = qc2deg(is_qc)
diff = is_deg - target_deg

if abs(diff) <= 180.0:
if diff < 0:
target_qc = is_qc + deg2qc(abs(diff))
else:
target_qc = is_qc + deg2qc(-diff)
else:
if diff < 0:
target_qc = is_qc + deg2qc(abs(diff)-360)
else:
target_qc = is_qc + deg2qc(360-diff)

return target_qc

class TestMovement(unittest.TestCase):

def test_1qc2deg(self):
self.assertEqual(qc2deg(qc360/2), 180)
self.assertEqual(qc2deg(qc360+qc360/2), 360+180)
self.assertEqual(qc2deg(-qc360/2), -180)
self.assertEqual(qc2deg(-qc360-qc360/2), -360-180)

def test_2deg2qc(self):
self.assertEqual(deg2qc(180), qc360/2)
self.assertEqual(deg2qc(360+180), qc360+qc360/2)
self.assertEqual(deg2qc(-180), -qc360/2)
self.assertEqual(deg2qc(-360-180), -qc360-qc360/2)

def test_3simpleMovePositive(self):
# Move within 180 degrees, without zero crossing
self.assertEqual(move(0, 170), deg2qc(170))
self.assertEqual(move(20, 90), deg2qc(90))

def test_4simpleMoveNegative(self):
self.assertEqual(move(20, 5), deg2qc(5))
self.assertEqual(move(210, 170), deg2qc(170))

def test_5wideMovePositive(self):
# positive direction
self.assertEqual(move(0, 190), deg2qc(-170))
self.assertEqual(move(deg2qc(60), 350), deg2qc(-10))

def test_6wideMoveNegative(self):
# negative direction
self.assertEqual(move(deg2qc(190), 0), deg2qc(360))
self.assertEqual(move(deg2qc(350), 60), deg2qc(360+60))

def test_7variousMoves(self):
self.assertEqual(move(deg2qc(260), 50), deg2qc(360+50))
self.assertEqual(move(deg2qc(260), 60), deg2qc(360+60))
self.assertEqual(move(deg2qc(260), 70), deg2qc(360+70))
self.assertEqual(move(deg2qc(260), 80), deg2qc(80))
self.assertEqual(move(deg2qc(260), 90), deg2qc(90))
self.assertEqual(move(deg2qc(260), 80), deg2qc(80))
self.assertEqual(move(deg2qc(260), 120), deg2qc(120))

if __name__ == '__main__':
unittest.main()


    if diff < 0:
target_qc = is_qc + deg2qc(abs(diff))
else:
target_qc = is_qc + deg2qc(-diff)


If diff < 0, then abs(diff) == -diff, just the same as the else case. The

    target_qc = is_qc + deg2qc(-diff)


(no if's and else's) should suffice. The else case (that is, abs(diff) > 180.0) also can be rewritten as

    if diff < 0:
target_qc = is_qc + deg2qc(-diff - 360)
else:
target_qc = is_qc + deg2qc(-diff + 360)


where the cases only differ in signs of 360, which suggests that you are looking for the remainder -diff % 360; notice that python supports the remainder of floats.

To get rid of ugly leading -, you may want to calculate diff as

    diff = target_deg - is_deg