kopia lustrzana https://github.com/AfBu/k40whisperer_turbo
254 wiersze
10 KiB
Python
254 wiersze
10 KiB
Python
#!/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
|