-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fca79cc
commit 553b397
Showing
8 changed files
with
945 additions
and
571 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
#!/usr/bin/env python | ||
|
||
from math import floor, ceil | ||
|
||
|
||
class LaserSpeed: | ||
""" | ||
MIT License. | ||
This is the standard library for converting to and from speed code information for LHYMICRO-GL. | ||
The units in the speed code have particular bands/gears which switches the equations used to convert | ||
between values and speeds. The fundamental units within the speed code value is period. All values | ||
are linearly related to the delay between ticks. The device controlled is ultimately a stepper motor | ||
and the speed a stepper motor travels at is the result of the time between ticks. We are dealing with | ||
a 1000 dpi stepper motor, so for example to travel at 1 inch a second requires that the device tick | ||
at 1 kHz. To do this it must delay 1 ms between ticks. This corresponds to a value of 48296 in the M2 | ||
board. Which has an equation of 60416 - (12120 * T) where T is the period requested in ms. This is | ||
equal to 25.4 mm/s. If we want a 2 ms delay, which is half the speed (0.5kHz, 0.5 inches/second, | ||
12.7 mm/s) we do 60416 - (12120 * 2) which gives us a value of 36176. This would be encoded a 16 bit | ||
number broken up into 2 ascii 3 digit strings between 0-255. 141 for the high bits and 80 for the low | ||
bits. So CV1410801 where the final 1 is the gearing equation we used. | ||
The speed in mm/s is also used for determining which gearing to use and as a factor for the horizontal | ||
encoded value. Slow down the device down while traveling diagonal to make the diagonal and orthogonal | ||
take the same amount of time (thereby cutting to the same depth). | ||
""" | ||
|
||
def __init__(self): | ||
pass | ||
|
||
@staticmethod | ||
def get_speed_from_code(speed_code, board="M2"): | ||
code_value, gear, step_value, diagonal, raster_step = LaserSpeed.parse_speed_code(speed_code) | ||
# b, m, gear = LaserSpeed.get_gearing(board, code_value, raster_step == 0) | ||
b, m, gear = LaserSpeed.get_gearing(board, gear=gear, uses_raster_step=raster_step != 0) | ||
return LaserSpeed.get_speed_from_value(code_value, b, m) | ||
|
||
@staticmethod | ||
def get_code_from_speed(mm_per_second, raster_step=0, board="M2", d_ratio=0.261199033289, gear=None): | ||
""" | ||
Get a speedcode from a given speed. The raster step appends the 'G' value and uses speed ranges. | ||
The d_ratio uses the default/auto ratio, but might be improved at sqrt(2)-1 (0.41421356). | ||
The gearing is optional and forces the speedcode to work for that particular gearing. Gear=0 | ||
refers to C-suffix notation speeds. | ||
:param mm_per_second: speed to convert to code. | ||
:param raster_step: raster step mode to use. | ||
:param board: Nano Board Model to do the conversion for. | ||
:param d_ratio: M1, M2, B1, B2 have ratio of optional speed | ||
:param gear: Optional force gearing rather than default gear for that speed. | ||
:return: speed code produced. | ||
""" | ||
if mm_per_second > 240 and raster_step == 0: | ||
mm_per_second = 19.05 # Arbitrary default speed for out range value. | ||
b, m, gear = LaserSpeed.get_gearing(board, mm_per_second, raster_step != 0, gear) | ||
|
||
speed_value = LaserSpeed.get_value_from_speed(mm_per_second, b, m) | ||
if (speed_value - round(speed_value)) > 0.005: | ||
speed_value = ceil(speed_value) | ||
speed_value = round(speed_value) | ||
encoded_speed = LaserSpeed.encode_value(speed_value) | ||
|
||
if raster_step != 0: | ||
if gear == 0: # There is no C suffix notation for gear raster step. | ||
gear = 1 | ||
return "V%s%1dG%03d" % ( | ||
encoded_speed, | ||
gear, | ||
raster_step | ||
) | ||
|
||
if d_ratio == 0 or board == "A" or board == "B" or board == "M": | ||
# We do not need the diagonal code. | ||
if raster_step == 0: | ||
if gear == 0: | ||
return "CV%s1C" % ( | ||
encoded_speed | ||
) | ||
else: | ||
return "CV%s%1d" % ( | ||
encoded_speed, | ||
gear) | ||
else: | ||
step_value = min(int(floor(mm_per_second) + 1), 128) | ||
frequency_kHz = float(mm_per_second) / 25.4 | ||
try: | ||
period_in_ms = 1 / frequency_kHz | ||
except ZeroDivisionError: | ||
period_in_ms = 0 | ||
d_value = d_ratio * -m * period_in_ms / float(step_value) | ||
encoded_diagonal = LaserSpeed.encode_value(d_value) | ||
if gear == 0: | ||
return "CV%s1%03d%sC" % ( | ||
encoded_speed, | ||
step_value, | ||
encoded_diagonal | ||
) | ||
else: | ||
return "CV%s%1d%03d%s" % ( | ||
encoded_speed, | ||
gear, | ||
step_value, | ||
encoded_diagonal) | ||
|
||
@staticmethod | ||
def parse_speed_code(speed_code): | ||
is_shortened = False | ||
normal = False | ||
if speed_code[0] == "C": | ||
speed_code = speed_code[1:] | ||
normal = True | ||
if speed_code[-1] == "C": | ||
speed_code = speed_code[:-1] | ||
is_shortened = True | ||
# This is an error speed. | ||
if "V1677" in speed_code or "V1676" in speed_code: | ||
# The 4th character can only be 0,1,2 except for error speeds. | ||
code_value = LaserSpeed.decode_value(speed_code[1:12]) | ||
speed_code = speed_code[12:] | ||
# The value for this speed is so low, it's negative | ||
# and bit-shifted in 24 bits of a negative number. | ||
else: | ||
code_value = LaserSpeed.decode_value(speed_code[1:7]) | ||
speed_code = speed_code[7:] | ||
gear = int(speed_code[0]) | ||
speed_code = speed_code[1:] | ||
|
||
if is_shortened: | ||
gear = 0 # Flags as step zero during code error. | ||
raster_step = 0 | ||
|
||
if normal: | ||
step_value = 0 | ||
diagonal = 0 | ||
if len(speed_code) > 1: | ||
step_value = int(speed_code[:3]) | ||
diagonal = LaserSpeed.decode_value(speed_code[3:]) | ||
return code_value, gear, step_value, diagonal, raster_step | ||
else: | ||
if "G" in speed_code: | ||
raster_step = int(speed_code[-3:]) | ||
return code_value, gear, 1, 1, raster_step | ||
|
||
@staticmethod | ||
def get_value_from_speed(mm_per_second, b, m): | ||
""" | ||
Takes in speed in mm per second and returns speed value. | ||
""" | ||
try: | ||
frequency_kHz = float(mm_per_second) / 25.4 | ||
period_in_ms = 1 / frequency_kHz | ||
return LaserSpeed.get_value_from_period(period_in_ms, b, m) | ||
except ZeroDivisionError: | ||
return b | ||
|
||
@staticmethod | ||
def get_value_from_period(x, b, m): | ||
""" | ||
Takes in period in ms and converts it to value. | ||
This is a simple linear relationship. | ||
""" | ||
return m * x + b | ||
|
||
@staticmethod | ||
def get_speed_from_value(value, b, m): | ||
try: | ||
period_in_ms = LaserSpeed.get_period_from_value(value, b, m) | ||
frequency_kHz = 1 / period_in_ms | ||
return 25.4 * frequency_kHz | ||
except ZeroDivisionError: | ||
return 0 | ||
|
||
@staticmethod | ||
def get_period_from_value(y, b, m): | ||
try: | ||
return (y - b) / m | ||
except ZeroDivisionError: | ||
return float('inf') | ||
|
||
@staticmethod | ||
def decode_value(code): | ||
b1 = int(code[0:-3]) | ||
if b1 > 16000000: | ||
b1 -= 16777216 # decode error negative numbers | ||
b2 = int(code[-3:]) | ||
return (b1 << 8) + b2 | ||
|
||
@staticmethod | ||
def encode_value(value): | ||
value = int(value) | ||
b0 = value & 255 | ||
b1 = (value >> 8) & 0xFFFFFF # unsigned shift, to emulate bugged form. | ||
return "%03d%03d" % (b1, b0) | ||
|
||
@staticmethod | ||
def get_gear_for_speed(mm_per_second, uses_raster_step=False): | ||
if mm_per_second <= 25.4: | ||
return 1 | ||
if 25.4 < mm_per_second <= 60: | ||
return 2 | ||
if not uses_raster_step: | ||
if 60 < mm_per_second < 127: | ||
return 3 | ||
if 127 <= mm_per_second: | ||
return 4 | ||
else: | ||
if 60 < mm_per_second < 127: | ||
return 2 | ||
if 127 <= mm_per_second <= 320: | ||
return 3 | ||
if 320 <= mm_per_second: | ||
return 4 | ||
|
||
@staticmethod | ||
def get_gearing(board, mm_per_second=None, uses_raster_step=False, gear=None): | ||
if gear is None: | ||
gear = LaserSpeed.get_gear_for_speed(mm_per_second, uses_raster_step) | ||
# A, B, B1, B2 | ||
b_values = [64752.0, 64752.0, 64640.0, 64512.0] | ||
m = -2000.0 | ||
if board[0] == "M": # any M series board | ||
b_values = [60416.0, 60416.0, 59904.0, 59392.0] | ||
m = -12120.0 | ||
if board == "B2": | ||
m = -24240.0 | ||
if gear == 0: | ||
if board == "B2": | ||
if uses_raster_step: | ||
return b_values[0], m / 12, 1 | ||
else: | ||
return b_values[0], m / 12, 0 | ||
elif board == "M" or board == "M1": | ||
return b_values[0], m, 0 | ||
elif board == "M2": | ||
return 65528.0, m / 12, 0 | ||
elif mm_per_second is not None: | ||
if board == "B2": | ||
if mm_per_second < 7: | ||
if uses_raster_step: | ||
return b_values[0], m / 12, 1 | ||
else: | ||
return b_values[0], m / 12, 0 | ||
elif board == "M": | ||
if mm_per_second < 6: | ||
return b_values[0], m, 0 | ||
elif board == "M1": | ||
if mm_per_second < 6 or (not uses_raster_step and mm_per_second < 7): | ||
return b_values[0], m, 0 | ||
elif board == "M2": | ||
if mm_per_second < 7: | ||
return 65528.0, m / 12, 0 | ||
return b_values[gear - 1], m, gear |
Oops, something went wrong.