# Timing Belt Calculator (console)¶

This command-line Python script is a utility to help calculate parameters for timing belt drive systems. A more general discussion of designing with timing belts can be found on the Timing Belt Guide pages.

## Installation Requirements¶

The code requires a working installation of Python 3 with scipy. For suggestions on setting up your system please see Python 3 Installation.

## Examples¶

The script accepts parameters on the command line and chooses a calculation based on the specific combination of supplied values.

To see all the command-line options:

```python3 timing_belt_calculator.py  --help
```

To calculate the timing belt length for a 20 tooth GT2 pulley driving a 60 tooth GT2 pulley separated by 100 mm:

```python3 timing_belt_calculator.py -N1 20 -N2 60 -C 100
Timing belt has 140.811669871759 teeth.
```

Note that this solution isn’t feasible, since endless belts must have an integer number of teeth selected from specific available sizes. To choose the correct separation for the available 140 tooth size:

```python3 timing_belt_calculator.py  -N1 20 -N2 60 -T 140
Pulley center distance is 99.181641 mm.
```

## Full Code¶

```  1#!/usr/bin/env python3
2
3import math, argparse
4
5# Use the scipy library for numerical solutions.
6# https://docs.scipy.org/doc/scipy/reference/optimize.html
7import scipy.optimize
8import numpy as np
9
10#================================================================
11
12# Each belt drive system is described by a set of properties.  This allows the
13# same code to work with different systems.
14
15GT2 = { 'units'             : 'mm',
16        'pitch'             : 2.00,
17        'tooth_height'      : 0.76,
18        'pitch_line_offset' : 0.25,
19        'belt_thickness'    : 1.52,
20       }
21
22MXL = { 'units'             : 'inch',
23        'pitch'             : 0.080,
24        'tooth_height'      : 0.020,
25        'pitch_line_offset' : 0.010,
26        'belt_thickness'    : 0.045,
27       }
28
29XL =  { 'units'             : 'inch',
30        'pitch'             : 0.200,
31        'tooth_height'      : 0.050,
32        'pitch_line_offset' : 0.010,
33        'belt_thickness'    : 0.090,
34       }
35
36L  =  { 'units'             : 'inch',
37        'pitch'             : 0.375,
38        'tooth_height'      : 0.075,
39        'pitch_line_offset' : 0.015,
40        'belt_thickness'    : 0.140,
41       }
42
43#================================================================
44# Timing pulley center distance formulas.
45# See section 22 in https://www.sdp-si.com/PDFS/Technical-Section-Timing.pdf
46
47# Given:
48#  C    center distance
49#  L    belt length
50#  R1   larger pulley pitch radius
51#  R2   smaller pulley pitch radius
52#  phi  One half angle of wrap on smaller pulley (radians)
53
54#  C * cos(phi) = R1 - R2
55#  2 * C * sin(phi) = L - pi*(R1+R2) - (pi - 2*phi)*(R1-R2)
56
57# Rewriting:
58#  phi = acos((R1 - R2) / C)
59#  L   = 2 * C * sin(phi) + pi * (R1 + R2) + (pi - 2 * phi) * (R1 - R2)
60
61def timing_belt_length(N1, N2, C, system=GT2):
62    """Calculate the belt length in teeth for a pair of pulleys with N1 and N2 teeth
63    separated by center distance C.  The result may be fractional and thus
64    unrealizable; endless belts have an integer number of teeth chosen from a
65    specific set of available sizes.
66    """
67
68    # Make sure N1 >= N2:
69    if N2 > N1:
70        N1, N2 = N2, N1
71
72    # Look up belt properties.
73    pitch = system['pitch']
74
75    # Calculate the radius of each pulley in the system units.  The circumference
76    # at the pitch diameter is the number of teeth multiplied by the belt tooth pitch.
77    R1 = N1 * pitch / (2 * math.pi)
78    R2 = N2 * pitch / (2 * math.pi)
79
80    # Check the pulley separation:
81    if R1 + R2 >= C:
82        print("Warning: pulleys collide, solution not feasible.")
83
84    # The essential calculation does not depend on units:
85    phi = math.acos((R1 - R2) / C)
86    L   = 2 * C * math.sin(phi) + math.pi * (R1 + R2) + (math.pi - 2 * phi) * (R1 - R2)
87
88    # Convert the length to a tooth count (possibly non-integer).
89    belt_teeth = L / pitch
90    return belt_teeth
91
92#================================================================
93def timing_belt_center_distance(N1, N2, T, system=GT2):
94    """Calculate the center to center distance for a pair of pulleys with N1 and N2
95    teeth driving a belt with T teeth.
96    """
97
98    # Look up belt properties.
99    pitch = system['pitch']
100
101    # Use the scipy fmin algorithm to calculate an inverse solution using the
102    # length function.  The initial guess is based on the pulley sizes.
103    x0 = np.array(((N1+N2) * pitch))
104
105    result = scipy.optimize.fmin(lambda x: abs(timing_belt_length(N1, N2, x[0]) - T), x0, disp=False)
106    return result[0]
107
108#================================================================
109def timing_pulley_dimensions(N, system=GT2):
110    """Calculate properties for a timing pulley of a given size and system.
111
112    :param N: integer number of teeth
113    :param system: dictionary of belt system properties.
114    :return: dictionary of pulley properties
115    """
116
117    # Look up belt properties.
118    pitch = system['pitch']
119    tooth_height = system['tooth_height']
120
121    # Calculate pulley properties.
122
123    # The pitch diameter is the effective diameter of the pulley acting as a
124    # wheel; it falls inside the belt, so outside the actual pulley.
125    pitch_diameter =  N * pitch / math.pi
126
127    # The stock diameter is the maximum diameter of the pulley teeth,
128    # corresponding to the stock circular diameter before cutting grooves.
129    stock_diameter = pitch_diameter - tooth_height
130
131    return { 'teeth' : N,
132             'pitch_diameter' : pitch_diameter,
133             'stock_diameter' : stock_diameter
134             }
135
136
137#================================================================
138# Main script follows.  This sequence is executed when the script is initiated from the command line.
139
140if __name__ == "__main__":
141    parser = argparse.ArgumentParser(description="Timing belt calculator.")
142    parser.add_argument('--debug', action='store_true', help='Enable debugging logging to console.')
143    parser.add_argument('-N1', type=int, help="Number of teeth on first pulley.")
144    parser.add_argument('-N2', type=int, help="Number of teeth on second pulley.")
145    parser.add_argument('-T', type=int, help="Number of teeth on timing belt.")
146    parser.add_argument('-C', type=float,help="Distance between pulley centers in default units.")
147    parser.add_argument('-sys', type=str, default='GT2', help="Belt system: GT2, MXL, XL, or L (default %(default)s).")
148
149    args = parser.parse_args()
150
151    system = {'GT2' : GT2, 'MXL' : MXL, 'XL' : XL, 'L' : L}.get(args.sys)
152    if system is None:
153        print("Warning: unrecognized belting system type, defaulting to GT2.")
154        system = GT2
155
156    # Choose a calculation depending on the combination of arguments.
157    if args.N1 is not None and args.N2 is not None and args.C is not None and args.T is None:
158        belt_teeth = timing_belt_length(args.N1, args.N2, args.C, system=system)
159        print(f"Timing belt has {belt_teeth} teeth.")
160
161    elif args.N1 is not None and args.N2 is not None and args.C is None and args.T is not None:
162        center_distance = timing_belt_center_distance(args.N1, args.N2, args.T, system=system)
163        print(f"Pulley center distance is %f %s." % (center_distance, system['units']))
164
165    else:
166        print("No calculation defined for this combination of arguments.")
```