Compare commits
5 Commits
5025b0c1a1
...
e328394184
| Author | SHA1 | Date | |
|---|---|---|---|
| e328394184 | |||
| 078f1f6f8e | |||
| 1de1f9503e | |||
| 173018162e | |||
| eb261462c1 |
@@ -13,6 +13,9 @@ This workspace is separate from the upstream LinuxCNC tree in
|
||||
Build a standalone CNC simulation system that:
|
||||
|
||||
- reuses LinuxCNC source as the semantic source of truth;
|
||||
- derives the CNC simulation program primarily from the LinuxCNC source
|
||||
program and strictly follows LinuxCNC behavior, structure, and semantics
|
||||
unless a documented standalone runtime boundary requires adaptation;
|
||||
- compiles core CNC logic to WASM;
|
||||
- uses HTML + JavaScript for the frontend;
|
||||
- uses OPFS for browser persistence;
|
||||
@@ -44,24 +47,28 @@ expanded into a separate implementation of G-code behavior.
|
||||
## Engineering Rules
|
||||
|
||||
1. Reuse LinuxCNC source before reimplementing any CNC logic.
|
||||
2. Do not add new project-authored CNC semantics when LinuxCNC source exists.
|
||||
2. Treat the LinuxCNC source program as the primary implementation source for
|
||||
the CNC simulation program. Standalone code must strictly follow vendored
|
||||
LinuxCNC behavior and may only adapt runtime edges such as filesystem, HAL,
|
||||
IPC, process model, and browser integration.
|
||||
3. Do not add new project-authored CNC semantics when LinuxCNC source exists.
|
||||
Replace temporary wrapper behavior with direct calls into vendored LinuxCNC
|
||||
source, or with the narrowest shims needed to make those calls compile.
|
||||
3. Prefer wrappers, shims, and extraction scripts over invasive source edits.
|
||||
4. Preserve LinuxCNC semantics for:
|
||||
4. Prefer wrappers, shims, and extraction scripts over invasive source edits.
|
||||
5. Preserve LinuxCNC semantics for:
|
||||
- G-code execution;
|
||||
- modal state;
|
||||
- parameter and variable behavior;
|
||||
- kinematics;
|
||||
- planner behavior;
|
||||
- machine and controller state visible to software.
|
||||
5. Replace only the native runtime edges:
|
||||
6. Replace only the native runtime edges:
|
||||
- file IO;
|
||||
- process model;
|
||||
- HAL runtime;
|
||||
- IPC;
|
||||
- GUI.
|
||||
6. Frontend code must be implemented with web technology, not migrated from
|
||||
7. Frontend code must be implemented with web technology, not migrated from
|
||||
native GUI code.
|
||||
|
||||
## Required Layout
|
||||
|
||||
@@ -9,6 +9,11 @@ LinuxCNC WASM Simulation Port
|
||||
This skill guides work inside `wasm-port/` for building a standalone
|
||||
LinuxCNC-based CNC simulation program.
|
||||
|
||||
The CNC simulation program must be derived primarily from the LinuxCNC source
|
||||
program. Work in this port must strictly follow LinuxCNC behavior, structure,
|
||||
and semantics, except where a documented standalone runtime boundary requires
|
||||
an adapter.
|
||||
|
||||
It is intended for tasks involving:
|
||||
|
||||
- source extraction from upstream LinuxCNC;
|
||||
@@ -33,12 +38,15 @@ Use this skill when the work involves any of the following:
|
||||
## Core Principles
|
||||
|
||||
1. Upstream LinuxCNC is the semantic source of truth.
|
||||
2. The standalone port is a separate program and separate workspace.
|
||||
3. Reuse comes before rewrite.
|
||||
4. Native runtime dependencies are replaced at the edges, not copied whole.
|
||||
5. Machine state and controller-visible behavior are first-class compatibility
|
||||
2. The CNC simulation program is primarily sourced from LinuxCNC source code;
|
||||
port-specific code must adapt LinuxCNC to standalone native/WASM execution,
|
||||
not replace LinuxCNC CNC behavior.
|
||||
3. The standalone port is a separate program and separate workspace.
|
||||
4. Reuse comes before rewrite.
|
||||
5. Native runtime dependencies are replaced at the edges, not copied whole.
|
||||
6. Machine state and controller-visible behavior are first-class compatibility
|
||||
targets, not optional nice-to-haves.
|
||||
6. Do not continue building a project-authored CNC program once the porting
|
||||
7. Do not continue building a project-authored CNC program once the porting
|
||||
harness runs; move behavior back to vendored LinuxCNC source functions.
|
||||
|
||||
## Source Reuse Priorities
|
||||
|
||||
109
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_hal_adapter.cpp
Normal file
109
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_hal_adapter.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace {
|
||||
|
||||
struct HalEntry {
|
||||
hal_type_t type = HAL_TYPE_UNINITIALIZED;
|
||||
hal_data_u value{};
|
||||
bool connected = true;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, HalEntry> &pins()
|
||||
{
|
||||
static std::unordered_map<std::string, HalEntry> values;
|
||||
return values;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, HalEntry> &signals()
|
||||
{
|
||||
static std::unordered_map<std::string, HalEntry> values;
|
||||
return values;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, HalEntry> ¶ms()
|
||||
{
|
||||
static std::unordered_map<std::string, HalEntry> values;
|
||||
return values;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, HalEntry> &values_for(standalone::HalValueKind kind)
|
||||
{
|
||||
switch (kind) {
|
||||
case standalone::HalValueKind::Pin:
|
||||
return pins();
|
||||
case standalone::HalValueKind::Signal:
|
||||
return signals();
|
||||
case standalone::HalValueKind::Param:
|
||||
return params();
|
||||
}
|
||||
return pins();
|
||||
}
|
||||
|
||||
int lookup_hal_value(const char *name, std::unordered_map<std::string, HalEntry> &values,
|
||||
hal_type_t *type, hal_data_u **ptr, bool *connected)
|
||||
{
|
||||
if (!name || !type || !ptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto it = values.find(name);
|
||||
if (it == values.end()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*type = it->second.type;
|
||||
*ptr = &it->second.value;
|
||||
if (connected) {
|
||||
*connected = it->second.connected;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace standalone {
|
||||
|
||||
void reset_hal_adapter()
|
||||
{
|
||||
pins().clear();
|
||||
signals().clear();
|
||||
params().clear();
|
||||
}
|
||||
|
||||
void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_data_u value,
|
||||
bool connected)
|
||||
{
|
||||
values_for(kind)[name] = HalEntry{type, value, connected};
|
||||
}
|
||||
|
||||
} // namespace standalone
|
||||
|
||||
int hal_init(const char *)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hal_ready(int)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hal_get_pin_value_by_name(const char *name, hal_type_t *type, hal_data_u **ptr,
|
||||
bool *connected)
|
||||
{
|
||||
return lookup_hal_value(name, pins(), type, ptr, connected);
|
||||
}
|
||||
|
||||
int hal_get_signal_value_by_name(const char *name, hal_type_t *type, hal_data_u **ptr,
|
||||
bool *connected)
|
||||
{
|
||||
return lookup_hal_value(name, signals(), type, ptr, connected);
|
||||
}
|
||||
|
||||
int hal_get_param_value_by_name(const char *name, hal_type_t *type, hal_data_u **ptr)
|
||||
{
|
||||
return lookup_hal_value(name, params(), type, ptr, nullptr);
|
||||
}
|
||||
17
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_hal_adapter.hh
Normal file
17
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_hal_adapter.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
namespace standalone {
|
||||
|
||||
enum class HalValueKind {
|
||||
Pin,
|
||||
Signal,
|
||||
Param,
|
||||
};
|
||||
|
||||
void reset_hal_adapter();
|
||||
void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_data_u value,
|
||||
bool connected = true);
|
||||
|
||||
} // namespace standalone
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <string>
|
||||
|
||||
#define private public
|
||||
#include "emc/rs274ngc/rs274ngc_interp.hh"
|
||||
#undef private
|
||||
|
||||
#include "emc/rs274ngc/rs274ngc_return.hh"
|
||||
#include "interp_python.hh"
|
||||
#include "pythonplugin/python_plugin.hh"
|
||||
|
||||
PythonPlugin *python_plugin = nullptr;
|
||||
int _task = 0;
|
||||
|
||||
struct _inittab {
|
||||
const char *name;
|
||||
void *initfunc;
|
||||
};
|
||||
|
||||
_inittab builtin_modules[] = {{nullptr, nullptr}};
|
||||
|
||||
InterpBase::~InterpBase() {}
|
||||
|
||||
PythonPlugin *PythonPlugin::instantiate(struct _inittab *)
|
||||
{
|
||||
static PythonPlugin standalone_plugin;
|
||||
python_plugin = &standalone_plugin;
|
||||
return python_plugin;
|
||||
}
|
||||
|
||||
std::string handle_pyerror()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Interp::is_pycallable(setup_pointer, const char *, const char *) { return false; }
|
||||
bool Interp::is_user_defined_g_code(int) { return false; }
|
||||
bool Interp::is_any_m_code_remapped(block_pointer, setup_pointer) { return false; }
|
||||
bool Interp::is_user_defined_m_code(block_pointer, setup_pointer, int) { return false; }
|
||||
bool Interp::is_m_code_remappable(int) { return false; }
|
||||
bool Interp::is_g_code_remappable(int) { return false; }
|
||||
bool Interp::remap_in_progress(const char *) { return false; }
|
||||
remap_pointer Interp::remapping(const char *) { return nullptr; }
|
||||
remap_pointer Interp::remapping(const char, int) { return nullptr; }
|
||||
int Interp::parse_remap(const char *, int) { return INTERP_ERROR; }
|
||||
int Interp::convert_remapped_code(block_pointer, setup_pointer, int, char, int) { return INTERP_ERROR; }
|
||||
int Interp::add_parameters(setup_pointer, block_pointer, char *) { return INTERP_ERROR; }
|
||||
int Interp::pycall(setup_pointer, context_pointer, const char *, const char *, int) { return INTERP_ERROR; }
|
||||
int Interp::py_execute(const char *, bool) { return INTERP_ERROR; }
|
||||
int Interp::py_reload() { return INTERP_OK; }
|
||||
@@ -9,11 +9,67 @@
|
||||
#include <vector>
|
||||
|
||||
#include "canon_event_sink.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
|
||||
namespace {
|
||||
|
||||
void seed_hal_values()
|
||||
{
|
||||
standalone::reset_hal_adapter();
|
||||
|
||||
hal_data_u pin_bit{};
|
||||
pin_bit.b = true;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-bit", HAL_BIT,
|
||||
pin_bit);
|
||||
|
||||
hal_data_u signal_float{};
|
||||
signal_float.f = 98.25;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-float",
|
||||
HAL_FLOAT, signal_float);
|
||||
|
||||
hal_data_u param_s32{};
|
||||
param_s32.s = -17;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-s32", HAL_S32,
|
||||
param_s32);
|
||||
|
||||
hal_data_u pin_u32{};
|
||||
pin_u32.u = 123456789u;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-u32", HAL_U32,
|
||||
pin_u32);
|
||||
|
||||
hal_data_u signal_s64{};
|
||||
signal_s64.ls = -9000000000LL;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-s64", HAL_S64,
|
||||
signal_s64);
|
||||
|
||||
hal_data_u param_u64{};
|
||||
param_u64.lu = 9000000000ULL;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-u64", HAL_U64,
|
||||
param_u64);
|
||||
|
||||
hal_data_u disconnected{};
|
||||
disconnected.f = 12.5;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.disconnected-float",
|
||||
HAL_FLOAT, disconnected, false);
|
||||
}
|
||||
|
||||
void seed_tool_values()
|
||||
{
|
||||
standalone::reset_tool_adapter();
|
||||
|
||||
CANON_TOOL_TABLE tool = standalone::tool_entry_init();
|
||||
tool.toolno = 2;
|
||||
tool.pocketno = 2;
|
||||
tool.offset.tran.z = 1.25;
|
||||
tool.diameter = 0.25;
|
||||
standalone::set_tool_entry(2, tool);
|
||||
}
|
||||
|
||||
void initialize_minimal_interp(Interp &interp)
|
||||
{
|
||||
seed_hal_values();
|
||||
seed_tool_values();
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
@@ -23,6 +79,10 @@ void initialize_minimal_interp(Interp &interp)
|
||||
interp._setup.percent_flag = false;
|
||||
interp._setup.sequence_number = 0;
|
||||
interp._setup.parameter_occurrence = 0;
|
||||
interp._setup.num_spindles = 1;
|
||||
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
interp.load_tool_table();
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
}
|
||||
|
||||
@@ -80,6 +140,51 @@ int main(int argc, char **argv)
|
||||
interp._setup.sequence_number = static_cast<int>(idx + 1);
|
||||
const int execute_rc = interp.execute(program[idx].c_str());
|
||||
std::cout << "execute_line_" << (idx + 1) << "=" << execute_rc << "\n";
|
||||
if (execute_rc > INTERP_MIN_ERROR) {
|
||||
char error_buf[LINELEN] = {0};
|
||||
interp.error_text(execute_rc, error_buf, sizeof(error_buf));
|
||||
std::cout << "error_text=" << error_buf << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int file_open_rc = INTERP_FILE_NOT_OPEN;
|
||||
int file_read_count = 0;
|
||||
int file_execute_count = 0;
|
||||
if (argc > 1) {
|
||||
Interp file_interp;
|
||||
initialize_minimal_interp(file_interp);
|
||||
file_open_rc = file_interp.open(argv[1]);
|
||||
if (file_open_rc == INTERP_OK) {
|
||||
while (true) {
|
||||
const int file_read_rc = file_interp.read();
|
||||
if (file_read_rc == INTERP_ENDFILE) {
|
||||
std::cout << "file_read_eof=" << file_read_rc << "\n";
|
||||
break;
|
||||
}
|
||||
++file_read_count;
|
||||
std::cout << "file_read_" << file_read_count << "=" << file_read_rc << "\n";
|
||||
if ((file_read_rc != INTERP_OK) && (file_read_rc != INTERP_EXECUTE_FINISH)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const int file_execute_rc = file_interp.execute();
|
||||
++file_execute_count;
|
||||
std::cout << "file_execute_" << file_execute_count << "=" << file_execute_rc << "\n";
|
||||
if (file_execute_rc > INTERP_MIN_ERROR) {
|
||||
char error_buf[LINELEN] = {0};
|
||||
file_interp.error_text(file_execute_rc, error_buf, sizeof(error_buf));
|
||||
std::cout << "file_error_text=" << error_buf << "\n";
|
||||
}
|
||||
if ((file_execute_rc != INTERP_OK) &&
|
||||
(file_execute_rc != INTERP_EXECUTE_FINISH) &&
|
||||
(file_execute_rc != INTERP_EXIT)) {
|
||||
break;
|
||||
}
|
||||
if (file_execute_rc == INTERP_EXIT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block block{};
|
||||
@@ -90,6 +195,9 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
std::cout << "read_text=" << read_text_rc << "\n";
|
||||
std::cout << "file_open=" << file_open_rc << "\n";
|
||||
std::cout << "file_read_count=" << file_read_count << "\n";
|
||||
std::cout << "file_execute_count=" << file_execute_count << "\n";
|
||||
std::cout << "raw_line=" << raw_line << "\n";
|
||||
std::cout << "cooked_line=" << cooked_line << "\n";
|
||||
std::cout << "line_length=" << length << "\n";
|
||||
@@ -105,6 +213,16 @@ int main(int argc, char **argv)
|
||||
std::cout << "setup.parameter_5421=" << interp._setup.parameters[5421] << "\n";
|
||||
std::cout << "setup.parameter_5422=" << interp._setup.parameters[5422] << "\n";
|
||||
std::cout << "setup.feed_rate=" << interp._setup.feed_rate << "\n";
|
||||
std::cout << "post_execute.plane=" << static_cast<int>(interp._setup.plane) << "\n";
|
||||
std::cout << "post_execute.distance_mode=" << static_cast<int>(interp._setup.distance_mode) << "\n";
|
||||
std::cout << "post_execute.feed_mode=" << static_cast<int>(interp._setup.feed_mode) << "\n";
|
||||
std::cout << "post_execute.motion_mode=" << interp._setup.motion_mode << "\n";
|
||||
std::cout << "post_execute.origin_index=" << interp._setup.origin_index << "\n";
|
||||
std::cout << "post_execute.feed_override=" << interp._setup.feed_override << "\n";
|
||||
std::cout << "post_execute.speed_override_0=" << interp._setup.speed_override[0] << "\n";
|
||||
std::cout << "post_execute.spindle_turning_0=" << interp._setup.spindle_turning[0] << "\n";
|
||||
std::cout << "post_execute.mist=" << interp._setup.mist << "\n";
|
||||
std::cout << "post_execute.flood=" << interp._setup.flood << "\n";
|
||||
std::cout << "close_and_downcase=" << downcase_rc << "\n";
|
||||
std::cout << "normalized_line=" << line << "\n";
|
||||
std::cout << "init_block=" << init_block_rc << "\n";
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
|
||||
#include "emc/rs274ngc/rs274ngc_return.hh"
|
||||
#include "canon_event_sink.hh"
|
||||
|
||||
PythonPlugin *python_plugin = nullptr;
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
|
||||
namespace standalone {
|
||||
|
||||
@@ -71,13 +70,45 @@ void STOP_SPEED_FEED_SYNCH() {}
|
||||
void RIGID_TAP(int, double, double, double, double) {}
|
||||
void STRAIGHT_PROBE(int, double, double, double, double, double, double, double, double, double, unsigned char) {}
|
||||
void STOP() {}
|
||||
void DWELL(double) {}
|
||||
void SET_SPINDLE_MODE(int, double) {}
|
||||
void DWELL(double seconds)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "DWELL seconds=" << seconds;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void SET_SPINDLE_MODE(int spindle, double css_max)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "SET_SPINDLE_MODE spindle=" << spindle << " css_max=" << css_max;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void SPINDLE_RETRACT_TRAVERSE() {}
|
||||
void START_SPINDLE_CLOCKWISE(int, int) {}
|
||||
void START_SPINDLE_COUNTERCLOCKWISE(int, int) {}
|
||||
void SET_SPINDLE_SPEED(int, double) {}
|
||||
void STOP_SPINDLE_TURNING(int) {}
|
||||
void START_SPINDLE_CLOCKWISE(int spindle, int wait_for_at_speed)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "START_SPINDLE_CLOCKWISE spindle=" << spindle
|
||||
<< " wait_for_at_speed=" << wait_for_at_speed;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void START_SPINDLE_COUNTERCLOCKWISE(int spindle, int wait_for_at_speed)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "START_SPINDLE_COUNTERCLOCKWISE spindle=" << spindle
|
||||
<< " wait_for_at_speed=" << wait_for_at_speed;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void SET_SPINDLE_SPEED(int spindle, double speed)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "SET_SPINDLE_SPEED spindle=" << spindle << " speed=" << speed;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void STOP_SPINDLE_TURNING(int spindle)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "STOP_SPINDLE_TURNING spindle=" << spindle;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void SPINDLE_RETRACT() {}
|
||||
void ORIENT_SPINDLE(int, double, int) {}
|
||||
void WAIT_SPINDLE_ORIENT_COMPLETE(int, double) {}
|
||||
@@ -85,25 +116,65 @@ void LOCK_SPINDLE_Z() {}
|
||||
void USE_SPINDLE_FORCE() {}
|
||||
void USE_NO_SPINDLE_FORCE() {}
|
||||
void SET_TOOL_TABLE_ENTRY(int, int, const EmcPose &, double, double, double, int) {}
|
||||
void USE_TOOL_LENGTH_OFFSET(const EmcPose &) {}
|
||||
void CHANGE_TOOL() {}
|
||||
void SELECT_TOOL(int) {}
|
||||
void CHANGE_TOOL_NUMBER(int) {}
|
||||
void USE_TOOL_LENGTH_OFFSET(const EmcPose &offset)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "USE_TOOL_LENGTH_OFFSET"
|
||||
<< " x=" << offset.tran.x
|
||||
<< " y=" << offset.tran.y
|
||||
<< " z=" << offset.tran.z
|
||||
<< " a=" << offset.a
|
||||
<< " b=" << offset.b
|
||||
<< " c=" << offset.c
|
||||
<< " u=" << offset.u
|
||||
<< " v=" << offset.v
|
||||
<< " w=" << offset.w;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void CHANGE_TOOL()
|
||||
{
|
||||
standalone::change_selected_tool();
|
||||
standalone::push_canon_event("CHANGE_TOOL");
|
||||
}
|
||||
void SELECT_TOOL(int tool)
|
||||
{
|
||||
standalone::select_tool(tool);
|
||||
std::ostringstream oss;
|
||||
oss << "SELECT_TOOL tool=" << tool;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void CHANGE_TOOL_NUMBER(int pocket)
|
||||
{
|
||||
standalone::change_tool_number(pocket);
|
||||
std::ostringstream oss;
|
||||
oss << "CHANGE_TOOL_NUMBER pocket=" << pocket;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void RELOAD_TOOLDATA(void) {}
|
||||
void CLAMP_AXIS(CANON_AXIS) {}
|
||||
void DISABLE_ADAPTIVE_FEED() {}
|
||||
void ENABLE_ADAPTIVE_FEED() {}
|
||||
void DISABLE_FEED_OVERRIDE() {}
|
||||
void ENABLE_FEED_OVERRIDE() {}
|
||||
void DISABLE_SPEED_OVERRIDE(int) {}
|
||||
void ENABLE_SPEED_OVERRIDE(int) {}
|
||||
void DISABLE_FEED_HOLD() {}
|
||||
void ENABLE_FEED_HOLD() {}
|
||||
void FLOOD_OFF() {}
|
||||
void FLOOD_ON() {}
|
||||
void MIST_OFF() {}
|
||||
void MIST_ON() {}
|
||||
void PALLET_SHUTTLE() {}
|
||||
void DISABLE_ADAPTIVE_FEED() { standalone::push_canon_event("DISABLE_ADAPTIVE_FEED"); }
|
||||
void ENABLE_ADAPTIVE_FEED() { standalone::push_canon_event("ENABLE_ADAPTIVE_FEED"); }
|
||||
void DISABLE_FEED_OVERRIDE() { standalone::push_canon_event("DISABLE_FEED_OVERRIDE"); }
|
||||
void ENABLE_FEED_OVERRIDE() { standalone::push_canon_event("ENABLE_FEED_OVERRIDE"); }
|
||||
void DISABLE_SPEED_OVERRIDE(int spindle)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "DISABLE_SPEED_OVERRIDE spindle=" << spindle;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void ENABLE_SPEED_OVERRIDE(int spindle)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "ENABLE_SPEED_OVERRIDE spindle=" << spindle;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void DISABLE_FEED_HOLD() { standalone::push_canon_event("DISABLE_FEED_HOLD"); }
|
||||
void ENABLE_FEED_HOLD() { standalone::push_canon_event("ENABLE_FEED_HOLD"); }
|
||||
void FLOOD_OFF() { standalone::push_canon_event("FLOOD_OFF"); }
|
||||
void FLOOD_ON() { standalone::push_canon_event("FLOOD_ON"); }
|
||||
void MIST_OFF() { standalone::push_canon_event("MIST_OFF"); }
|
||||
void MIST_ON() { standalone::push_canon_event("MIST_ON"); }
|
||||
void PALLET_SHUTTLE() { standalone::push_canon_event("PALLET_SHUTTLE"); }
|
||||
void TURN_PROBE_OFF() {}
|
||||
void TURN_PROBE_ON() {}
|
||||
void UNCLAMP_AXIS(CANON_AXIS) {}
|
||||
@@ -111,18 +182,57 @@ void NURB_KNOT_VECTOR() {}
|
||||
void NURB_CONTROL_POINT(int, double, double, double, double) {}
|
||||
void NURB_FEED(double, double) {}
|
||||
void SET_BLOCK_DELETE(bool) {}
|
||||
void OPTIONAL_PROGRAM_STOP() {}
|
||||
void OPTIONAL_PROGRAM_STOP() { standalone::push_canon_event("OPTIONAL_PROGRAM_STOP"); }
|
||||
void SET_OPTIONAL_PROGRAM_STOP(bool) {}
|
||||
bool GET_OPTIONAL_PROGRAM_STOP() { return false; }
|
||||
void PROGRAM_END() {}
|
||||
void PROGRAM_STOP() {}
|
||||
void SET_MOTION_OUTPUT_BIT(int) {}
|
||||
void CLEAR_MOTION_OUTPUT_BIT(int) {}
|
||||
void SET_AUX_OUTPUT_BIT(int) {}
|
||||
void CLEAR_AUX_OUTPUT_BIT(int) {}
|
||||
void SET_MOTION_OUTPUT_VALUE(int, double) {}
|
||||
void SET_AUX_OUTPUT_VALUE(int, double) {}
|
||||
int WAIT(int, int, int, double) { return 0; }
|
||||
void PROGRAM_END() { standalone::push_canon_event("PROGRAM_END"); }
|
||||
void PROGRAM_STOP() { standalone::push_canon_event("PROGRAM_STOP"); }
|
||||
void SET_MOTION_OUTPUT_BIT(int index)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "SET_MOTION_OUTPUT_BIT index=" << index;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void CLEAR_MOTION_OUTPUT_BIT(int index)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "CLEAR_MOTION_OUTPUT_BIT index=" << index;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void SET_AUX_OUTPUT_BIT(int index)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "SET_AUX_OUTPUT_BIT index=" << index;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void CLEAR_AUX_OUTPUT_BIT(int index)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "CLEAR_AUX_OUTPUT_BIT index=" << index;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void SET_MOTION_OUTPUT_VALUE(int index, double value)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "SET_MOTION_OUTPUT_VALUE index=" << index << " value=" << value;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
void SET_AUX_OUTPUT_VALUE(int index, double value)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "SET_AUX_OUTPUT_VALUE index=" << index << " value=" << value;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
int WAIT(int index, int input_type, int wait_type, double timeout)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "WAIT index=" << index
|
||||
<< " input_type=" << input_type
|
||||
<< " wait_type=" << wait_type
|
||||
<< " timeout=" << timeout;
|
||||
standalone::push_canon_event(oss.str());
|
||||
return 0;
|
||||
}
|
||||
int UNLOCK_ROTARY(int, int) { return 0; }
|
||||
int LOCK_ROTARY(int, int) { return 0; }
|
||||
void UPDATE_TAG(const StateTag &) {}
|
||||
@@ -144,7 +254,8 @@ void CANON_ERROR(const char *fmt, ...)
|
||||
|
||||
double GET_EXTERNAL_FEED_RATE() { return 0.0; }
|
||||
int GET_EXTERNAL_FLOOD() { return 0; }
|
||||
double GET_EXTERNAL_LENGTH_UNITS() { return CANON_UNITS_MM; }
|
||||
CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE() { return CANON_UNITS_MM; }
|
||||
double GET_EXTERNAL_LENGTH_UNITS() { return 1.0; }
|
||||
double GET_EXTERNAL_ANGLE_UNITS() { return 1.0; }
|
||||
int GET_EXTERNAL_MIST() { return 0; }
|
||||
CANON_MOTION_MODE GET_EXTERNAL_MOTION_CONTROL_MODE() { return CANON_EXACT_STOP; }
|
||||
@@ -198,7 +309,12 @@ double GET_EXTERNAL_TOOL_LENGTH_VOFFSET() { return 0.0; }
|
||||
double GET_EXTERNAL_TOOL_LENGTH_WOFFSET() { return 0.0; }
|
||||
int GET_EXTERNAL_TOOL_SLOT() { return 0; }
|
||||
int GET_EXTERNAL_SELECTED_TOOL_SLOT() { return -1; }
|
||||
CANON_TOOL_TABLE GET_EXTERNAL_TOOL_TABLE(int) { return tooldata_entry_init(); }
|
||||
CANON_TOOL_TABLE GET_EXTERNAL_TOOL_TABLE(int index)
|
||||
{
|
||||
CANON_TOOL_TABLE tool = standalone::tool_entry_init();
|
||||
standalone::get_tool_entry(&tool, index);
|
||||
return tool;
|
||||
}
|
||||
int GET_EXTERNAL_TC_FAULT() { return 0; }
|
||||
int GET_EXTERNAL_TC_REASON() { return 0; }
|
||||
double GET_EXTERNAL_TRAVERSE_RATE() { return 0.0; }
|
||||
@@ -288,277 +404,3 @@ void SET_FEED_RATE(double rate)
|
||||
oss << "SET_FEED_RATE rate=" << rate;
|
||||
standalone::push_canon_event(oss.str());
|
||||
}
|
||||
|
||||
InterpBase::~InterpBase() {}
|
||||
|
||||
Interp::Interp()
|
||||
: log_file(stderr),
|
||||
_setup{}
|
||||
{
|
||||
_setup.init_once = 1;
|
||||
memset(_readers, 0, sizeof(_readers));
|
||||
}
|
||||
|
||||
Interp::~Interp() {
|
||||
if (log_file && log_file != stderr) {
|
||||
fclose(log_file);
|
||||
}
|
||||
log_file = nullptr;
|
||||
}
|
||||
|
||||
void Interp::doLog(unsigned int, const char *, int, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
const char *Interp::interp_status(int status)
|
||||
{
|
||||
static char statustext[64];
|
||||
snprintf(statustext, sizeof(statustext), "%d", status);
|
||||
return statustext;
|
||||
}
|
||||
|
||||
const char *Interp::getSavedError()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
int Interp::setSavedError(const char *)
|
||||
{
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
void Interp::setError(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fputc('\n', stderr);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int Interp::unwind_call(int status, const char *, int, const char *)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
int Interp::close() { return INTERP_OK; }
|
||||
int Interp::execute(const char *command)
|
||||
{
|
||||
int status = read(command);
|
||||
if (status != INTERP_OK) {
|
||||
if (status > INTERP_MIN_ERROR) {
|
||||
unwind_call(status, __FILE__, __LINE__, __FUNCTION__);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (_setup.line_length != 0) {
|
||||
status = execute_block(&EXECUTING_BLOCK(_setup), &_setup);
|
||||
if (status > INTERP_MIN_ERROR) {
|
||||
unwind_call(status, __FILE__, __LINE__, __FUNCTION__);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int Interp::execute() { return execute(nullptr); }
|
||||
|
||||
int Interp::execute(const char *command, int line_number)
|
||||
{
|
||||
if (command && line_number) {
|
||||
_setup.sequence_number = line_number;
|
||||
}
|
||||
int status = execute(command);
|
||||
if ((_setup.call_level == 0) &&
|
||||
(status == INTERP_EXECUTE_FINISH) &&
|
||||
(_setup.mdi_interrupt)) {
|
||||
_setup.mdi_interrupt = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
int Interp::exit() { return INTERP_OK; }
|
||||
int Interp::init() { return INTERP_OK; }
|
||||
void Interp::set_loop_on_main_m99(bool state) { _setup.loop_on_main_m99 = state; }
|
||||
int Interp::open(const char *) { return INTERP_ERROR; }
|
||||
|
||||
int Interp::read_inputs(setup_pointer)
|
||||
{
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int Interp::_read(const char *command)
|
||||
{
|
||||
int read_status = INTERP_OK;
|
||||
block_pointer eblock = &EXECUTING_BLOCK(_setup);
|
||||
|
||||
if ((_setup.call_state > CS_NORMAL) &&
|
||||
(eblock->call_type != CT_NGC_OWORD_SUB) &&
|
||||
(eblock->call_type != CT_NGC_M98_SUB) &&
|
||||
(eblock->call_type != CT_NONE) &&
|
||||
((eblock->o_type == O_call) ||
|
||||
(eblock->o_type == M_98) ||
|
||||
(eblock->o_type == O_return) ||
|
||||
(eblock->o_type == O_endsub) ||
|
||||
(eblock->o_type == M_99))) {
|
||||
_setup.line_length = 0;
|
||||
_setup.linetext[0] = 0;
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
_setup.call_state = CS_NORMAL;
|
||||
if (read_inputs(&_setup) != INTERP_OK) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
|
||||
if ((command == nullptr) && (_setup.file_pointer == nullptr)) {
|
||||
return INTERP_FILE_NOT_OPEN;
|
||||
}
|
||||
|
||||
_setup.parameters[5420] = _setup.current_x;
|
||||
_setup.parameters[5421] = _setup.current_y;
|
||||
_setup.parameters[5422] = _setup.current_z;
|
||||
_setup.parameters[5423] = _setup.AA_current;
|
||||
_setup.parameters[5424] = _setup.BB_current;
|
||||
_setup.parameters[5425] = _setup.CC_current;
|
||||
_setup.parameters[5426] = _setup.u_current;
|
||||
_setup.parameters[5427] = _setup.v_current;
|
||||
_setup.parameters[5428] = _setup.w_current;
|
||||
|
||||
if (_setup.file_pointer) {
|
||||
EXECUTING_BLOCK(_setup).offset = ftell(_setup.file_pointer);
|
||||
}
|
||||
|
||||
read_status = read_text(command, _setup.file_pointer, _setup.linetext,
|
||||
_setup.blocktext, &_setup.line_length);
|
||||
|
||||
if ((read_status == INTERP_EXECUTE_FINISH) || (read_status == INTERP_OK)) {
|
||||
if (_setup.line_length != 0) {
|
||||
if (parse_line(_setup.blocktext, &(EXECUTING_BLOCK(_setup)), &_setup) != INTERP_OK) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (EXECUTING_BLOCK(_setup).o_type != O_none) {
|
||||
EXECUTING_BLOCK(_setup).o_type = 0;
|
||||
}
|
||||
}
|
||||
} else if (read_status == INTERP_ENDFILE) {
|
||||
if (_setup.skipping_o != nullptr) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
} else {
|
||||
return read_status;
|
||||
}
|
||||
|
||||
return read_status;
|
||||
}
|
||||
|
||||
int Interp::read(const char *command)
|
||||
{
|
||||
int status = _read(command);
|
||||
if (status > INTERP_MIN_ERROR) {
|
||||
unwind_call(status, __FILE__, __LINE__, __FUNCTION__);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int Interp::read()
|
||||
{
|
||||
return read(nullptr);
|
||||
}
|
||||
|
||||
int Interp::reset() { return INTERP_OK; }
|
||||
int Interp::synch() { return INTERP_OK; }
|
||||
void Interp::active_g_codes(int *codes) { std::memcpy(codes, _setup.active_g_codes, sizeof(_setup.active_g_codes)); }
|
||||
void Interp::active_m_codes(int *codes) { std::memcpy(codes, _setup.active_m_codes, sizeof(_setup.active_m_codes)); }
|
||||
void Interp::active_settings(double *settings) { std::memcpy(settings, _setup.active_settings, sizeof(_setup.active_settings)); }
|
||||
int Interp::active_modes(int *, int *, double *, StateTag const &) { return INTERP_ERROR; }
|
||||
void Interp::print_state_tag(StateTag const &) {}
|
||||
char *Interp::error_text(int, char *buf, size_t max_size)
|
||||
{
|
||||
if (max_size) buf[0] = '\0';
|
||||
return buf;
|
||||
}
|
||||
char *Interp::file_name(char *buf, size_t max_size)
|
||||
{
|
||||
if (max_size) {
|
||||
std::strncpy(buf, _setup.filename, max_size - 1);
|
||||
buf[max_size - 1] = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
size_t Interp::line_length() { return static_cast<size_t>(_setup.line_length); }
|
||||
char *Interp::line_text(char *buf, size_t max_size)
|
||||
{
|
||||
if (max_size) {
|
||||
std::strncpy(buf, _setup.linetext, max_size - 1);
|
||||
buf[max_size - 1] = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
int Interp::sequence_number() { return _setup.sequence_number; }
|
||||
char *Interp::stack_name(int idx, char *buf, size_t max_size)
|
||||
{
|
||||
if (!max_size) return buf;
|
||||
if (idx < 0 || idx >= STACK_LEN) {
|
||||
buf[0] = '\0';
|
||||
return buf;
|
||||
}
|
||||
std::strncpy(buf, _setup.stack[idx], max_size - 1);
|
||||
buf[max_size - 1] = '\0';
|
||||
return buf;
|
||||
}
|
||||
int Interp::ini_load(const char *) { return INTERP_OK; }
|
||||
int Interp::on_abort(int, const char *) { return INTERP_OK; }
|
||||
void Interp::set_loglevel(int level) { _setup.loggingLevel = level; }
|
||||
|
||||
int Interp::find_remappings(block_pointer, setup_pointer) { return 0; }
|
||||
|
||||
int Interp::load_tool_table() { return INTERP_OK; }
|
||||
int Interp::init_tool_parameters() { return INTERP_OK; }
|
||||
int Interp::default_tool_parameters() { return INTERP_OK; }
|
||||
int Interp::set_tool_parameters() { return INTERP_OK; }
|
||||
bool Interp::is_pycallable(setup_pointer, const char *, const char *) { return false; }
|
||||
bool Interp::is_user_defined_g_code(int) { return false; }
|
||||
bool Interp::is_any_m_code_remapped(block_pointer, setup_pointer) { return false; }
|
||||
bool Interp::is_user_defined_m_code(block_pointer, setup_pointer, int) { return false; }
|
||||
void Interp::loop_to_beginning(setup_pointer) {}
|
||||
int Interp::convert_control_functions(block_pointer, setup_pointer) { return INTERP_OK; }
|
||||
int Interp::convert_remapped_code(block_pointer, setup_pointer, int, char, int) { return INTERP_ERROR; }
|
||||
int Interp::py_execute(const char *, bool) { return INTERP_OK; }
|
||||
int Interp::py_reload() { return INTERP_OK; }
|
||||
|
||||
const char *o_ops[] = {
|
||||
"O_none", "O_sub", "O_endsub", "O_call", "O_do", "O_while", "O_if",
|
||||
"O_elseif", "O_else", "O_endif", "O_break", "O_continue",
|
||||
"O_endwhile", "O_return", "O_repeat", "O_endrepeat", "M_98", "M_99", "O_"
|
||||
};
|
||||
|
||||
int Interp::read_named_parameter(char *, int *, double *double_ptr, double *, bool check_exists)
|
||||
{
|
||||
if (check_exists) {
|
||||
*double_ptr = 0.0;
|
||||
}
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
|
||||
int Interp::find_named_param(const char *, int *status, double *value)
|
||||
{
|
||||
*status = 0;
|
||||
*value = 0.0;
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int Interp::store_named_param(setup_pointer, const char *, double, int)
|
||||
{
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
|
||||
int Interp::add_named_param(const char *, int)
|
||||
{
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
@@ -4,338 +4,104 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "config.h"
|
||||
#include "emc/ini/inifile.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
|
||||
namespace {
|
||||
|
||||
enum predefined_named_parameters {
|
||||
NP_LINE,
|
||||
NP_MOTION_MODE,
|
||||
NP_PLANE,
|
||||
NP_CCOMP,
|
||||
NP_METRIC,
|
||||
NP_IMPERIAL,
|
||||
NP_ABSOLUTE,
|
||||
NP_INCREMENTAL,
|
||||
NP_INVERSE_TIME,
|
||||
NP_UNITS_PER_MINUTE,
|
||||
NP_UNITS_PER_REV,
|
||||
NP_COORD_SYSTEM,
|
||||
NP_TOOL_OFFSET,
|
||||
NP_RETRACT_R_PLANE,
|
||||
NP_RETRACT_OLD_Z,
|
||||
NP_SPINDLE_RPM_MODE,
|
||||
NP_SPINDLE_CSS_MODE,
|
||||
NP_IJK_ABSOLUTE_MODE,
|
||||
NP_LATHE_DIAMETER_MODE,
|
||||
NP_LATHE_RADIUS_MODE,
|
||||
NP_SPINDLE_ON,
|
||||
NP_SPINDLE_CW,
|
||||
NP_MIST,
|
||||
NP_FLOOD,
|
||||
NP_SPEED_OVERRIDE,
|
||||
NP_FEED_OVERRIDE,
|
||||
NP_ADAPTIVE_FEED,
|
||||
NP_FEED_HOLD,
|
||||
NP_FEED,
|
||||
NP_RPM,
|
||||
NP_CURRENT_TOOL,
|
||||
NP_SELECTED_POCKET,
|
||||
NP_CURRENT_POCKET,
|
||||
NP_X,
|
||||
NP_Y,
|
||||
NP_Z,
|
||||
NP_A,
|
||||
NP_B,
|
||||
NP_C,
|
||||
NP_U,
|
||||
NP_V,
|
||||
NP_W,
|
||||
NP_ABS_X,
|
||||
NP_ABS_Y,
|
||||
NP_ABS_Z,
|
||||
NP_ABS_A,
|
||||
NP_ABS_B,
|
||||
NP_ABS_C,
|
||||
NP_VALUE,
|
||||
NP_CALL_LEVEL,
|
||||
NP_REMAP_LEVEL,
|
||||
NP_SELECTED_TOOL,
|
||||
NP_VALUE_RETURNED,
|
||||
NP_TASK,
|
||||
};
|
||||
void seed_hal_values()
|
||||
{
|
||||
standalone::reset_hal_adapter();
|
||||
|
||||
struct NamedParamRuntime {
|
||||
setup state;
|
||||
hal_data_u pin_bit{};
|
||||
pin_bit.b = true;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-bit", HAL_BIT,
|
||||
pin_bit);
|
||||
|
||||
int fetch_ini_param(const char *nameBuf, int *status, double *value) {
|
||||
*status = 0;
|
||||
const int n = static_cast<int>(strlen(nameBuf));
|
||||
if (n < 8) {
|
||||
return INTERP_OK;
|
||||
hal_data_u signal_float{};
|
||||
signal_float.f = 98.25;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-float",
|
||||
HAL_FLOAT, signal_float);
|
||||
|
||||
hal_data_u param_s32{};
|
||||
param_s32.s = -17;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-s32", HAL_S32,
|
||||
param_s32);
|
||||
|
||||
hal_data_u pin_u32{};
|
||||
pin_u32.u = 123456789u;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-u32", HAL_U32,
|
||||
pin_u32);
|
||||
|
||||
hal_data_u signal_s64{};
|
||||
signal_s64.ls = -9000000000LL;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-s64", HAL_S64,
|
||||
signal_s64);
|
||||
|
||||
hal_data_u param_u64{};
|
||||
param_u64.lu = 9000000000ULL;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-u64", HAL_U64,
|
||||
param_u64);
|
||||
|
||||
hal_data_u disconnected{};
|
||||
disconnected.f = 12.5;
|
||||
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.disconnected-float",
|
||||
HAL_FLOAT, disconnected, false);
|
||||
}
|
||||
|
||||
std::string sect = nameBuf + 5;
|
||||
for (auto &c : sect) {
|
||||
c = static_cast<char>(toupper(c));
|
||||
}
|
||||
const size_t i = sect.find(']');
|
||||
if (i == std::string::npos) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
std::string var = sect.substr(i + 1);
|
||||
sect.erase(i);
|
||||
|
||||
const char *iniFileName = getenv("INI_FILE_NAME");
|
||||
if (!iniFileName) {
|
||||
return INTERP_OK;
|
||||
}
|
||||
linuxcnc::IniFile inifile(iniFileName);
|
||||
if (!inifile) {
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
if (auto inival = inifile.findReal(var, sect)) {
|
||||
*value = *inival;
|
||||
*status = 1;
|
||||
}
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int lookup_named_param(const char *nameBuf, double index, double *value) {
|
||||
const int cmd = round_to_int(index);
|
||||
switch (cmd) {
|
||||
case NP_LINE:
|
||||
*value = state.sequence_number;
|
||||
break;
|
||||
case NP_MOTION_MODE:
|
||||
*value = state.motion_mode;
|
||||
break;
|
||||
case NP_METRIC:
|
||||
*value = (state.length_units == CANON_UNITS_MM);
|
||||
break;
|
||||
case NP_IMPERIAL:
|
||||
*value = (state.length_units == CANON_UNITS_INCHES);
|
||||
break;
|
||||
case NP_ABSOLUTE:
|
||||
*value = (state.distance_mode == DISTANCE_MODE::ABSOLUTE);
|
||||
break;
|
||||
case NP_INCREMENTAL:
|
||||
*value = (state.distance_mode == DISTANCE_MODE::INCREMENTAL);
|
||||
break;
|
||||
case NP_FEED:
|
||||
*value = state.feed_rate;
|
||||
break;
|
||||
case NP_RPM:
|
||||
*value = state.speed[0];
|
||||
break;
|
||||
case NP_X:
|
||||
*value = state.current_x;
|
||||
break;
|
||||
case NP_Y:
|
||||
*value = state.current_y;
|
||||
break;
|
||||
case NP_Z:
|
||||
*value = state.current_z;
|
||||
break;
|
||||
case NP_CURRENT_TOOL:
|
||||
*value = state.parameters[interp_param_global::TOOL_NUMBER];
|
||||
break;
|
||||
case NP_CALL_LEVEL:
|
||||
*value = state.call_level;
|
||||
break;
|
||||
default:
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int find_named_param(const char *nameBuf, int *status, double *value) {
|
||||
const int level = (nameBuf[0] == '_') ? 0 : state.call_level;
|
||||
context_pointer frame = &state.sub_context[level];
|
||||
*status = 0;
|
||||
|
||||
auto pi = frame->named_params.find(nameBuf);
|
||||
if (pi == frame->named_params.end()) {
|
||||
int exists = 0;
|
||||
double inivalue = 0.0;
|
||||
if ((state.feature_set & FEATURE_INI_VARS) && (strncasecmp(nameBuf, "_ini[", 5) == 0)) {
|
||||
fetch_ini_param(nameBuf, &exists, &inivalue);
|
||||
if (exists) {
|
||||
*value = inivalue;
|
||||
*status = 1;
|
||||
parameter_value param;
|
||||
param.value = inivalue;
|
||||
param.attr = PA_GLOBAL | PA_READONLY | PA_FROM_INI;
|
||||
state.sub_context[0].named_params[strstore(nameBuf)] = param;
|
||||
return INTERP_OK;
|
||||
}
|
||||
}
|
||||
*value = 0.0;
|
||||
*status = 0;
|
||||
} else {
|
||||
parameter_pointer pv = &pi->second;
|
||||
if (pv->attr & PA_USE_LOOKUP) {
|
||||
if (lookup_named_param(nameBuf, pv->value, value) != INTERP_OK) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
*status = 1;
|
||||
} else {
|
||||
*value = pv->value;
|
||||
*status = 1;
|
||||
}
|
||||
}
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int store_named_param(const char *nameBuf, double value, bool override_readonly) {
|
||||
const int level = (nameBuf[0] == '_') ? 0 : state.call_level;
|
||||
context_pointer frame = &state.sub_context[level];
|
||||
auto pi = frame->named_params.find(nameBuf);
|
||||
if (pi == frame->named_params.end()) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
parameter_pointer pv = &pi->second;
|
||||
if ((pv->attr & PA_READONLY) && !override_readonly) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
pv->value = value;
|
||||
pv->attr &= ~PA_UNSET;
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int add_named_param(const char *nameBuf, int attr) {
|
||||
int findStatus = 0;
|
||||
double value = 0.0;
|
||||
find_named_param(nameBuf, &findStatus, &value);
|
||||
if (findStatus) {
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int level = 0;
|
||||
if (nameBuf[0] != '_') {
|
||||
level = state.call_level;
|
||||
} else {
|
||||
level = 0;
|
||||
attr |= PA_GLOBAL;
|
||||
}
|
||||
attr |= PA_UNSET;
|
||||
|
||||
parameter_value param;
|
||||
param.value = 0.0;
|
||||
param.attr = attr;
|
||||
state.sub_context[level].named_params[strstore(nameBuf)] = param;
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int init_readonly_param(const char *nameBuf, double value, int attr) {
|
||||
if (add_named_param(nameBuf, PA_READONLY | attr) != INTERP_OK) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
if (store_named_param(nameBuf, value, true) != INTERP_OK) {
|
||||
return INTERP_ERROR;
|
||||
}
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
double inicheck() {
|
||||
const char *filename = getenv("INI_FILE_NAME");
|
||||
if (!filename) {
|
||||
return -1.0;
|
||||
}
|
||||
linuxcnc::IniFile inifile(filename);
|
||||
if (!inifile) {
|
||||
return -1.0;
|
||||
}
|
||||
if (auto inistring = inifile.findString("LINEAR_UNITS", "TRAJ")) {
|
||||
if ((strcasecmp("mm", inistring->c_str()) == 0) ||
|
||||
(strcasecmp("metric", inistring->c_str()) == 0)) {
|
||||
return 1.0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
int init_named_parameters() {
|
||||
const char *pkgversion = PACKAGE_VERSION;
|
||||
const char *version_major = "_vmajor";
|
||||
const char *version_minor = "_vminor";
|
||||
const char *metric_machine = "_metric_machine";
|
||||
double vmajor = 0.0;
|
||||
double vminor = 0.0;
|
||||
double munits = 1.0;
|
||||
sscanf(pkgversion, "%lf%lf", &vmajor, &vminor);
|
||||
|
||||
if (init_readonly_param(version_major, vmajor, 0) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param(version_minor, vminor, 0) != INTERP_OK) return INTERP_ERROR;
|
||||
|
||||
munits = inicheck();
|
||||
if (init_readonly_param(metric_machine, munits, 0) != INTERP_OK) return INTERP_ERROR;
|
||||
|
||||
if (init_readonly_param("_line", NP_LINE, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_motion_mode", NP_MOTION_MODE, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_metric", NP_METRIC, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_imperial", NP_IMPERIAL, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_absolute", NP_ABSOLUTE, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_incremental", NP_INCREMENTAL, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_feed", NP_FEED, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_rpm", NP_RPM, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_x", NP_X, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_y", NP_Y, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_z", NP_Z, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_current_tool", NP_CURRENT_TOOL, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
if (init_readonly_param("_call_level", NP_CALL_LEVEL, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
|
||||
|
||||
return INTERP_OK;
|
||||
}
|
||||
};
|
||||
|
||||
void print_named_value(NamedParamRuntime &runtime, const char *name) {
|
||||
void print_named_value(Interp &interp, const char *name)
|
||||
{
|
||||
int status = 0;
|
||||
double value = 0.0;
|
||||
const int rc = runtime.find_named_param(name, &status, &value);
|
||||
const int rc = interp.find_named_param(name, &status, &value);
|
||||
std::cout << name << ": rc=" << rc << " found=" << status << " value=" << value << "\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc == 2) {
|
||||
setenv("INI_FILE_NAME", argv[1], 1);
|
||||
}
|
||||
|
||||
NamedParamRuntime runtime;
|
||||
runtime.state.feature_set = FEATURE_INI_VARS;
|
||||
runtime.state.length_units = CANON_UNITS_MM;
|
||||
runtime.state.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
runtime.state.motion_mode = G_1;
|
||||
runtime.state.feed_rate = 123.45;
|
||||
runtime.state.speed[0] = 678.9;
|
||||
runtime.state.current_x = 1.25;
|
||||
runtime.state.current_y = 2.5;
|
||||
runtime.state.current_z = 3.75;
|
||||
runtime.state.parameters[interp_param_global::TOOL_NUMBER] = 12.0;
|
||||
Interp interp;
|
||||
seed_hal_values();
|
||||
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.motion_mode = G_1;
|
||||
interp._setup.feed_rate = 123.45;
|
||||
interp._setup.speed[0] = 678.9;
|
||||
interp._setup.current_x = 1.25;
|
||||
interp._setup.current_y = 2.5;
|
||||
interp._setup.current_z = 3.75;
|
||||
interp._setup.parameters[interp_param_global::TOOL_NUMBER] = 12.0;
|
||||
|
||||
const int rc = runtime.init_named_parameters();
|
||||
const int rc = interp.init_named_parameters();
|
||||
std::cout << "init_named_parameters=" << rc << "\n";
|
||||
std::cout << "global_named_count=" << runtime.state.sub_context[0].named_params.size() << "\n";
|
||||
std::cout << "global_named_count=" << interp._setup.sub_context[0].named_params.size() << "\n";
|
||||
std::cout << "required_parameter_first=" << interp_param_global::G28_X << "\n";
|
||||
std::cout << "readonly_tool_number_index=" << interp_param_global::TOOL_NUMBER << "\n";
|
||||
|
||||
print_named_value(runtime, "_vmajor");
|
||||
print_named_value(runtime, "_vminor");
|
||||
print_named_value(runtime, "_metric_machine");
|
||||
print_named_value(runtime, "_motion_mode");
|
||||
print_named_value(runtime, "_metric");
|
||||
print_named_value(runtime, "_feed");
|
||||
print_named_value(runtime, "_rpm");
|
||||
print_named_value(runtime, "_x");
|
||||
print_named_value(runtime, "_current_tool");
|
||||
print_named_value(runtime, "_ini[traj]max_linear_velocity");
|
||||
print_named_value(interp, "_vmajor");
|
||||
print_named_value(interp, "_vminor");
|
||||
print_named_value(interp, "_metric_machine");
|
||||
print_named_value(interp, "_motion_mode");
|
||||
print_named_value(interp, "_metric");
|
||||
print_named_value(interp, "_feed");
|
||||
print_named_value(interp, "_rpm");
|
||||
print_named_value(interp, "_x");
|
||||
print_named_value(interp, "_current_tool");
|
||||
print_named_value(interp, "_ini[traj]max_linear_velocity");
|
||||
print_named_value(interp, "_hal[standalone.pin-bit]");
|
||||
print_named_value(interp, "_hal[standalone.signal-float]");
|
||||
print_named_value(interp, "_hal[standalone.param-s32]");
|
||||
print_named_value(interp, "_hal[standalone.pin-u32]");
|
||||
print_named_value(interp, "_hal[standalone.signal-s64]");
|
||||
print_named_value(interp, "_hal[standalone.param-u64]");
|
||||
print_named_value(interp, "_hal[standalone.disconnected-float]");
|
||||
print_named_value(interp, "_hal[standalone.missing]");
|
||||
|
||||
return rc == INTERP_OK ? 0 : 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#define private public
|
||||
#include "emc/rs274ngc/rs274ngc_interp.hh"
|
||||
#undef private
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
|
||||
std::filesystem::path make_temp_dir()
|
||||
{
|
||||
std::string pattern = "/tmp/linuxcnc-params-XXXXXX";
|
||||
char *path = mkdtemp(pattern.data());
|
||||
if (path == nullptr) {
|
||||
std::perror("mkdtemp");
|
||||
std::exit(2);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void write_file(const std::filesystem::path &path, const std::string &content)
|
||||
{
|
||||
std::ofstream out(path);
|
||||
if (!out) {
|
||||
std::cerr << "failed to write " << path << "\n";
|
||||
std::exit(2);
|
||||
}
|
||||
out << content;
|
||||
}
|
||||
|
||||
std::string read_file(const std::filesystem::path &path)
|
||||
{
|
||||
std::ifstream in(path);
|
||||
if (!in) {
|
||||
std::cerr << "failed to read " << path << "\n";
|
||||
std::exit(2);
|
||||
}
|
||||
return std::string(std::istreambuf_iterator<char>(in),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
bool contains(const std::string &haystack, const std::string &needle)
|
||||
{
|
||||
return haystack.find(needle) != std::string::npos;
|
||||
}
|
||||
|
||||
void print_error_text(Interp &interp, const char *prefix, int status)
|
||||
{
|
||||
char error_buf[LINELEN] = {0};
|
||||
interp.error_text(status, error_buf, sizeof(error_buf));
|
||||
std::cout << prefix << error_buf << "\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
const auto temp_dir = make_temp_dir();
|
||||
const auto parameter_file = temp_dir / "rs274ngc.var";
|
||||
const auto out_of_order_file = temp_dir / "out-of-order.var";
|
||||
const auto missing_required_file = temp_dir / "missing-required.var";
|
||||
const auto missing_file = temp_dir / "does-not-exist.var";
|
||||
|
||||
write_file(parameter_file,
|
||||
"5161 10.5\n"
|
||||
"5162 20.25\n"
|
||||
"5220 1\n"
|
||||
"5221 2.25\n"
|
||||
"5399 44\n"
|
||||
"<_named_param> 123\n");
|
||||
|
||||
write_file(out_of_order_file,
|
||||
"5220 1\n"
|
||||
"5161 2\n");
|
||||
|
||||
write_file(missing_required_file,
|
||||
"5161 3.5\n"
|
||||
"5220 1\n");
|
||||
|
||||
Interp interp;
|
||||
int rc = interp.restore_parameters(parameter_file.c_str());
|
||||
std::cout << "restore_existing=" << rc << "\n";
|
||||
std::cout << "parameter_5161=" << interp._setup.parameters[5161] << "\n";
|
||||
std::cout << "parameter_5162=" << interp._setup.parameters[5162] << "\n";
|
||||
std::cout << "parameter_5220=" << interp._setup.parameters[5220] << "\n";
|
||||
std::cout << "parameter_5221=" << interp._setup.parameters[5221] << "\n";
|
||||
std::cout << "parameter_5399=" << interp._setup.parameters[5399] << "\n";
|
||||
|
||||
rc = interp.restore_parameters(missing_file.c_str());
|
||||
std::cout << "restore_missing_file=" << rc << "\n";
|
||||
|
||||
rc = interp.restore_parameters(out_of_order_file.c_str());
|
||||
std::cout << "restore_out_of_order=" << rc << "\n";
|
||||
if (rc > INTERP_MIN_ERROR) {
|
||||
print_error_text(interp, "restore_out_of_order_error=", rc);
|
||||
}
|
||||
|
||||
Interp missing_required_interp;
|
||||
rc = missing_required_interp.restore_parameters(missing_required_file.c_str());
|
||||
std::cout << "restore_missing_required=" << rc << "\n";
|
||||
std::cout << "missing_required_5161=" << missing_required_interp._setup.parameters[5161] << "\n";
|
||||
std::cout << "missing_required_5162=" << missing_required_interp._setup.parameters[5162] << "\n";
|
||||
|
||||
interp._setup.parameters[5161] = 12.34;
|
||||
interp._setup.parameters[5162] = 56.78;
|
||||
interp._setup.parameters[5220] = 1.0;
|
||||
interp._setup.parameters[5221] = 9.87;
|
||||
interp._setup.parameters[5399] = 66.6;
|
||||
|
||||
rc = interp.save_parameters(parameter_file.c_str(), interp._setup.parameters);
|
||||
std::cout << "save_parameters=" << rc << "\n";
|
||||
|
||||
const std::string saved = read_file(parameter_file);
|
||||
const std::string backup = read_file(parameter_file.string() + RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX);
|
||||
std::cout << "saved_has_5161=" << contains(saved, "5161\t12.340000") << "\n";
|
||||
std::cout << "saved_has_5162=" << contains(saved, "5162\t56.780000") << "\n";
|
||||
std::cout << "saved_has_5221=" << contains(saved, "5221\t9.870000") << "\n";
|
||||
std::cout << "saved_has_5399=" << contains(saved, "5399\t66.600000") << "\n";
|
||||
std::cout << "saved_has_named_param=" << contains(saved, "_named_param") << "\n";
|
||||
std::cout << "backup_has_original_5161=" << contains(backup, "5161 10.5") << "\n";
|
||||
|
||||
std::filesystem::remove_all(temp_dir);
|
||||
return 0;
|
||||
}
|
||||
@@ -5,8 +5,7 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "emc/rs274ngc/interp_internal.hh"
|
||||
|
||||
struct pycontext_impl {};
|
||||
#include "interp_python.hh"
|
||||
|
||||
pycontext::pycontext() : impl(new pycontext_impl) {}
|
||||
pycontext::~pycontext() { delete impl; }
|
||||
@@ -20,6 +19,7 @@ pycontext &pycontext::operator=(const pycontext &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifndef LINUXCNC_STANDALONE_USE_RS274_PRE_STATE
|
||||
const char *strstore(const char *s)
|
||||
{
|
||||
static std::unordered_set<std::string> stringtable;
|
||||
@@ -45,6 +45,7 @@ void context_struct::clear()
|
||||
{
|
||||
new (this) context_struct();
|
||||
}
|
||||
#endif
|
||||
|
||||
setup::setup()
|
||||
: AA_axis_offset(0.0),
|
||||
|
||||
101
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_tool_adapter.cpp
Normal file
101
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_tool_adapter.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace {
|
||||
|
||||
std::array<CANON_TOOL_TABLE, CANON_POCKETS_MAX> &tool_table()
|
||||
{
|
||||
static std::array<CANON_TOOL_TABLE, CANON_POCKETS_MAX> tools{};
|
||||
return tools;
|
||||
}
|
||||
|
||||
int &selected_tool_index()
|
||||
{
|
||||
static int index = -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
CANON_TOOL_TABLE empty_tool()
|
||||
{
|
||||
CANON_TOOL_TABLE tool{};
|
||||
tool.toolno = 0;
|
||||
tool.pocketno = 0;
|
||||
return tool;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace standalone {
|
||||
|
||||
void reset_tool_adapter()
|
||||
{
|
||||
selected_tool_index() = -1;
|
||||
auto &tools = tool_table();
|
||||
for (int index = 0; index < CANON_POCKETS_MAX; ++index) {
|
||||
tools[index] = empty_tool();
|
||||
tools[index].pocketno = index;
|
||||
}
|
||||
tools[0].pocketno = 0;
|
||||
}
|
||||
|
||||
void set_tool_entry(int index, const CANON_TOOL_TABLE &tool)
|
||||
{
|
||||
if ((index < 0) || (index >= CANON_POCKETS_MAX)) {
|
||||
return;
|
||||
}
|
||||
tool_table()[index] = tool;
|
||||
}
|
||||
|
||||
CANON_TOOL_TABLE tool_entry_init()
|
||||
{
|
||||
return empty_tool();
|
||||
}
|
||||
|
||||
int find_tool_index_for_tool(int toolno)
|
||||
{
|
||||
if (toolno == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto &tools = tool_table();
|
||||
for (int index = 0; index < CANON_POCKETS_MAX; ++index) {
|
||||
if (tools[index].toolno == toolno) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_tool_entry(CANON_TOOL_TABLE *tool, int index)
|
||||
{
|
||||
if ((tool == nullptr) || (index < 0) || (index >= CANON_POCKETS_MAX)) {
|
||||
return -1;
|
||||
}
|
||||
*tool = tool_table()[index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void select_tool(int toolno)
|
||||
{
|
||||
selected_tool_index() = find_tool_index_for_tool(toolno);
|
||||
}
|
||||
|
||||
void change_tool_number(int index)
|
||||
{
|
||||
if ((index < 0) || (index >= CANON_POCKETS_MAX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &tools = tool_table();
|
||||
CANON_TOOL_TABLE spindle_tool = tools[index];
|
||||
spindle_tool.pocketno = 0;
|
||||
tools[0] = spindle_tool;
|
||||
}
|
||||
|
||||
void change_selected_tool()
|
||||
{
|
||||
change_tool_number(selected_tool_index());
|
||||
}
|
||||
|
||||
} // namespace standalone
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "emc/nml_intf/emctool.h"
|
||||
|
||||
namespace standalone {
|
||||
|
||||
void reset_tool_adapter();
|
||||
void set_tool_entry(int index, const CANON_TOOL_TABLE &tool);
|
||||
CANON_TOOL_TABLE tool_entry_init();
|
||||
int find_tool_index_for_tool(int toolno);
|
||||
int get_tool_entry(CANON_TOOL_TABLE *tool, int index);
|
||||
void select_tool(int toolno);
|
||||
void change_selected_tool();
|
||||
void change_tool_number(int index);
|
||||
|
||||
} // namespace standalone
|
||||
146
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_tp_api_probe.cpp
Normal file
146
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_tp_api_probe.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "emc/motion/motion.h"
|
||||
#include "emc/nml_intf/motion_types.h"
|
||||
#include "emc/tp/tp.h"
|
||||
|
||||
namespace {
|
||||
|
||||
emcmot_status_t status{};
|
||||
emcmot_config_t config{};
|
||||
emcmot_joint_t joints[EMCMOT_MAX_JOINTS]{};
|
||||
|
||||
void dio_write(int, char) {}
|
||||
void aio_write(int, double) {}
|
||||
void set_rotary_unlock(int, int) {}
|
||||
int get_rotary_unlock(int) { return 1; }
|
||||
|
||||
double axis_vel_limit(int axis)
|
||||
{
|
||||
return axis >= 0 && axis < EMCMOT_MAX_AXIS ? 10.0 : 0.0;
|
||||
}
|
||||
|
||||
double axis_acc_limit(int axis)
|
||||
{
|
||||
return axis >= 0 && axis < EMCMOT_MAX_AXIS ? 20.0 : 0.0;
|
||||
}
|
||||
|
||||
void init_motion_state()
|
||||
{
|
||||
config.numJoints = 3;
|
||||
config.numSpindles = 1;
|
||||
config.numDIO = 4;
|
||||
config.numAIO = 4;
|
||||
config.arcBlendOptDepth = 0;
|
||||
config.arcBlendEnable = 0;
|
||||
config.arcBlendFallbackEnable = 0;
|
||||
config.arcBlendGapCycles = 4;
|
||||
config.arcBlendRampFreq = 20.0;
|
||||
config.arcBlendTangentKinkRatio = 0.1;
|
||||
config.maxFeedScale = 1.0;
|
||||
|
||||
status.net_feed_scale = 1.0;
|
||||
status.feed_scale = 1.0;
|
||||
status.rapid_scale = 1.0;
|
||||
status.enables_new = FS_ENABLED | SS_ENABLED;
|
||||
status.enables_queued = status.enables_new;
|
||||
status.spindle_status[0].at_speed = 1;
|
||||
status.spindle_status[0].direction = 1;
|
||||
status.spindleSync = 0;
|
||||
status.jerk = 0.0;
|
||||
status.planner_type = 0;
|
||||
|
||||
tpMotData(&status, &config);
|
||||
tpMotFunctions(
|
||||
dio_write,
|
||||
aio_write,
|
||||
set_rotary_unlock,
|
||||
get_rotary_unlock,
|
||||
axis_vel_limit,
|
||||
axis_acc_limit);
|
||||
}
|
||||
|
||||
void run_linear_probe()
|
||||
{
|
||||
init_motion_state();
|
||||
|
||||
TP_STRUCT tp{};
|
||||
EmcPose start{};
|
||||
EmcPose end{};
|
||||
struct state_tag_t tag {};
|
||||
|
||||
end.tran.x = 1.0;
|
||||
|
||||
const int create_rc = tpCreate(&tp, TP_DEFAULT_QUEUE_SIZE, 1);
|
||||
const int set_cycle_rc = tpSetCycleTime(&tp, 0.001);
|
||||
const int set_pos_rc = tpSetPos(&tp, &start);
|
||||
const int set_vmax_rc = tpSetVmax(&tp, 1.0, 1.0);
|
||||
const int set_vlimit_rc = tpSetVlimit(&tp, 1.0);
|
||||
const int set_amax_rc = tpSetAmax(&tp, 10.0);
|
||||
const int set_term_rc = tpSetTermCond(&tp, TC_TERM_COND_STOP, 0.0);
|
||||
const int add_line_rc = tpAddLine(
|
||||
&tp,
|
||||
end,
|
||||
EMC_MOTION_TYPE_FEED,
|
||||
1.0,
|
||||
1.0,
|
||||
10.0,
|
||||
100.0,
|
||||
status.enables_new,
|
||||
0,
|
||||
-1,
|
||||
tag);
|
||||
|
||||
int cycle_rc = 0;
|
||||
int cycles = 0;
|
||||
for (; cycles < 10000 && !tpIsDone(&tp); ++cycles) {
|
||||
cycle_rc = tpRunCycle(&tp, 1000000);
|
||||
if (cycle_rc < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EmcPose final_pos{};
|
||||
const int get_pos_rc = tpGetPos(&tp, &final_pos);
|
||||
|
||||
std::cout << "tp_create=" << create_rc << "\n";
|
||||
std::cout << "tp_set_cycle_time=" << set_cycle_rc << "\n";
|
||||
std::cout << "tp_set_pos=" << set_pos_rc << "\n";
|
||||
std::cout << "tp_set_vmax=" << set_vmax_rc << "\n";
|
||||
std::cout << "tp_set_vlimit=" << set_vlimit_rc << "\n";
|
||||
std::cout << "tp_set_amax=" << set_amax_rc << "\n";
|
||||
std::cout << "tp_set_term_cond=" << set_term_rc << "\n";
|
||||
std::cout << "tp_add_line=" << add_line_rc << "\n";
|
||||
std::cout << "tp_cycle_rc=" << cycle_rc << "\n";
|
||||
std::cout << "tp_cycles=" << cycles << "\n";
|
||||
std::cout << "tp_get_pos=" << get_pos_rc << "\n";
|
||||
std::cout << "tp_done_after_line=" << tpIsDone(&tp) << "\n";
|
||||
std::cout << "tp_queue_depth=" << tpQueueDepth(&tp) << "\n";
|
||||
std::cout << "tp_final_pos="
|
||||
<< final_pos.tran.x << ","
|
||||
<< final_pos.tran.y << ","
|
||||
<< final_pos.tran.z << "\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
TP_STRUCT tp{};
|
||||
TC_STRUCT tc{};
|
||||
EmcPose pose{};
|
||||
|
||||
pose.tran.x = 1.0;
|
||||
pose.tran.y = 2.0;
|
||||
pose.tran.z = 3.0;
|
||||
|
||||
std::cout << "sizeof_TP_STRUCT=" << sizeof(tp) << "\n";
|
||||
std::cout << "sizeof_TC_STRUCT=" << sizeof(tc) << "\n";
|
||||
std::cout << "tp_default_queue_size=" << TP_DEFAULT_QUEUE_SIZE << "\n";
|
||||
std::cout << "tp_err_ok=" << TP_ERR_OK << "\n";
|
||||
std::cout << "tc_linear=" << TC_LINEAR << "\n";
|
||||
std::cout << "tc_circular=" << TC_CIRCULAR << "\n";
|
||||
std::cout << "pose_xyz=" << pose.tran.x << "," << pose.tran.y << "," << pose.tran.z << "\n";
|
||||
run_linear_probe();
|
||||
return 0;
|
||||
}
|
||||
3
wasm-port/runtime/core/shims/boost/python/dict.hpp
Normal file
3
wasm-port/runtime/core/shims/boost/python/dict.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/python/object_fwd.hpp"
|
||||
3
wasm-port/runtime/core/shims/boost/python/extract.hpp
Normal file
3
wasm-port/runtime/core/shims/boost/python/extract.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/python/object_fwd.hpp"
|
||||
3
wasm-port/runtime/core/shims/boost/python/import.hpp
Normal file
3
wasm-port/runtime/core/shims/boost/python/import.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/python/object_fwd.hpp"
|
||||
3
wasm-port/runtime/core/shims/boost/python/list.hpp
Normal file
3
wasm-port/runtime/core/shims/boost/python/list.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/python/object_fwd.hpp"
|
||||
@@ -1,9 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct _typeobject {
|
||||
const char *tp_name = "standalone-python-shim";
|
||||
};
|
||||
|
||||
struct _object {
|
||||
_typeobject *ob_type = nullptr;
|
||||
};
|
||||
typedef _object PyObject;
|
||||
|
||||
inline int PyCallable_Check(PyObject *) { return 0; }
|
||||
inline int PyErr_ExceptionMatches(PyObject *) { return 0; }
|
||||
inline void PyErr_Clear() {}
|
||||
inline int PyUnicode_Check(PyObject *) { return 0; }
|
||||
inline int PyLong_Check(PyObject *) { return 0; }
|
||||
inline int PyFloat_Check(PyObject *) { return 0; }
|
||||
inline PyObject *PyObject_Str(PyObject *) { return nullptr; }
|
||||
inline void Py_XDECREF(PyObject *) {}
|
||||
inline const char *PyUnicode_AsUTF8(PyObject *) { return ""; }
|
||||
|
||||
inline PyObject *PyExc_KeyError = nullptr;
|
||||
inline PyObject *Py_None = nullptr;
|
||||
|
||||
namespace boost {
|
||||
namespace python {
|
||||
|
||||
class object {};
|
||||
class error_already_set {};
|
||||
|
||||
class object {
|
||||
public:
|
||||
object() = default;
|
||||
|
||||
template <typename T>
|
||||
explicit object(const T &) {}
|
||||
|
||||
object attr(const char *) const { return object(); }
|
||||
object operator[](const char *) const { return object(); }
|
||||
object operator[](const std::string &) const { return object(); }
|
||||
object operator[](int) const { return object(); }
|
||||
PyObject *ptr() const { return nullptr; }
|
||||
};
|
||||
|
||||
class list : public object {
|
||||
public:
|
||||
using object::object;
|
||||
list() = default;
|
||||
explicit list(const object &) {}
|
||||
|
||||
template <typename T>
|
||||
void append(const T &) {}
|
||||
};
|
||||
|
||||
class tuple : public object {
|
||||
public:
|
||||
tuple() = default;
|
||||
explicit tuple(const list &) {}
|
||||
};
|
||||
|
||||
class dict : public object {
|
||||
public:
|
||||
object keys() const { return object(); }
|
||||
};
|
||||
|
||||
class scope {
|
||||
public:
|
||||
explicit scope(const object &) {}
|
||||
object attr(const char *) const { return object(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
object import(const T &) { return object(); }
|
||||
|
||||
template <typename T>
|
||||
class extract {
|
||||
public:
|
||||
explicit extract(const object &) {}
|
||||
|
||||
operator T() const { return T(); }
|
||||
};
|
||||
|
||||
inline int len(const object &) { return 0; }
|
||||
inline void handle_exception() {}
|
||||
|
||||
} // namespace python
|
||||
} // namespace boost
|
||||
|
||||
3
wasm-port/runtime/core/shims/boost/python/scope.hpp
Normal file
3
wasm-port/runtime/core/shims/boost/python/scope.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/python/object_fwd.hpp"
|
||||
3
wasm-port/runtime/core/shims/boost/python/tuple.hpp
Normal file
3
wasm-port/runtime/core/shims/boost/python/tuple.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/python/object_fwd.hpp"
|
||||
37
wasm-port/runtime/core/shims/hal.h
Normal file
37
wasm-port/runtime/core/shims/hal.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HAL_NAME_LEN 64
|
||||
|
||||
typedef enum {
|
||||
HAL_TYPE_UNINITIALIZED = 0,
|
||||
HAL_BIT,
|
||||
HAL_FLOAT,
|
||||
HAL_S32,
|
||||
HAL_U32,
|
||||
HAL_S64,
|
||||
HAL_U64
|
||||
} hal_type_t;
|
||||
|
||||
typedef union {
|
||||
bool b;
|
||||
double f;
|
||||
int s;
|
||||
unsigned int u;
|
||||
long long ls;
|
||||
unsigned long long lu;
|
||||
} hal_data_u;
|
||||
|
||||
typedef bool hal_bit_t;
|
||||
typedef double hal_float_t;
|
||||
typedef int hal_s32_t;
|
||||
typedef unsigned int hal_u32_t;
|
||||
typedef long long hal_s64_t;
|
||||
typedef unsigned long long hal_u64_t;
|
||||
|
||||
int hal_init(const char *);
|
||||
int hal_ready(int);
|
||||
int hal_get_pin_value_by_name(const char *, hal_type_t *, hal_data_u **, bool *);
|
||||
int hal_get_signal_value_by_name(const char *, hal_type_t *, hal_data_u **, bool *);
|
||||
int hal_get_param_value_by_name(const char *, hal_type_t *, hal_data_u **);
|
||||
16
wasm-port/runtime/core/shims/interp_python.hh
Normal file
16
wasm-port/runtime/core/shims/interp_python.hh
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "boost/python/object_fwd.hpp"
|
||||
|
||||
struct pycontext_impl {
|
||||
boost::python::object tupleargs;
|
||||
boost::python::object kwargs;
|
||||
int py_return_type = 0;
|
||||
double py_returned_double = 0.0;
|
||||
int py_returned_int = 0;
|
||||
boost::python::object generator_next;
|
||||
};
|
||||
|
||||
std::string handle_pyerror();
|
||||
@@ -2,12 +2,37 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "boost/python/object_fwd.hpp"
|
||||
|
||||
enum pp_status {
|
||||
PLUGIN_NO_SECTION = -1,
|
||||
PLUGIN_OK = 0,
|
||||
PLUGIN_NO_CALLABLE = 1,
|
||||
PLUGIN_EXCEPTION = 2
|
||||
};
|
||||
|
||||
std::string handle_pyerror();
|
||||
|
||||
class PythonPlugin {
|
||||
public:
|
||||
static PythonPlugin *instantiate(struct _inittab * = nullptr);
|
||||
int configure(const char * = nullptr, const char * = nullptr) { return PLUGIN_NO_SECTION; }
|
||||
bool is_callable(const char *, const char *) { return false; }
|
||||
int call(const char *, const char *, boost::python::object, boost::python::object,
|
||||
boost::python::object &) { return PLUGIN_NO_CALLABLE; }
|
||||
int run_string(const char *, boost::python::object &, bool = false) { return PLUGIN_NO_CALLABLE; }
|
||||
int call_method(boost::python::object, boost::python::object &) { return PLUGIN_NO_CALLABLE; }
|
||||
int plugin_status() const { return PLUGIN_NO_SECTION; }
|
||||
bool usable() const { return false; }
|
||||
int plugin_status() const { return 0; }
|
||||
int initialize() { return PLUGIN_NO_SECTION; }
|
||||
const std::string &last_exception() const {
|
||||
static std::string empty;
|
||||
return empty;
|
||||
}
|
||||
const std::string &last_errmsg() const {
|
||||
static std::string empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
boost::python::object main_namespace;
|
||||
};
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#define RTAPI_NAME_LEN 31
|
||||
#endif
|
||||
|
||||
#ifndef EXPORT_SYMBOL
|
||||
#define EXPORT_SYMBOL(symbol)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
RTAPI_MSG_NONE = 0,
|
||||
RTAPI_MSG_ERR,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "emc/nml_intf/emctool.h"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
|
||||
enum {
|
||||
IDX_OK = 0,
|
||||
@@ -8,22 +9,15 @@ enum {
|
||||
|
||||
inline CANON_TOOL_TABLE tooldata_entry_init()
|
||||
{
|
||||
CANON_TOOL_TABLE tool{};
|
||||
tool.toolno = 0;
|
||||
tool.pocketno = 0;
|
||||
return tool;
|
||||
return standalone::tool_entry_init();
|
||||
}
|
||||
|
||||
inline int tooldata_find_index_for_tool(int toolno)
|
||||
{
|
||||
return toolno == 0 ? 0 : -1;
|
||||
return standalone::find_tool_index_for_tool(toolno);
|
||||
}
|
||||
|
||||
inline int tooldata_get(CANON_TOOL_TABLE *tool, int index)
|
||||
{
|
||||
if ((tool == nullptr) || (index != 0)) {
|
||||
return -1;
|
||||
}
|
||||
*tool = tooldata_entry_init();
|
||||
return IDX_OK;
|
||||
return standalone::get_tool_entry(tool, index) == 0 ? IDX_OK : -1;
|
||||
}
|
||||
|
||||
13
wasm-port/tests/fixtures/canon/arc_semantics.events
vendored
Normal file
13
wasm-port/tests/fixtures/canon/arc_semantics.events
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
canon_event=STRAIGHT_TRAVERSE line=1 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=COMMENT: interpreter: IJK distance mode changed to incremental
|
||||
canon_event=SET_FEED_RATE rate=60
|
||||
canon_event=ARC_FEED line=3 first_end=1 second_end=1 first_axis=1 second_axis=0 rotation=-1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=COMMENT: interpreter: IJK distance mode changed to absolute
|
||||
canon_event=ARC_FEED line=5 first_end=0 second_end=0 first_axis=0 second_axis=1 rotation=1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=ARC_FEED line=6 first_end=1 second_end=0 first_axis=0.5 second_axis=-3.06162e-17 rotation=-1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=STRAIGHT_TRAVERSE line=7 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=ARC_FEED line=9 first_end=1 second_end=1 first_axis=0 second_axis=1 rotation=-1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=STRAIGHT_TRAVERSE line=10 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=ARC_FEED line=11 first_end=1 second_end=1 first_axis=0 second_axis=1 rotation=1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=STRAIGHT_TRAVERSE line=12 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=ARC_FEED line=13 first_end=1 second_end=0 first_axis=0.5 second_axis=0.866025 rotation=-1 axis_end_point=2 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
33
wasm-port/tests/fixtures/canon/canon_runtime_edges.events
vendored
Normal file
33
wasm-port/tests/fixtures/canon/canon_runtime_edges.events
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
canon_event=SET_SPINDLE_SPEED spindle=0 speed=1200
|
||||
canon_event=START_SPINDLE_CLOCKWISE spindle=0 wait_for_at_speed=1
|
||||
canon_event=STOP_SPINDLE_TURNING spindle=0
|
||||
canon_event=SET_SPINDLE_SPEED spindle=0 speed=900
|
||||
canon_event=START_SPINDLE_COUNTERCLOCKWISE spindle=0 wait_for_at_speed=1
|
||||
canon_event=STOP_SPINDLE_TURNING spindle=0
|
||||
canon_event=MIST_ON
|
||||
canon_event=FLOOD_ON
|
||||
canon_event=MIST_OFF
|
||||
canon_event=FLOOD_OFF
|
||||
canon_event=DWELL seconds=0.25
|
||||
canon_event=ENABLE_FEED_OVERRIDE
|
||||
canon_event=ENABLE_SPEED_OVERRIDE spindle=0
|
||||
canon_event=DISABLE_FEED_OVERRIDE
|
||||
canon_event=ENABLE_FEED_OVERRIDE
|
||||
canon_event=DISABLE_SPEED_OVERRIDE spindle=0
|
||||
canon_event=ENABLE_SPEED_OVERRIDE spindle=0
|
||||
canon_event=DISABLE_ADAPTIVE_FEED
|
||||
canon_event=ENABLE_ADAPTIVE_FEED
|
||||
canon_event=DISABLE_FEED_HOLD
|
||||
canon_event=ENABLE_FEED_HOLD
|
||||
canon_event=SET_MOTION_OUTPUT_BIT index=3
|
||||
canon_event=CLEAR_MOTION_OUTPUT_BIT index=3
|
||||
canon_event=SET_AUX_OUTPUT_BIT index=4
|
||||
canon_event=CLEAR_AUX_OUTPUT_BIT index=4
|
||||
canon_event=SET_MOTION_OUTPUT_VALUE index=2 value=12.5
|
||||
canon_event=SET_AUX_OUTPUT_VALUE index=2 value=34.5
|
||||
canon_event=WAIT index=1 input_type=1 wait_type=0 timeout=0
|
||||
canon_event=PROGRAM_STOP
|
||||
canon_event=SET_FEED_RATE rate=0
|
||||
canon_event=STOP_SPINDLE_TURNING spindle=0
|
||||
canon_event=SET_SPINDLE_MODE spindle=0 css_max=0
|
||||
canon_event=PROGRAM_END
|
||||
7
wasm-port/tests/fixtures/canon/namedparam_semantics.events
vendored
Normal file
7
wasm-port/tests/fixtures/canon/namedparam_semantics.events
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
canon_event=MESSAGE: named global=42.5000
|
||||
canon_event=MESSAGE: named local=7.2500
|
||||
canon_event=MESSAGE: exists global=1 local=1 missing=0
|
||||
canon_event=MESSAGE: ini velocity=35
|
||||
canon_event=MESSAGE: hal pin=1 signal=98.2500 param=-17
|
||||
canon_event=MESSAGE: hal u32=123456789 s64=-9000000000 u64=9000000000
|
||||
canon_event=MESSAGE: hal disconnected=12.5000 missing_exists=0
|
||||
3
wasm-port/tests/fixtures/canon/numbered_params.events
vendored
Normal file
3
wasm-port/tests/fixtures/canon/numbered_params.events
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
canon_event=MESSAGE: numbered p1=12.5000 p2=15.5000 exists1=1 exists5602=0
|
||||
canon_event=STRAIGHT_TRAVERSE line=6 x=2 y=3 z=4 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=MESSAGE: numbered pos x=2.0000 y=3.0000 z=4.0000
|
||||
3
wasm-port/tests/fixtures/canon/oword_subroutine.events
vendored
Normal file
3
wasm-port/tests/fixtures/canon/oword_subroutine.events
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
STRAIGHT_TRAVERSE line=4 x=0 y=0 z=0
|
||||
SET_FEED_RATE rate=100
|
||||
STRAIGHT_FEED line=2 x=2 y=3 z=0
|
||||
17
wasm-port/tests/fixtures/canon/program_end_modal_reset.events
vendored
Normal file
17
wasm-port/tests/fixtures/canon/program_end_modal_reset.events
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
canon_event=STRAIGHT_FEED line=9
|
||||
canon_event=SET_FEED_RATE rate=0
|
||||
canon_event=STOP_SPINDLE_TURNING spindle=0
|
||||
canon_event=SET_SPINDLE_MODE spindle=0 css_max=0
|
||||
canon_event=MIST_OFF
|
||||
canon_event=FLOOD_OFF
|
||||
canon_event=PROGRAM_END
|
||||
post_execute.plane=1
|
||||
post_execute.distance_mode=0
|
||||
post_execute.feed_mode=0
|
||||
post_execute.motion_mode=10
|
||||
post_execute.origin_index=1
|
||||
post_execute.feed_override=1
|
||||
post_execute.speed_override_0=1
|
||||
post_execute.spindle_turning_0=1
|
||||
post_execute.mist=0
|
||||
post_execute.flood=0
|
||||
11
wasm-port/tests/fixtures/canon/tool_semantics.events
vendored
Normal file
11
wasm-port/tests/fixtures/canon/tool_semantics.events
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
canon_event=SELECT_TOOL tool=2
|
||||
canon_event=MESSAGE: tool selected=2 selected_pocket=2
|
||||
canon_event=STOP_SPINDLE_TURNING spindle=0
|
||||
canon_event=CHANGE_TOOL
|
||||
canon_event=MESSAGE: tool after_m6 current_pocket=2 current_tool=2
|
||||
canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=1.25 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=MESSAGE: tool g43 offset=1 x=0.0000 y=0.0000 z=1.2500
|
||||
canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=MESSAGE: tool g49 offset=0 x=0.0000 y=0.0000 z=0.0000
|
||||
canon_event=CHANGE_TOOL_NUMBER pocket=0
|
||||
canon_event=MESSAGE: tool after_m61 current_pocket=0 current_tool=2
|
||||
4
wasm-port/tests/fixtures/canon_errors/arc_radius_mismatch.expected
vendored
Normal file
4
wasm-port/tests/fixtures/canon_errors/arc_radius_mismatch.expected
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
execute_line_1=0
|
||||
execute_line_2=5
|
||||
error_text=Radius to end of arc differs from radius to start
|
||||
absent=canon_event=ARC_FEED
|
||||
4
wasm-port/tests/fixtures/canon_errors/arc_zero_radius.expected
vendored
Normal file
4
wasm-port/tests/fixtures/canon_errors/arc_zero_radius.expected
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
execute_line_1=0
|
||||
execute_line_2=5
|
||||
error_text=Zero-radius arc
|
||||
absent=canon_event=ARC_FEED
|
||||
2
wasm-port/tests/fixtures/canon_errors/namedparam_readonly.expected
vendored
Normal file
2
wasm-port/tests/fixtures/canon_errors/namedparam_readonly.expected
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
execute_line_1=5
|
||||
error_text=Cannot assign to read-only parameter #<_feed>
|
||||
2
wasm-port/tests/fixtures/canon_errors/numbered_param_readonly.expected
vendored
Normal file
2
wasm-port/tests/fixtures/canon_errors/numbered_param_readonly.expected
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
execute_line_1=5
|
||||
error_text=Parameter is readonly
|
||||
3
wasm-port/tests/fixtures/canon_errors/tool_length_offset_not_found.expected
vendored
Normal file
3
wasm-port/tests/fixtures/canon_errors/tool_length_offset_not_found.expected
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
execute_line_1=5
|
||||
error_text=Requested tool 999 not found in the tool table
|
||||
absent=canon_event=USE_TOOL_LENGTH_OFFSET
|
||||
2
wasm-port/tests/fixtures/canon_errors/tool_not_found.expected
vendored
Normal file
2
wasm-port/tests/fixtures/canon_errors/tool_not_found.expected
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
execute_line_1=5
|
||||
error_text=Requested tool 999 not found in the tool table
|
||||
13
wasm-port/tests/fixtures/gcode/arc_semantics.ngc
vendored
Normal file
13
wasm-port/tests/fixtures/gcode/arc_semantics.ngc
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
G90 G17 G0 X0 Y0 Z0
|
||||
G91.1
|
||||
G2 X1 Y1 I1 J0 F60
|
||||
G90.1
|
||||
G3 X0 Y0 I0 J1
|
||||
G2 X1 Y0 R0.5
|
||||
G18 G0 X0 Y0 Z0
|
||||
G91.1
|
||||
G2 X1 Z1 I1 K0
|
||||
G19 G0 X0 Y0 Z0
|
||||
G3 Y1 Z1 J0 K1
|
||||
G17 G0 X0 Y0 Z0
|
||||
G2 X1 Y0 Z2 R-1
|
||||
26
wasm-port/tests/fixtures/gcode/canon_runtime_edges.ngc
vendored
Normal file
26
wasm-port/tests/fixtures/gcode/canon_runtime_edges.ngc
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
S1200 M3
|
||||
M5
|
||||
S900 M4
|
||||
M5
|
||||
M7
|
||||
M8
|
||||
M9
|
||||
G4 P0.25
|
||||
M48
|
||||
M50 P0
|
||||
M50 P1
|
||||
M51 P0
|
||||
M51 P1
|
||||
M52 P0
|
||||
M52 P1
|
||||
M53 P0
|
||||
M53 P1
|
||||
M62 P3
|
||||
M63 P3
|
||||
M64 P4
|
||||
M65 P4
|
||||
M67 E2 Q12.5
|
||||
M68 E2 Q34.5
|
||||
M66 P1 L0 Q0
|
||||
M0
|
||||
M2
|
||||
21
wasm-port/tests/fixtures/gcode/namedparam_semantics.ngc
vendored
Normal file
21
wasm-port/tests/fixtures/gcode/namedparam_semantics.ngc
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
#<_global_probe> = 42.5
|
||||
#<local_probe> = 7.25
|
||||
#<exists_global> = EXISTS[#<_global_probe>]
|
||||
#<exists_local> = EXISTS[#<local_probe>]
|
||||
#<exists_missing> = EXISTS[#<missing_probe>]
|
||||
#<ini_velocity> = #<_ini[traj]max_linear_velocity>
|
||||
#<hal_pin> = #<_hal[standalone.pin-bit]>
|
||||
#<hal_signal> = #<_hal[standalone.signal-float]>
|
||||
#<hal_param> = #<_hal[standalone.param-s32]>
|
||||
#<hal_u32> = #<_hal[standalone.pin-u32]>
|
||||
#<hal_s64> = #<_hal[standalone.signal-s64]>
|
||||
#<hal_u64> = #<_hal[standalone.param-u64]>
|
||||
#<hal_disconnected> = #<_hal[standalone.disconnected-float]>
|
||||
#<hal_missing_exists> = EXISTS[#<_hal[standalone.missing]>]
|
||||
(DEBUG, named global=%f#<_global_probe>)
|
||||
(DEBUG, named local=%f#<local_probe>)
|
||||
(DEBUG, exists global=%d#<exists_global> local=%d#<exists_local> missing=%d#<exists_missing>)
|
||||
(DEBUG, ini velocity=%d#<ini_velocity>)
|
||||
(DEBUG, hal pin=%d#<hal_pin> signal=%f#<hal_signal> param=%d#<hal_param>)
|
||||
(DEBUG, hal u32=%d#<hal_u32> s64=%.0f#<hal_s64> u64=%.0f#<hal_u64>)
|
||||
(DEBUG, hal disconnected=%f#<hal_disconnected> missing_exists=%d#<hal_missing_exists>)
|
||||
7
wasm-port/tests/fixtures/gcode/numbered_params.ngc
vendored
Normal file
7
wasm-port/tests/fixtures/gcode/numbered_params.ngc
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#1 = 12.5
|
||||
#2 = [#1 + 3]
|
||||
#3 = EXISTS[#1]
|
||||
#4 = EXISTS[#5602]
|
||||
(DEBUG, numbered p1=%f#1 p2=%f#2 exists1=%d#3 exists5602=%d#4)
|
||||
G90 G0 X2 Y3 Z4
|
||||
(DEBUG, numbered pos x=%f#5420 y=%f#5421 z=%f#5422)
|
||||
5
wasm-port/tests/fixtures/gcode/oword_subroutine.ngc
vendored
Normal file
5
wasm-port/tests/fixtures/gcode/oword_subroutine.ngc
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
O<move> sub
|
||||
G1 X#1 Y#2 F100
|
||||
O<move> endsub
|
||||
G0 X0 Y0
|
||||
O<move> call [2] [3]
|
||||
10
wasm-port/tests/fixtures/gcode/program_end_modal_reset.ngc
vendored
Normal file
10
wasm-port/tests/fixtures/gcode/program_end_modal_reset.ngc
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
G55
|
||||
G18
|
||||
G91
|
||||
G93
|
||||
M49
|
||||
S1200 M3
|
||||
M7
|
||||
M8
|
||||
G1 X1 Z1 F2
|
||||
M2
|
||||
10
wasm-port/tests/fixtures/gcode/tool_semantics.ngc
vendored
Normal file
10
wasm-port/tests/fixtures/gcode/tool_semantics.ngc
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
T2
|
||||
(DEBUG, tool selected=%d#<_selected_tool> selected_pocket=%d#<_selected_pocket>)
|
||||
M6
|
||||
(DEBUG, tool after_m6 current_pocket=%d#<_current_pocket> current_tool=%d#<_current_tool>)
|
||||
G43 H2
|
||||
(DEBUG, tool g43 offset=%d#<_tool_offset> x=%f#5401 y=%f#5402 z=%f#5403)
|
||||
G49
|
||||
(DEBUG, tool g49 offset=%d#<_tool_offset> x=%f#5401 y=%f#5402 z=%f#5403)
|
||||
M61 Q2
|
||||
(DEBUG, tool after_m61 current_pocket=%d#<_current_pocket> current_tool=%d#<_current_tool>)
|
||||
2
wasm-port/tests/fixtures/gcode_errors/arc_radius_mismatch.ngc
vendored
Normal file
2
wasm-port/tests/fixtures/gcode_errors/arc_radius_mismatch.ngc
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
G90 G17 G0 X0 Y0
|
||||
G2 X1 Y1 I0.2 J0 F60
|
||||
2
wasm-port/tests/fixtures/gcode_errors/arc_zero_radius.ngc
vendored
Normal file
2
wasm-port/tests/fixtures/gcode_errors/arc_zero_radius.ngc
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
G90 G17 G0 X0 Y0
|
||||
G2 X1 Y0 I0 J0 F60
|
||||
1
wasm-port/tests/fixtures/gcode_errors/namedparam_readonly.ngc
vendored
Normal file
1
wasm-port/tests/fixtures/gcode_errors/namedparam_readonly.ngc
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#<_feed> = 1
|
||||
1
wasm-port/tests/fixtures/gcode_errors/numbered_param_readonly.ngc
vendored
Normal file
1
wasm-port/tests/fixtures/gcode_errors/numbered_param_readonly.ngc
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#5400 = 1
|
||||
1
wasm-port/tests/fixtures/gcode_errors/tool_length_offset_not_found.ngc
vendored
Normal file
1
wasm-port/tests/fixtures/gcode_errors/tool_length_offset_not_found.ngc
vendored
Normal file
@@ -0,0 +1 @@
|
||||
G43 H999
|
||||
1
wasm-port/tests/fixtures/gcode_errors/tool_not_found.ngc
vendored
Normal file
1
wasm-port/tests/fixtures/gcode_errors/tool_not_found.ngc
vendored
Normal file
@@ -0,0 +1 @@
|
||||
T999
|
||||
3
wasm-port/tests/fixtures/ini/namedparams.ini
vendored
Normal file
3
wasm-port/tests/fixtures/ini/namedparams.ini
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[TRAJ]
|
||||
LINEAR_UNITS = mm
|
||||
MAX_LINEAR_VELOCITY = 35
|
||||
@@ -7,6 +7,7 @@ GCODE_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode"
|
||||
CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon"
|
||||
GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors"
|
||||
CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors"
|
||||
NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini"
|
||||
|
||||
"$ROOT_DIR/tools/build_native_probes.sh"
|
||||
|
||||
@@ -31,26 +32,95 @@ check_exitcode() {
|
||||
}
|
||||
|
||||
check_exitcode linuxcnc_interp_state_probe
|
||||
check_exitcode linuxcnc_tp_api_probe
|
||||
check_exitcode linuxcnc_tp_api_probe.run
|
||||
check_exitcode linuxcnc_namedparam_harness
|
||||
check_exitcode linuxcnc_namedparam_harness.run
|
||||
check_exitcode linuxcnc_interp_minimal_harness
|
||||
check_exitcode linuxcnc_interp_minimal_harness.run
|
||||
check_exitcode linuxcnc_parameter_file_harness
|
||||
check_exitcode linuxcnc_parameter_file_harness.run
|
||||
check_exitcode linuxcnc_rs274_compile_probe
|
||||
check_exitcode linuxcnc_interp_convert_source_probe
|
||||
|
||||
NAMEDPARAM_STDOUT="$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log"
|
||||
grep -Fq "init_named_parameters=0" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "global_named_count=57" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_metric_machine: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_motion_mode: rc=0 found=1 value=10" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_metric: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_feed: rc=0 found=1 value=123.45" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_rpm: rc=0 found=1 value=678.9" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_x: rc=0 found=1 value=1.25" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_current_tool: rc=0 found=1 value=12" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_ini[traj]max_linear_velocity: rc=0 found=1 value=35" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_hal[standalone.pin-bit]: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_hal[standalone.signal-float]: rc=0 found=1 value=98.25" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_hal[standalone.param-s32]: rc=0 found=1 value=-17" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_hal[standalone.pin-u32]: rc=0 found=1 value=1.23457e+08" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_hal[standalone.signal-s64]: rc=0 found=1 value=-9e+09" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_hal[standalone.param-u64]: rc=0 found=1 value=9e+09" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_hal[standalone.disconnected-float]: rc=0 found=1 value=12.5" "$NAMEDPARAM_STDOUT"
|
||||
grep -Fq "_hal[standalone.missing]: rc=0 found=0 value=0" "$NAMEDPARAM_STDOUT"
|
||||
|
||||
PARAMETER_FILE_STDOUT="$BUILD_DIR/linuxcnc_parameter_file_harness.run.stdout.log"
|
||||
grep -Fq "restore_existing=0" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "parameter_5161=10.5" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "parameter_5162=20.25" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "parameter_5220=1" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "parameter_5221=2.25" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "parameter_5399=44" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "restore_missing_file=0" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "restore_out_of_order=5" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "restore_out_of_order_error=Parameter file out of order" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "restore_missing_required=0" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "missing_required_5161=3.5" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "missing_required_5162=0" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "save_parameters=0" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "saved_has_5161=1" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "saved_has_5162=1" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "saved_has_5221=1" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "saved_has_5399=1" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "saved_has_named_param=0" "$PARAMETER_FILE_STDOUT"
|
||||
grep -Fq "backup_has_original_5161=1" "$PARAMETER_FILE_STDOUT"
|
||||
|
||||
TP_API_STDOUT="$BUILD_DIR/linuxcnc_tp_api_probe.run.stdout.log"
|
||||
grep -Fq "tp_default_queue_size=32" "$TP_API_STDOUT"
|
||||
grep -Fq "tp_err_ok=0" "$TP_API_STDOUT"
|
||||
grep -Fq "tc_linear=1" "$TP_API_STDOUT"
|
||||
grep -Fq "tc_circular=2" "$TP_API_STDOUT"
|
||||
grep -Fq "pose_xyz=1,2,3" "$TP_API_STDOUT"
|
||||
grep -Fq "tp_create=0" "$TP_API_STDOUT"
|
||||
grep -Fq "tp_set_cycle_time=0" "$TP_API_STDOUT"
|
||||
grep -Fq "tp_add_line=0" "$TP_API_STDOUT"
|
||||
grep -Fq "tp_done_after_line=1" "$TP_API_STDOUT"
|
||||
grep -Fq "tp_final_pos=1,0,0" "$TP_API_STDOUT"
|
||||
|
||||
check_fixture_output() {
|
||||
local fixture="$1"
|
||||
local expected="$2"
|
||||
local stdout_file="$3"
|
||||
local require_mdi="${4:-1}"
|
||||
|
||||
grep -Fq "read=0" "$stdout_file"
|
||||
grep -Fq "parse_line=0" "$stdout_file"
|
||||
|
||||
if [[ "$require_mdi" == "1" ]]; then
|
||||
local line_count
|
||||
line_count="$(grep -cv '^[[:space:]]*$' "$fixture")"
|
||||
local line_number
|
||||
for ((line_number = 1; line_number <= line_count; line_number += 1)); do
|
||||
grep -Fq "execute_line_${line_number}=0" "$stdout_file"
|
||||
if ! grep -Fq "execute_line_${line_number}=0" "$stdout_file" &&
|
||||
! grep -Fq "execute_line_${line_number}=1" "$stdout_file" &&
|
||||
! grep -Fq "execute_line_${line_number}=2" "$stdout_file"; then
|
||||
echo "missing successful execute status for line $line_number in $stdout_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
else
|
||||
grep -Fq "file_open=0" "$stdout_file"
|
||||
grep -Fq "file_execute_1=0" "$stdout_file"
|
||||
fi
|
||||
|
||||
while IFS= read -r expected_event; do
|
||||
[[ -z "$expected_event" ]] && continue
|
||||
@@ -74,10 +144,14 @@ for fixture in "$GCODE_FIXTURE_DIR"/*.ngc; do
|
||||
|
||||
stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stdout.log"
|
||||
stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stderr.log"
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \
|
||||
INI_FILE_NAME="$NAMEDPARAM_INI_FIXTURE" "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \
|
||||
>"$stdout_file" \
|
||||
2>"$stderr_file"
|
||||
check_fixture_output "$fixture" "$expected" "$stdout_file"
|
||||
require_mdi=1
|
||||
if [[ "$name" == "oword_subroutine" ]]; then
|
||||
require_mdi=0
|
||||
fi
|
||||
check_fixture_output "$fixture" "$expected" "$stdout_file" "$require_mdi"
|
||||
done
|
||||
|
||||
for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
|
||||
@@ -90,7 +164,7 @@ for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
|
||||
|
||||
stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stdout.log"
|
||||
stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stderr.log"
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \
|
||||
INI_FILE_NAME="$NAMEDPARAM_INI_FIXTURE" "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \
|
||||
>"$stdout_file" \
|
||||
2>"$stderr_file"
|
||||
|
||||
@@ -103,7 +177,7 @@ for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
|
||||
exit 1
|
||||
fi
|
||||
elif [[ "$expected_line" == error_text=* ]]; then
|
||||
grep -Fq "${expected_line#error_text=}" "$stderr_file"
|
||||
grep -Fq "$expected_line" "$stdout_file"
|
||||
else
|
||||
grep -Fq "$expected_line" "$stdout_file"
|
||||
fi
|
||||
|
||||
@@ -8,141 +8,408 @@ WRAP_DIR="$ROOT_DIR/runtime/core/linuxcnc_wrap"
|
||||
SHIM_DIR="$ROOT_DIR/runtime/core/shims"
|
||||
INCLUDE_DIR="$ROOT_DIR/runtime/core/include"
|
||||
MINIMAL_GCODE_FIXTURE="$ROOT_DIR/tests/fixtures/gcode/minimal_linear.ngc"
|
||||
NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini"
|
||||
CXX="${CXX:-g++}"
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
rm -f \
|
||||
COMMON_FLAGS=(
|
||||
-std=c++20
|
||||
-O2
|
||||
-D_GNU_SOURCE
|
||||
-DM_PI=3.14159265358979323846
|
||||
-DOBJECT_FWD_DWA2002724_HPP
|
||||
-I"$WRAP_DIR"
|
||||
-I"$SHIM_DIR"
|
||||
-I"$INCLUDE_DIR"
|
||||
-I"$VENDOR_DIR/src"
|
||||
-I"$VENDOR_DIR/src/rtapi"
|
||||
-I"$VENDOR_DIR/src/emc"
|
||||
-I"$VENDOR_DIR/src/emc/nml_intf"
|
||||
-I"$VENDOR_DIR/src/emc/motion"
|
||||
-I"$VENDOR_DIR/src/emc/tp"
|
||||
-I"$VENDOR_DIR/src/emc/kinematics"
|
||||
-I"$VENDOR_DIR/src/libnml/posemath"
|
||||
)
|
||||
|
||||
COMMON_INI_FLAGS=(
|
||||
"${COMMON_FLAGS[@]}"
|
||||
-I"$VENDOR_DIR/src/emc/ini"
|
||||
)
|
||||
|
||||
MINIMAL_FLAGS=(
|
||||
"${COMMON_INI_FLAGS[@]}"
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
-DUNIT_TEST
|
||||
-DLINUXCNC_STANDALONE_USE_RS274_PRE_STATE
|
||||
)
|
||||
|
||||
MINIMAL_LINK_FLAGS=(
|
||||
-Wl,--gc-sections
|
||||
)
|
||||
|
||||
TP_FLAGS=(
|
||||
"${COMMON_FLAGS[@]}"
|
||||
-fpermissive
|
||||
)
|
||||
|
||||
write_command_file() {
|
||||
local file="$1"
|
||||
shift
|
||||
printf '%q\n' "$@" > "$file"
|
||||
}
|
||||
|
||||
command_matches() {
|
||||
local file="$1"
|
||||
shift
|
||||
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
write_command_file "$tmp" "$@"
|
||||
if [[ -f "$file" ]] && cmp -s "$tmp" "$file"; then
|
||||
rm -f "$tmp"
|
||||
return 0
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
}
|
||||
|
||||
depfile_newer_than() {
|
||||
local depfile="$1"
|
||||
local output="$2"
|
||||
|
||||
[[ -f "$depfile" ]] || return 0
|
||||
local deps
|
||||
deps="$(tr '\\\n' ' ' < "$depfile" | sed -e 's/^[^:]*://')"
|
||||
local dep
|
||||
for dep in $deps; do
|
||||
if [[ -f "$dep" && "$dep" -nt "$output" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
object_needs_rebuild() {
|
||||
local obj="$1"
|
||||
local depfile="$2"
|
||||
local cmdfile="$3"
|
||||
shift 3
|
||||
|
||||
[[ -f "$obj" ]] || return 0
|
||||
command_matches "$cmdfile" "$@" || return 0
|
||||
depfile_newer_than "$depfile" "$obj" && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
compile_object() {
|
||||
local target="$1"
|
||||
local source="$2"
|
||||
local obj="$3"
|
||||
shift 3
|
||||
|
||||
local stdout_log="$BUILD_DIR/$target.stdout.log"
|
||||
local stderr_log="$BUILD_DIR/$target.stderr.log"
|
||||
local depfile="$obj.d"
|
||||
local cmdfile="$obj.cmd"
|
||||
local cmd=("$CXX" "$@" -MMD -MP -MF "$depfile" -c "$source" -o "$obj")
|
||||
|
||||
mkdir -p "$(dirname "$obj")"
|
||||
if ! object_needs_rebuild "$obj" "$depfile" "$cmdfile" "${cmd[@]}"; then
|
||||
printf 'up to date: %s\n' "$source" >> "$stdout_log"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf 'compile: %s\n' "$source" >> "$stdout_log"
|
||||
if "${cmd[@]}" >> "$stdout_log" 2>> "$stderr_log"; then
|
||||
write_command_file "$cmdfile" "${cmd[@]}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
link_needs_rebuild() {
|
||||
local output="$1"
|
||||
local cmdfile="$2"
|
||||
shift 2
|
||||
|
||||
[[ -f "$output" ]] || return 0
|
||||
command_matches "$cmdfile" "$@" || return 0
|
||||
|
||||
local arg
|
||||
for arg in "$@"; do
|
||||
if [[ -f "$arg" && "$arg" -nt "$output" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
link_binary() {
|
||||
local target="$1"
|
||||
local output="$2"
|
||||
shift 2
|
||||
|
||||
local stdout_log="$BUILD_DIR/$target.stdout.log"
|
||||
local stderr_log="$BUILD_DIR/$target.stderr.log"
|
||||
local cmdfile="$output.cmd"
|
||||
local cmd=("$CXX" "$@" -o "$output")
|
||||
|
||||
if ! link_needs_rebuild "$output" "$cmdfile" "${cmd[@]}"; then
|
||||
printf 'up to date: %s\n' "$output" >> "$stdout_log"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf 'link: %s\n' "$output" >> "$stdout_log"
|
||||
if "${cmd[@]}" >> "$stdout_log" 2>> "$stderr_log"; then
|
||||
write_command_file "$cmdfile" "${cmd[@]}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
compile_target_objects() {
|
||||
local target="$1"
|
||||
local flags_name="$2"
|
||||
local sources_name="$3"
|
||||
local objs_name="$4"
|
||||
|
||||
local -n flags="$flags_name"
|
||||
local -n sources="$sources_name"
|
||||
local -n objs="$objs_name"
|
||||
local obj_dir="$BUILD_DIR/obj/$target"
|
||||
local status=0
|
||||
|
||||
objs=()
|
||||
local source
|
||||
for source in "${sources[@]}"; do
|
||||
local base
|
||||
base="$(basename "$source")"
|
||||
local obj="$obj_dir/${base%.*}.o"
|
||||
objs+=("$obj")
|
||||
if ! compile_object "$target" "$source" "$obj" "${flags[@]}"; then
|
||||
status=1
|
||||
fi
|
||||
done
|
||||
|
||||
return "$status"
|
||||
}
|
||||
|
||||
build_binary_target() {
|
||||
local target="$1"
|
||||
local output="$2"
|
||||
local flags_name="$3"
|
||||
local sources_name="$4"
|
||||
local link_flags_name="$5"
|
||||
shift 5
|
||||
|
||||
local stdout_log="$BUILD_DIR/$target.stdout.log"
|
||||
local stderr_log="$BUILD_DIR/$target.stderr.log"
|
||||
: > "$stdout_log"
|
||||
: > "$stderr_log"
|
||||
|
||||
local target_objs=()
|
||||
local status=0
|
||||
if ! compile_target_objects "$target" "$flags_name" "$sources_name" target_objs; then
|
||||
status=1
|
||||
fi
|
||||
|
||||
if [[ "$status" -eq 0 ]]; then
|
||||
local -n link_flags="$link_flags_name"
|
||||
if ! link_binary "$target" "$output" "${link_flags[@]}" "${target_objs[@]}" "$@"; then
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$status" > "$BUILD_DIR/$target.exitcode"
|
||||
return "$status"
|
||||
}
|
||||
|
||||
build_object_target() {
|
||||
local target="$1"
|
||||
local output="$2"
|
||||
local source="$3"
|
||||
local flags_name="$4"
|
||||
|
||||
local stdout_log="$BUILD_DIR/$target.stdout.log"
|
||||
local stderr_log="$BUILD_DIR/$target.stderr.log"
|
||||
: > "$stdout_log"
|
||||
: > "$stderr_log"
|
||||
|
||||
local -n flags="$flags_name"
|
||||
local status=0
|
||||
if ! compile_object "$target" "$source" "$output" "${flags[@]}"; then
|
||||
status=1
|
||||
fi
|
||||
|
||||
echo "$status" > "$BUILD_DIR/$target.exitcode"
|
||||
return "$status"
|
||||
}
|
||||
|
||||
NO_LINK_FLAGS=()
|
||||
|
||||
INI_PROBE_SOURCES=(
|
||||
"$VENDOR_DIR/src/emc/ini/inifile.cc"
|
||||
"$WRAP_DIR/linuxcnc_ini_probe.cpp"
|
||||
)
|
||||
|
||||
STATE_PROBE_SOURCES=(
|
||||
"$WRAP_DIR/linuxcnc_interp_state_probe.cpp"
|
||||
)
|
||||
|
||||
TP_API_PROBE_SOURCES=(
|
||||
"$WRAP_DIR/linuxcnc_tp_api_probe.cpp"
|
||||
)
|
||||
|
||||
TP_CORE_SOURCES=(
|
||||
"$VENDOR_DIR/src/emc/tp/tp.c"
|
||||
"$VENDOR_DIR/src/emc/tp/tc.c"
|
||||
"$VENDOR_DIR/src/emc/tp/tcq.c"
|
||||
"$VENDOR_DIR/src/emc/tp/spherical_arc.c"
|
||||
"$VENDOR_DIR/src/emc/tp/blendmath.c"
|
||||
"$VENDOR_DIR/src/emc/tp/sp_scurve.c"
|
||||
"$VENDOR_DIR/src/emc/tp/ruckig_wrapper.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/block.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/brake.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/calculator.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/cruckig.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/input_parameter.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/output_parameter.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/profile.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/roots.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/trajectory.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/position_first_step1.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/position_first_step2.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/position_second_step1.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/position_second_step2.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/position_third_step1.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/position_third_step2.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/velocity_second_step1.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/velocity_second_step2.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/velocity_third_step1.c"
|
||||
"$VENDOR_DIR/src/emc/tp/cruckig/velocity_third_step2.c"
|
||||
"$VENDOR_DIR/src/emc/nml_intf/emcpose.c"
|
||||
"$VENDOR_DIR/src/libnml/posemath/posemath.cc"
|
||||
"$VENDOR_DIR/src/libnml/posemath/_posemath.c"
|
||||
"$VENDOR_DIR/src/libnml/posemath/sincos.c"
|
||||
)
|
||||
|
||||
TP_RUNTIME_PROBE_SOURCES=(
|
||||
"${TP_CORE_SOURCES[@]}"
|
||||
"$WRAP_DIR/linuxcnc_tp_api_probe.cpp"
|
||||
)
|
||||
|
||||
INTERP_CORE_SOURCES=(
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_array.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_internal.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_read.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_check.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_arc.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_inverse.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_execute.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_cycles.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_g7x.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_queue.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_find.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_namedparams.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_write.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_o_word.cc"
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/rs274ngc_pre.cc"
|
||||
"$WRAP_DIR/linuxcnc_hal_adapter.cpp"
|
||||
"$WRAP_DIR/linuxcnc_interp_edge_stubs.cpp"
|
||||
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp"
|
||||
"$WRAP_DIR/linuxcnc_tool_adapter.cpp"
|
||||
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp"
|
||||
"$VENDOR_DIR/src/emc/ini/inifile.cc"
|
||||
)
|
||||
|
||||
NAMEDPARAM_SOURCES=(
|
||||
"${INTERP_CORE_SOURCES[@]}"
|
||||
"$WRAP_DIR/linuxcnc_namedparam_harness.cpp"
|
||||
)
|
||||
|
||||
MINIMAL_SOURCES=(
|
||||
"${INTERP_CORE_SOURCES[@]}"
|
||||
"$WRAP_DIR/linuxcnc_interp_minimal_harness.cpp"
|
||||
)
|
||||
|
||||
PARAMETER_FILE_SOURCES=(
|
||||
"${INTERP_CORE_SOURCES[@]}"
|
||||
"$WRAP_DIR/linuxcnc_parameter_file_harness.cpp"
|
||||
)
|
||||
|
||||
build_binary_target \
|
||||
linuxcnc_ini_probe \
|
||||
"$BUILD_DIR/linuxcnc_ini_probe" \
|
||||
COMMON_FLAGS \
|
||||
INI_PROBE_SOURCES \
|
||||
NO_LINK_FLAGS \
|
||||
-lfmt
|
||||
|
||||
build_binary_target \
|
||||
linuxcnc_interp_state_probe \
|
||||
"$BUILD_DIR/linuxcnc_interp_state_probe" \
|
||||
"$BUILD_DIR/linuxcnc_interp_state_probe.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_interp_state_probe.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_interp_state_probe.stderr.log" \
|
||||
COMMON_FLAGS \
|
||||
STATE_PROBE_SOURCES \
|
||||
NO_LINK_FLAGS
|
||||
|
||||
build_binary_target \
|
||||
linuxcnc_tp_api_probe \
|
||||
"$BUILD_DIR/linuxcnc_tp_api_probe" \
|
||||
TP_FLAGS \
|
||||
TP_RUNTIME_PROBE_SOURCES \
|
||||
NO_LINK_FLAGS
|
||||
|
||||
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_tp_api_probe.exitcode")" == "0" ]]; then
|
||||
set +e
|
||||
"$BUILD_DIR/linuxcnc_tp_api_probe" \
|
||||
>"$BUILD_DIR/linuxcnc_tp_api_probe.run.stdout.log" \
|
||||
2>"$BUILD_DIR/linuxcnc_tp_api_probe.run.stderr.log"
|
||||
TP_API_RUN_RC=$?
|
||||
set -e
|
||||
echo "$TP_API_RUN_RC" > "$BUILD_DIR/linuxcnc_tp_api_probe.run.exitcode"
|
||||
else
|
||||
rm -f \
|
||||
"$BUILD_DIR/linuxcnc_tp_api_probe.run.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_tp_api_probe.run.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_tp_api_probe.run.stderr.log"
|
||||
fi
|
||||
|
||||
build_binary_target \
|
||||
linuxcnc_namedparam_harness \
|
||||
"$BUILD_DIR/linuxcnc_namedparam_harness" \
|
||||
"$BUILD_DIR/linuxcnc_namedparam_harness.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_namedparam_harness.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_namedparam_harness.stderr.log" \
|
||||
MINIMAL_FLAGS \
|
||||
NAMEDPARAM_SOURCES \
|
||||
MINIMAL_LINK_FLAGS \
|
||||
-lfmt
|
||||
|
||||
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_namedparam_harness.exitcode")" == "0" ]]; then
|
||||
set +e
|
||||
"$BUILD_DIR/linuxcnc_namedparam_harness" "$NAMEDPARAM_INI_FIXTURE" \
|
||||
>"$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" \
|
||||
2>"$BUILD_DIR/linuxcnc_namedparam_harness.run.stderr.log"
|
||||
NAMEDPARAM_RUN_RC=$?
|
||||
set -e
|
||||
echo "$NAMEDPARAM_RUN_RC" > "$BUILD_DIR/linuxcnc_namedparam_harness.run.exitcode"
|
||||
else
|
||||
rm -f \
|
||||
"$BUILD_DIR/linuxcnc_namedparam_harness.run.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_namedparam_harness.run.stderr.log"
|
||||
fi
|
||||
|
||||
build_binary_target \
|
||||
linuxcnc_interp_minimal_harness \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness" \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.stderr.log" \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stderr.log" \
|
||||
"$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \
|
||||
"$BUILD_DIR/linuxcnc_rs274_compile_probe.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_rs274_compile_probe.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_rs274_compile_probe.stderr.log" \
|
||||
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.o" \
|
||||
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.stderr.log"
|
||||
MINIMAL_FLAGS \
|
||||
MINIMAL_SOURCES \
|
||||
MINIMAL_LINK_FLAGS \
|
||||
-lfmt
|
||||
|
||||
g++ -std=c++20 -O2 \
|
||||
-D_GNU_SOURCE \
|
||||
-DM_PI=3.14159265358979323846 \
|
||||
-DOBJECT_FWD_DWA2002724_HPP \
|
||||
-I"$SHIM_DIR" \
|
||||
-I"$INCLUDE_DIR" \
|
||||
-I"$VENDOR_DIR/src" \
|
||||
-I"$VENDOR_DIR/src/rtapi" \
|
||||
-I"$VENDOR_DIR/src/emc" \
|
||||
-I"$VENDOR_DIR/src/emc/nml_intf" \
|
||||
-I"$VENDOR_DIR/src/emc/motion" \
|
||||
-I"$VENDOR_DIR/src/libnml/posemath" \
|
||||
"$VENDOR_DIR/src/emc/ini/inifile.cc" \
|
||||
"$WRAP_DIR/linuxcnc_ini_probe.cpp" \
|
||||
-lfmt \
|
||||
-o "$BUILD_DIR/linuxcnc_ini_probe"
|
||||
|
||||
set +e
|
||||
g++ -std=c++20 -O2 \
|
||||
-D_GNU_SOURCE \
|
||||
-DM_PI=3.14159265358979323846 \
|
||||
-DOBJECT_FWD_DWA2002724_HPP \
|
||||
-I"$SHIM_DIR" \
|
||||
-I"$INCLUDE_DIR" \
|
||||
-I"$VENDOR_DIR/src" \
|
||||
-I"$VENDOR_DIR/src/rtapi" \
|
||||
-I"$VENDOR_DIR/src/emc" \
|
||||
-I"$VENDOR_DIR/src/emc/nml_intf" \
|
||||
-I"$VENDOR_DIR/src/emc/motion" \
|
||||
-I"$VENDOR_DIR/src/libnml/posemath" \
|
||||
"$WRAP_DIR/linuxcnc_interp_state_probe.cpp" \
|
||||
-o "$BUILD_DIR/linuxcnc_interp_state_probe" \
|
||||
>"$BUILD_DIR/linuxcnc_interp_state_probe.stdout.log" \
|
||||
2>"$BUILD_DIR/linuxcnc_interp_state_probe.stderr.log"
|
||||
STATE_RC=$?
|
||||
set -e
|
||||
|
||||
echo "$STATE_RC" > "$BUILD_DIR/linuxcnc_interp_state_probe.exitcode"
|
||||
|
||||
set +e
|
||||
g++ -std=c++20 -O2 \
|
||||
-D_GNU_SOURCE \
|
||||
-DM_PI=3.14159265358979323846 \
|
||||
-DOBJECT_FWD_DWA2002724_HPP \
|
||||
-I"$SHIM_DIR" \
|
||||
-I"$INCLUDE_DIR" \
|
||||
-I"$VENDOR_DIR/src" \
|
||||
-I"$VENDOR_DIR/src/rtapi" \
|
||||
-I"$VENDOR_DIR/src/emc" \
|
||||
-I"$VENDOR_DIR/src/emc/nml_intf" \
|
||||
-I"$VENDOR_DIR/src/emc/motion" \
|
||||
-I"$VENDOR_DIR/src/libnml/posemath" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc" \
|
||||
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp" \
|
||||
"$WRAP_DIR/linuxcnc_namedparam_harness.cpp" \
|
||||
"$VENDOR_DIR/src/emc/ini/inifile.cc" \
|
||||
-lfmt \
|
||||
-o "$BUILD_DIR/linuxcnc_namedparam_harness" \
|
||||
>"$BUILD_DIR/linuxcnc_namedparam_harness.stdout.log" \
|
||||
2>"$BUILD_DIR/linuxcnc_namedparam_harness.stderr.log"
|
||||
NAMEDPARAM_RC=$?
|
||||
set -e
|
||||
|
||||
echo "$NAMEDPARAM_RC" > "$BUILD_DIR/linuxcnc_namedparam_harness.exitcode"
|
||||
|
||||
set +e
|
||||
g++ -std=c++20 -O2 -ffunction-sections -fdata-sections \
|
||||
-Wl,--gc-sections \
|
||||
-D_GNU_SOURCE \
|
||||
-DM_PI=3.14159265358979323846 \
|
||||
-DOBJECT_FWD_DWA2002724_HPP \
|
||||
-I"$SHIM_DIR" \
|
||||
-I"$INCLUDE_DIR" \
|
||||
-I"$VENDOR_DIR/src" \
|
||||
-I"$VENDOR_DIR/src/rtapi" \
|
||||
-I"$VENDOR_DIR/src/emc" \
|
||||
-I"$VENDOR_DIR/src/emc/nml_intf" \
|
||||
-I"$VENDOR_DIR/src/emc/motion" \
|
||||
-I"$VENDOR_DIR/src/libnml/posemath" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_array.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_internal.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_read.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_check.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_arc.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_inverse.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_execute.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_cycles.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_g7x.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_queue.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_find.cc" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_write.cc" \
|
||||
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp" \
|
||||
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp" \
|
||||
"$WRAP_DIR/linuxcnc_interp_minimal_harness.cpp" \
|
||||
-lfmt \
|
||||
-o "$BUILD_DIR/linuxcnc_interp_minimal_harness" \
|
||||
>"$BUILD_DIR/linuxcnc_interp_minimal_harness.stdout.log" \
|
||||
2>"$BUILD_DIR/linuxcnc_interp_minimal_harness.stderr.log"
|
||||
INTERP_MIN_RC=$?
|
||||
set -e
|
||||
|
||||
echo "$INTERP_MIN_RC" > "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode"
|
||||
|
||||
if [[ "$INTERP_MIN_RC" -eq 0 ]]; then
|
||||
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then
|
||||
set +e
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$MINIMAL_GCODE_FIXTURE" \
|
||||
>"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \
|
||||
@@ -150,49 +417,46 @@ if [[ "$INTERP_MIN_RC" -eq 0 ]]; then
|
||||
INTERP_MIN_RUN_RC=$?
|
||||
set -e
|
||||
echo "$INTERP_MIN_RUN_RC" > "$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode"
|
||||
else
|
||||
rm -f \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stderr.log"
|
||||
fi
|
||||
|
||||
build_binary_target \
|
||||
linuxcnc_parameter_file_harness \
|
||||
"$BUILD_DIR/linuxcnc_parameter_file_harness" \
|
||||
MINIMAL_FLAGS \
|
||||
PARAMETER_FILE_SOURCES \
|
||||
MINIMAL_LINK_FLAGS \
|
||||
-lfmt
|
||||
|
||||
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_parameter_file_harness.exitcode")" == "0" ]]; then
|
||||
set +e
|
||||
g++ -std=c++20 -O2 \
|
||||
-D_GNU_SOURCE \
|
||||
-DM_PI=3.14159265358979323846 \
|
||||
-DOBJECT_FWD_DWA2002724_HPP \
|
||||
-I"$SHIM_DIR" \
|
||||
-I"$INCLUDE_DIR" \
|
||||
-I"$VENDOR_DIR/src" \
|
||||
-I"$VENDOR_DIR/src/rtapi" \
|
||||
-I"$VENDOR_DIR/src/emc" \
|
||||
-I"$VENDOR_DIR/src/emc/nml_intf" \
|
||||
-I"$VENDOR_DIR/src/emc/motion" \
|
||||
-I"$VENDOR_DIR/src/libnml/posemath" \
|
||||
-c "$WRAP_DIR/linuxcnc_rs274_compile_probe.cpp" \
|
||||
-o "$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \
|
||||
>"$BUILD_DIR/linuxcnc_rs274_compile_probe.stdout.log" \
|
||||
2>"$BUILD_DIR/linuxcnc_rs274_compile_probe.stderr.log"
|
||||
RS274_RC=$?
|
||||
"$BUILD_DIR/linuxcnc_parameter_file_harness" \
|
||||
>"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stdout.log" \
|
||||
2>"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stderr.log"
|
||||
PARAMETER_FILE_RUN_RC=$?
|
||||
set -e
|
||||
echo "$PARAMETER_FILE_RUN_RC" > "$BUILD_DIR/linuxcnc_parameter_file_harness.run.exitcode"
|
||||
else
|
||||
rm -f \
|
||||
"$BUILD_DIR/linuxcnc_parameter_file_harness.run.exitcode" \
|
||||
"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stdout.log" \
|
||||
"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stderr.log"
|
||||
fi
|
||||
|
||||
echo "$RS274_RC" > "$BUILD_DIR/linuxcnc_rs274_compile_probe.exitcode"
|
||||
build_object_target \
|
||||
linuxcnc_rs274_compile_probe \
|
||||
"$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \
|
||||
"$WRAP_DIR/linuxcnc_rs274_compile_probe.cpp" \
|
||||
COMMON_FLAGS
|
||||
|
||||
set +e
|
||||
g++ -std=c++20 -O2 \
|
||||
-D_GNU_SOURCE \
|
||||
-DM_PI=3.14159265358979323846 \
|
||||
-DOBJECT_FWD_DWA2002724_HPP \
|
||||
-I"$SHIM_DIR" \
|
||||
-I"$INCLUDE_DIR" \
|
||||
-I"$VENDOR_DIR/src" \
|
||||
-I"$VENDOR_DIR/src/rtapi" \
|
||||
-I"$VENDOR_DIR/src/emc" \
|
||||
-I"$VENDOR_DIR/src/emc/nml_intf" \
|
||||
-I"$VENDOR_DIR/src/emc/motion" \
|
||||
-I"$VENDOR_DIR/src/libnml/posemath" \
|
||||
-c "$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc" \
|
||||
-o "$BUILD_DIR/linuxcnc_interp_convert_source_probe.o" \
|
||||
>"$BUILD_DIR/linuxcnc_interp_convert_source_probe.stdout.log" \
|
||||
2>"$BUILD_DIR/linuxcnc_interp_convert_source_probe.stderr.log"
|
||||
CONVERT_SOURCE_RC=$?
|
||||
set -e
|
||||
build_object_target \
|
||||
linuxcnc_interp_convert_source_probe \
|
||||
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.o" \
|
||||
"$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc" \
|
||||
COMMON_FLAGS
|
||||
|
||||
echo "$CONVERT_SOURCE_RC" > "$BUILD_DIR/linuxcnc_interp_convert_source_probe.exitcode"
|
||||
echo "native probes complete"
|
||||
|
||||
@@ -2,12 +2,18 @@ src/emc/ini/inifile.cc
|
||||
src/emc/ini/inifile.h
|
||||
src/emc/ini/inifile.hh
|
||||
src/rtapi/rtapi_stdint.h
|
||||
src/rtapi/rtapi_bool.h
|
||||
src/rtapi/rtapi_limits.h
|
||||
src/rtapi/rtapi_atomic.h
|
||||
src/rtapi/rtapi_slab.h
|
||||
src/rtapi/rtapi_string.h
|
||||
src/rtapi/rtapi_gfp.h
|
||||
src/rtapi/rtapi_math.h
|
||||
src/rtapi/rtapi_byteorder.h
|
||||
src/emc/nml_intf/emcpos.h
|
||||
src/emc/nml_intf/emcpose.h
|
||||
src/emc/nml_intf/emcpose.c
|
||||
src/emc/nml_intf/motion_types.h
|
||||
src/emc/linuxcnc.h
|
||||
src/emc/nml_intf/canon.hh
|
||||
src/emc/nml_intf/canon_position.hh
|
||||
@@ -17,11 +23,71 @@ src/emc/nml_intf/debugflags.h
|
||||
src/emc/nml_intf/interp_return.hh
|
||||
src/emc/motion/state_tag.h
|
||||
src/emc/motion/emcmotcfg.h
|
||||
src/emc/motion/simple_tp.h
|
||||
src/emc/motion/motion.h
|
||||
src/emc/motion/mot_priv.h
|
||||
src/emc/motion/axis.h
|
||||
src/emc/kinematics/kinematics.h
|
||||
src/emc/kinematics/cubic.h
|
||||
src/emc/tp/tp.h
|
||||
src/emc/tp/tp_types.h
|
||||
src/emc/tp/tc.h
|
||||
src/emc/tp/tc_types.h
|
||||
src/emc/tp/tcq.h
|
||||
src/emc/tp/spherical_arc.h
|
||||
src/emc/tp/blendmath.h
|
||||
src/emc/tp/sp_scurve.h
|
||||
src/emc/tp/ruckig_wrapper.h
|
||||
src/emc/tp/tp_debug.h
|
||||
src/emc/tp/tp.c
|
||||
src/emc/tp/tc.c
|
||||
src/emc/tp/tcq.c
|
||||
src/emc/tp/spherical_arc.c
|
||||
src/emc/tp/blendmath.c
|
||||
src/emc/tp/sp_scurve.c
|
||||
src/emc/tp/ruckig_wrapper.c
|
||||
src/emc/tp/cruckig/block.h
|
||||
src/emc/tp/cruckig/brake.h
|
||||
src/emc/tp/cruckig/calculator.h
|
||||
src/emc/tp/cruckig/cruckig.h
|
||||
src/emc/tp/cruckig/cruckig_internal.h
|
||||
src/emc/tp/cruckig/input_parameter.h
|
||||
src/emc/tp/cruckig/output_parameter.h
|
||||
src/emc/tp/cruckig/position.h
|
||||
src/emc/tp/cruckig/profile.h
|
||||
src/emc/tp/cruckig/result.h
|
||||
src/emc/tp/cruckig/roots.h
|
||||
src/emc/tp/cruckig/trajectory.h
|
||||
src/emc/tp/cruckig/utils.h
|
||||
src/emc/tp/cruckig/velocity.h
|
||||
src/emc/tp/cruckig/block.c
|
||||
src/emc/tp/cruckig/brake.c
|
||||
src/emc/tp/cruckig/calculator.c
|
||||
src/emc/tp/cruckig/cruckig.c
|
||||
src/emc/tp/cruckig/input_parameter.c
|
||||
src/emc/tp/cruckig/output_parameter.c
|
||||
src/emc/tp/cruckig/profile.c
|
||||
src/emc/tp/cruckig/roots.c
|
||||
src/emc/tp/cruckig/trajectory.c
|
||||
src/emc/tp/cruckig/position_first_step1.c
|
||||
src/emc/tp/cruckig/position_first_step2.c
|
||||
src/emc/tp/cruckig/position_second_step1.c
|
||||
src/emc/tp/cruckig/position_second_step2.c
|
||||
src/emc/tp/cruckig/position_third_step1.c
|
||||
src/emc/tp/cruckig/position_third_step2.c
|
||||
src/emc/tp/cruckig/velocity_second_step1.c
|
||||
src/emc/tp/cruckig/velocity_second_step2.c
|
||||
src/emc/tp/cruckig/velocity_third_step1.c
|
||||
src/emc/tp/cruckig/velocity_third_step2.c
|
||||
src/emc/rs274ngc/modal_state.hh
|
||||
src/emc/rs274ngc/modal_state.cc
|
||||
src/libnml/posemath/posemath.h
|
||||
src/libnml/posemath/posemath.cc
|
||||
src/libnml/posemath/_posemath.c
|
||||
src/libnml/posemath/gomath.c
|
||||
src/libnml/posemath/gomath.h
|
||||
src/libnml/posemath/gotypes.h
|
||||
src/libnml/posemath/sincos.c
|
||||
src/libnml/posemath/sincos.h
|
||||
src/emc/rs274ngc/interp_parameter_def.hh
|
||||
src/emc/rs274ngc/interp_array.cc
|
||||
|
||||
62
wasm-port/vendor/linuxcnc/src/emc/kinematics/cubic.h
vendored
Normal file
62
wasm-port/vendor/linuxcnc/src/emc/kinematics/cubic.h
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
/********************************************************************
|
||||
* Description: cubic.h
|
||||
* Cubic polynomial interpolation code
|
||||
*
|
||||
* Derived from a work by Fred Proctor & Will Shackleford
|
||||
*
|
||||
* Author:
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
*
|
||||
* Copyright (c) 2004 All rights reserved.
|
||||
********************************************************************/
|
||||
#ifndef CUBIC_H
|
||||
#define CUBIC_H
|
||||
|
||||
/*
|
||||
Coefficients of a cubic polynomial,
|
||||
|
||||
a * x^3 + b * x^2 + c * x + d
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
double a;
|
||||
double b;
|
||||
double c;
|
||||
double d;
|
||||
} CUBIC_COEFF;
|
||||
|
||||
typedef struct {
|
||||
int configured;
|
||||
double segmentTime;
|
||||
int interpolationRate;
|
||||
double interpolationTime;
|
||||
double interpolationIncrement;
|
||||
double x0, x1, x2, x3;
|
||||
double wp0, wp1;
|
||||
double velp0, velp1;
|
||||
int filled;
|
||||
int needNextPoint;
|
||||
CUBIC_COEFF coeff;
|
||||
} CUBIC_STRUCT;
|
||||
|
||||
extern int cubicInit(CUBIC_STRUCT * ci);
|
||||
extern int cubicSetSegmentTime(CUBIC_STRUCT * ci, double time);
|
||||
extern double cubicGetSegmentTime(CUBIC_STRUCT * ci);
|
||||
extern int cubicSetInterpolationRate(CUBIC_STRUCT * ci, int rate);
|
||||
extern int cubicGetInterpolationRate(CUBIC_STRUCT * ci);
|
||||
extern int cubicAddPoint(CUBIC_STRUCT * ci, double point);
|
||||
extern int cubicOffset(CUBIC_STRUCT * ci, double offset);
|
||||
extern double cubicGetInterpolationIncrement(CUBIC_STRUCT * ci);
|
||||
extern CUBIC_COEFF cubicGetCubicCoeff(CUBIC_STRUCT * ci);
|
||||
extern int cubicFilled(CUBIC_STRUCT * ci);
|
||||
extern double cubicInterpolate(CUBIC_STRUCT * ci, double *x, /* same as
|
||||
return val
|
||||
*/
|
||||
double *v, /* velocity */
|
||||
double *a, /* accel */
|
||||
double *j); /* jerk */
|
||||
extern int cubicNeedNextPoint(CUBIC_STRUCT * ci);
|
||||
extern int cubicDrain(CUBIC_STRUCT * ci);
|
||||
|
||||
#endif /* CUBIC_H */
|
||||
215
wasm-port/vendor/linuxcnc/src/emc/kinematics/kinematics.h
vendored
Normal file
215
wasm-port/vendor/linuxcnc/src/emc/kinematics/kinematics.h
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
/********************************************************************
|
||||
* Description: kinematics.h
|
||||
*
|
||||
* Derived from a work by Fred Proctor & Will Shackleford
|
||||
*
|
||||
* Author:
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
*
|
||||
* Copyright (c) 2004 All rights reserved.
|
||||
*
|
||||
* Last change:
|
||||
********************************************************************/
|
||||
|
||||
#ifndef __LINUXCNC_KINEMATICS_H
|
||||
#define __LINUXCNC_KINEMATICS_H
|
||||
|
||||
#include "emcpos.h" /* EmcPose */
|
||||
#include "rtapi_bool.h"
|
||||
|
||||
/*
|
||||
The type of kinematics used.
|
||||
|
||||
KINEMATICS_IDENTITY means that the joints and world coordinates are the
|
||||
same, as for slideway machines (XYZ milling machines). The EMC will allow
|
||||
changing from joint to world mode and vice versa. Also, the EMC will set
|
||||
the actual world position to be the actual joint positions (not commanded)
|
||||
by calling the forward kinematics each trajectory cycle.
|
||||
|
||||
KINEMATICS_FORWARD_ONLY means that only the forward kinematics exist.
|
||||
Since the EMC requires at least the inverse kinematics, this should simply
|
||||
terminate the EMC.
|
||||
|
||||
KINEMATICS_INVERSE_ONLY means that only the inverse kinematics exist.
|
||||
The forwards won't be called, and the EMC will only allow changing from
|
||||
joint to world mode at the home position.
|
||||
|
||||
KINEMATICS_BOTH means that both the forward and inverse kins are defined.
|
||||
Like KINEMATICS_IDENTITY, the EMC will allow changing between world and
|
||||
joint modes. However, the kins are assumed to be somewhat expensive
|
||||
computationally, and the forwards won't be called at the trajectory rate
|
||||
to compute actual world coordinates from actual joint values.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
KINEMATICS_IDENTITY = 1,/* forward=inverse, both well-behaved */
|
||||
KINEMATICS_FORWARD_ONLY,/* forward but no inverse */
|
||||
KINEMATICS_INVERSE_ONLY,/* inverse but no forward */
|
||||
KINEMATICS_BOTH /* forward and inverse both */
|
||||
} KINEMATICS_TYPE;
|
||||
|
||||
/* the forward flags are passed to the forward kinematics so that they
|
||||
can resolve ambiguities in the world coordinates for a given joint set,
|
||||
e.g., for hexpods, this would be platform-below-base, platform-above-base.
|
||||
|
||||
The flags are also passed to the inverse kinematics and are set by them,
|
||||
which is how they are changed from their initial value. For example, for
|
||||
hexapods you could do a coordinated move that brings the platform up from
|
||||
below the base to above the base. The forward flags would be set to
|
||||
indicate this. */
|
||||
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
|
||||
|
||||
/* the inverse flags are passed to the inverse kinematics so that they
|
||||
can resolve ambiguities in the joint angles for a given world coordinate,
|
||||
e.g., for robots, this would be elbow-up, elbow-down, etc.
|
||||
|
||||
The flags are also passed to the forward kinematics and are set by them,
|
||||
which is how they are changed from their initial value. For example, for
|
||||
robots you could do a joint move that brings the elbow from a down
|
||||
configuration to an up configuration. The inverse flags would be set to
|
||||
indicate this. */
|
||||
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
|
||||
|
||||
/* the forward kinematics take joint values and determine world coordinates,
|
||||
given forward kinematics flags to resolve any ambiguities. The inverse
|
||||
flags are set to indicate their value appropriate to the joint values
|
||||
passed in. */
|
||||
extern int kinematicsForward(const double *joint,
|
||||
struct EmcPose * world,
|
||||
const KINEMATICS_FORWARD_FLAGS * fflags,
|
||||
KINEMATICS_INVERSE_FLAGS * iflags);
|
||||
|
||||
/* the inverse kinematics take world coordinates and determine joint values,
|
||||
given the inverse kinematics flags to resolve any ambiguities. The forward
|
||||
flags are set to indicate their value appropriate to the world coordinates
|
||||
passed in. */
|
||||
extern int kinematicsInverse(const struct EmcPose * world,
|
||||
double *joint,
|
||||
const KINEMATICS_INVERSE_FLAGS * iflags,
|
||||
KINEMATICS_FORWARD_FLAGS * fflags);
|
||||
|
||||
/* the home kinematics function sets all its arguments to their proper
|
||||
values at the known home position. When called, these should be set,
|
||||
when known, to initial values, e.g., from an INI file. If the home
|
||||
kinematics can accept arbitrary starting points, these initial values
|
||||
should be used.
|
||||
*/
|
||||
extern int kinematicsHome(struct EmcPose * world,
|
||||
double *joint,
|
||||
KINEMATICS_FORWARD_FLAGS * fflags,
|
||||
KINEMATICS_INVERSE_FLAGS * iflags);
|
||||
|
||||
extern KINEMATICS_TYPE kinematicsType(void);
|
||||
|
||||
/* parameters for use with switchkins.c */
|
||||
typedef struct kinematics_parms {
|
||||
char* sparm; // module string parameter passed to kins
|
||||
char* kinsname; // must agree with module(file) name
|
||||
char* halprefix; // for hal pin hames
|
||||
char* required_coordinates;
|
||||
int max_joints;
|
||||
int allow_duplicates;
|
||||
int fwd_iterates_mask; // identify kins types that use iterative
|
||||
// forward kinematics (typ: genhex)
|
||||
// bitmask: 0x0 none
|
||||
// bitmask: 0x1 bit0: switchkins_type==0
|
||||
// bitmask: 0x2 bit1: switchkins_type==1
|
||||
// bitmask: 0x4 bit2: switchkins_type==2
|
||||
int gui_kinstype; // may be reqd for parallel kins with vismach
|
||||
// to select switchkins_type for gui pins
|
||||
} kparms;
|
||||
|
||||
/* map letters in a coordinates string to joint numbers
|
||||
** sequentially. Axis indices are 0:x,1:y,...,etc
|
||||
** Example: coordinates=XYZYAC
|
||||
** Result: axis_idx_for_jno[0] = 0 ==> X
|
||||
** axis_idx_for_jno[1] = 1 ==> Y
|
||||
** axis_idx_for_jno[2] = 2 ==> Z
|
||||
** axis_idx_for_jno[3] = 1 ==> Y (duplicate allowed)
|
||||
** axis_idx_for_jno[4] = 1 ==> A
|
||||
** axis_idx_for_jno[5] = 1 ==> C
|
||||
*/
|
||||
extern int map_coordinates_to_jnumbers(const char *coordinates,
|
||||
const int max_joints,
|
||||
const int allow_duplicates,
|
||||
int axis_idx_for_jno[]);
|
||||
|
||||
extern int mapped_joints_to_position(const int max_joints,
|
||||
const double* joints,
|
||||
EmcPose* pose);
|
||||
|
||||
extern int position_to_mapped_joints(const int max_joints,
|
||||
const EmcPose* pos,
|
||||
double* joints);
|
||||
|
||||
extern int identityKinematicsSetup(const int comp_id,
|
||||
const char* coordinates,
|
||||
kparms* ksetup_parms);
|
||||
|
||||
extern int identityKinematicsForward(const double *joint,
|
||||
struct EmcPose * world,
|
||||
const KINEMATICS_FORWARD_FLAGS * fflags,
|
||||
KINEMATICS_INVERSE_FLAGS * iflags);
|
||||
|
||||
extern int identityKinematicsInverse(const struct EmcPose * world,
|
||||
double *joint,
|
||||
const KINEMATICS_INVERSE_FLAGS * iflags,
|
||||
KINEMATICS_FORWARD_FLAGS * fflags);
|
||||
|
||||
extern int kinematicsSwitchable(void);
|
||||
extern int kinematicsSwitch(int switchkins_type);
|
||||
//NOTE: switchable kinematics may require Interp::Synch
|
||||
// before/after invoking kinematicsSwitch()
|
||||
// A convenient command to synch is: M66 E0 L0
|
||||
|
||||
#define KINS_NOT_SWITCHABLE \
|
||||
extern int kinematicsSwitchable() {return 0;} \
|
||||
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0;} \
|
||||
EXPORT_SYMBOL(kinematicsSwitchable); \
|
||||
EXPORT_SYMBOL(kinematicsSwitch);
|
||||
|
||||
|
||||
// support for template for user-defined switchkins_type==2
|
||||
extern int userkKinematicsSetup(const int comp_id,
|
||||
const char* coordinates,
|
||||
kparms* ksetup_parms);
|
||||
|
||||
extern int userkKinematicsForward(const double *joint,
|
||||
struct EmcPose * world,
|
||||
const KINEMATICS_FORWARD_FLAGS * fflags,
|
||||
KINEMATICS_INVERSE_FLAGS * iflags);
|
||||
|
||||
extern int userkKinematicsInverse(const struct EmcPose * world,
|
||||
double *joint,
|
||||
const KINEMATICS_INVERSE_FLAGS * iflags,
|
||||
KINEMATICS_FORWARD_FLAGS * fflags);
|
||||
#endif
|
||||
//*********************************************************************
|
||||
// xyzac,xyzbc;
|
||||
extern int trtKinematicsSetup(const int comp_id,
|
||||
const char* coordinates,
|
||||
kparms* ksetup_parms);
|
||||
|
||||
extern int xyzacKinematicsForward(const double *joints,
|
||||
EmcPose * pos,
|
||||
const KINEMATICS_FORWARD_FLAGS * fflags,
|
||||
KINEMATICS_INVERSE_FLAGS * iflags);
|
||||
|
||||
extern int xyzacKinematicsInverse(const EmcPose * pos,
|
||||
double *joints,
|
||||
const KINEMATICS_INVERSE_FLAGS * iflags,
|
||||
KINEMATICS_FORWARD_FLAGS * fflags);
|
||||
|
||||
|
||||
extern int xyzbcKinematicsForward(const double *joints,
|
||||
EmcPose * pos,
|
||||
const KINEMATICS_FORWARD_FLAGS * fflags,
|
||||
KINEMATICS_INVERSE_FLAGS * iflags);
|
||||
|
||||
extern int xyzbcKinematicsInverse(const EmcPose * pos,
|
||||
double *joints,
|
||||
const KINEMATICS_INVERSE_FLAGS * iflags,
|
||||
KINEMATICS_FORWARD_FLAGS * fflags);
|
||||
|
||||
//*********************************************************************
|
||||
60
wasm-port/vendor/linuxcnc/src/emc/motion/axis.h
vendored
Normal file
60
wasm-port/vendor/linuxcnc/src/emc/motion/axis.h
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
#ifndef AXIS_H
|
||||
#define AXIS_H
|
||||
|
||||
#include <rtapi_bool.h>
|
||||
#include <hal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void axis_init_all(void);
|
||||
void axis_initialize_external_offsets(void);
|
||||
int axis_init_hal_io(int mot_comp_id);
|
||||
|
||||
void axis_handle_jogwheels(bool motion_teleop_flag, bool motion_enable_flag, bool homing_is_active);
|
||||
bool axis_plan_external_offsets(double servo_period, bool motion_enable_flag, bool all_homed);
|
||||
void axis_check_constraints(double pos[], int failing_axes[]);
|
||||
|
||||
void axis_jog_cont(int axis_num, double vel, long servo_period);
|
||||
void axis_jog_incr(int axis_num, double offset, double vel, long servo_period);
|
||||
void axis_jog_abs(int axis_num, double offset, double vel);
|
||||
bool axis_jog_abort_all(bool immediate);
|
||||
bool axis_jog_abort(int axis_num, bool immediate);
|
||||
bool axis_jog_is_active(void);
|
||||
|
||||
void axis_output_to_hal(double *pcmd_p[]);
|
||||
|
||||
void axis_set_max_pos_limit(int axis_num, double maxLimit);
|
||||
void axis_set_min_pos_limit(int axis_num, double minLimit);
|
||||
void axis_set_vel_limit(int axis_num, double vel);
|
||||
void axis_set_acc_limit(int axis_num, double acc);
|
||||
void axis_set_jerk_limit(int axis_num, double jerk);
|
||||
void axis_set_ext_offset_vel_limit(int axis_num, double ext_offset_vel);
|
||||
void axis_set_ext_offset_acc_limit(int axis_num, double ext_offset_acc);
|
||||
void axis_set_locking_joint(int axis_num, int joint);
|
||||
|
||||
double axis_get_min_pos_limit(int axis_num);
|
||||
double axis_get_max_pos_limit(int axis_num);
|
||||
double axis_get_vel_limit(int axis_num);
|
||||
double axis_get_acc_limit(int axis_num);
|
||||
int axis_get_locking_joint(int axis_num);
|
||||
double axis_get_compound_velocity(void);
|
||||
double axis_get_ext_offset_curr_pos(int axis_num);
|
||||
|
||||
double axis_get_teleop_vel_cmd(int axis_num);
|
||||
|
||||
void axis_sync_teleop_tp_to_carte_pos(int extfactor, double *pcmd_p[]);
|
||||
void axis_sync_carte_pos_to_teleop_tp(int extfactor, double *pcmd_p[]);
|
||||
void axis_apply_ext_offsets_to_carte_pos(int extfactor, double *pcmd_p[]);
|
||||
|
||||
int axis_update_coord_with_bound(double *pcmd_p[], double servo_period);
|
||||
|
||||
int axis_calc_motion(double servo_period);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* AXIS_H */
|
||||
355
wasm-port/vendor/linuxcnc/src/emc/motion/mot_priv.h
vendored
Normal file
355
wasm-port/vendor/linuxcnc/src/emc/motion/mot_priv.h
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
/*******************************************************************
|
||||
* Description: mot_priv.h
|
||||
* Macros and declarations local to the realtime sources.
|
||||
*
|
||||
* Author:
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
*
|
||||
* Copyright (c) 2004 All rights reserved.
|
||||
********************************************************************/
|
||||
#ifndef MOT_PRIV_H
|
||||
#define MOT_PRIV_H
|
||||
|
||||
/***********************************************************************
|
||||
* TYPEDEFS, ENUMS, ETC. *
|
||||
************************************************************************/
|
||||
|
||||
/* First we define structures for data shared with the HAL */
|
||||
|
||||
/* HAL visible data notations:
|
||||
RPA: read only parameter
|
||||
WPA: write only parameter
|
||||
WRPA: read/write parameter
|
||||
RPI: read only pin
|
||||
WPI: write only pin
|
||||
WRPI: read/write pin
|
||||
*/
|
||||
|
||||
/* joint data */
|
||||
#include <hal.h>
|
||||
#include "../motion/motion.h"
|
||||
|
||||
typedef struct {
|
||||
// creating a lot of pins for spindle control to be very flexible
|
||||
// the user needs only a subset of these
|
||||
|
||||
// simplest way of spindle control (output start/stop)
|
||||
hal_bit_t *spindle_on; /* spindle spin output */
|
||||
|
||||
// same thing for 2 directions
|
||||
hal_bit_t *spindle_forward; /* spindle spin-forward output */
|
||||
hal_bit_t *spindle_reverse; /* spindle spin-reverse output */
|
||||
|
||||
// simple velocity control (as long as the output is active the spindle
|
||||
// should accelerate/decelerate
|
||||
hal_bit_t *spindle_incr_speed; /* spindle spin-increase output */
|
||||
hal_bit_t *spindle_decr_speed; /* spindle spin-decrease output */
|
||||
|
||||
// simple output for brake
|
||||
hal_bit_t *spindle_brake; /* spindle brake output */
|
||||
|
||||
// output of a prescribed speed (to hook-up to a velocity controller)
|
||||
hal_float_t *spindle_speed_out; /* spindle speed output */
|
||||
hal_float_t *spindle_speed_out_rps; /* spindle speed output */
|
||||
hal_float_t *spindle_speed_out_abs; /* spindle speed output absolute*/
|
||||
hal_float_t *spindle_speed_out_rps_abs; /* spindle speed output absolute*/
|
||||
hal_float_t *spindle_speed_cmd_rps; /* spindle speed command without SO applied */
|
||||
hal_float_t *spindle_speed_in; /* spindle speed measured */
|
||||
hal_bit_t *spindle_index_enable; /* spindle inde I/O pin */
|
||||
hal_bit_t *spindle_inhibit;
|
||||
hal_float_t *spindle_revs;
|
||||
hal_bit_t *spindle_is_atspeed;
|
||||
hal_bit_t *spindle_amp_fault;
|
||||
|
||||
// spindle orient
|
||||
hal_float_t *spindle_orient_angle; /* out: desired spindle angle, degrees */
|
||||
hal_s32_t *spindle_orient_mode; /* out: 0: least travel; 1: cw; 2: ccw */
|
||||
hal_bit_t *spindle_orient; /* out: signal orient in progress */
|
||||
hal_bit_t *spindle_locked; /* out: signal orient complete, spindle locked */
|
||||
hal_bit_t *spindle_is_oriented; /* in: orientation completed */
|
||||
hal_s32_t *spindle_orient_fault; /* in: error code of failed operation */
|
||||
|
||||
} spindle_hal_t;
|
||||
|
||||
typedef struct {
|
||||
hal_float_t *coarse_pos_cmd;/* RPI: commanded position, w/o comp */
|
||||
hal_float_t *joint_vel_cmd; /* RPI: commanded velocity, w/o comp */
|
||||
hal_float_t *joint_acc_cmd; /* RPI: commanded acceleration, w/o comp */
|
||||
hal_float_t *joint_jerk_cmd;/* RPI: commanded jerk, w/o comp */
|
||||
hal_float_t *backlash_corr; /* RPI: correction for backlash */
|
||||
hal_float_t *backlash_filt; /* RPI: filtered backlash correction */
|
||||
hal_float_t *backlash_vel; /* RPI: backlash speed variable */
|
||||
hal_float_t *motor_offset; /* RPI: motor offset, for checking homing stability */
|
||||
hal_float_t *motor_pos_cmd; /* WPI: commanded position, with comp */
|
||||
hal_float_t *motor_pos_fb; /* RPI: position feedback, with comp */
|
||||
hal_float_t *joint_pos_cmd; /* WPI: commanded position w/o comp, not ofs */
|
||||
hal_float_t *joint_pos_fb; /* RPI: position feedback, w/o comp */
|
||||
hal_float_t *f_error; /* RPI: following error */
|
||||
hal_float_t *f_error_lim; /* RPI: following error limit */
|
||||
|
||||
hal_float_t *free_pos_cmd; /* RPI: free traj planner pos cmd */
|
||||
hal_float_t *free_vel_lim; /* RPI: free traj planner vel limit */
|
||||
hal_bit_t *free_tp_enable; /* RPI: free traj planner is running */
|
||||
hal_bit_t *kb_jjog_active; /* RPI: executing keyboard jog */
|
||||
hal_bit_t *wheel_jjog_active;/* RPI: executing handwheel jog */
|
||||
|
||||
hal_bit_t *active; /* RPI: joint is active, whatever that means */
|
||||
hal_bit_t *in_position; /* RPI: joint is in position */
|
||||
hal_bit_t *error; /* RPI: joint has an error */
|
||||
hal_bit_t *phl; /* RPI: joint is at positive hard limit */
|
||||
hal_bit_t *nhl; /* RPI: joint is at negative hard limit */
|
||||
hal_bit_t *f_errored; /* RPI: joint had too much following error */
|
||||
hal_bit_t *faulted; /* RPI: joint amp faulted */
|
||||
hal_bit_t *pos_lim_sw; /* RPI: positive limit switch input */
|
||||
hal_bit_t *neg_lim_sw; /* RPI: negative limit switch input */
|
||||
hal_bit_t *amp_fault; /* RPI: amp fault input */
|
||||
hal_bit_t *amp_enable; /* WPI: amp enable output */
|
||||
|
||||
hal_bit_t *unlock; /* WPI: command that axis should unlock for rotation */
|
||||
hal_bit_t *is_unlocked; /* RPI: axis is currently unlocked */
|
||||
|
||||
hal_s32_t *jjog_counts; /* WPI: jogwheel position input */
|
||||
hal_bit_t *jjog_enable; /* RPI: enable jogwheel */
|
||||
hal_float_t *jjog_scale; /* RPI: distance to jog on each count */
|
||||
hal_float_t *jjog_accel_fraction; /* RPI: to limit wheel jog accel */
|
||||
hal_bit_t *jjog_vel_mode; /* RPI: true for "velocity mode" jogwheel */
|
||||
} joint_hal_t;
|
||||
|
||||
typedef struct {
|
||||
hal_float_t *posthome_cmd; // IN pin extrajoint
|
||||
} extrajoint_hal_t;
|
||||
|
||||
/* machine data */
|
||||
|
||||
typedef struct {
|
||||
hal_bit_t *probe_input; /* RPI: probe switch input */
|
||||
hal_bit_t *enable; /* RPI: motion inhibit input */
|
||||
hal_float_t *adaptive_feed; /* RPI: adaptive feedrate, 0.0 to 1.0 */
|
||||
hal_bit_t *feed_hold; /* RPI: set TRUE to stop motion maskable with g53 P1*/
|
||||
hal_bit_t *feed_inhibit; /* RPI: set TRUE to stop motion (non maskable)*/
|
||||
hal_bit_t *homing_inhibit; /* RPI: set TRUE to inhibit homing*/
|
||||
hal_bit_t *jog_inhibit; /* RPI: set TRUE to inhibit jogging*/
|
||||
hal_bit_t *jog_stop; /* RPI: set TRUE to stop jogging following accel values*/
|
||||
hal_bit_t *jog_stop_immediate; /* RPI: set TRUE to stop jogging immediately*/
|
||||
hal_bit_t *jog_is_active; /* RPI: TRUE if active jogging*/
|
||||
hal_bit_t *tp_reverse; /* Set true if trajectory planner is running in reverse*/
|
||||
hal_bit_t *motion_enabled; /* RPI: motion enable for all joints */
|
||||
hal_bit_t *is_all_homed; /* RPI: TRUE if all active joints is homed */
|
||||
hal_bit_t *in_position; /* RPI: all joints are in position */
|
||||
hal_bit_t *coord_mode; /* RPA: TRUE if coord, FALSE if free */
|
||||
hal_bit_t *teleop_mode; /* RPA: TRUE if teleop mode */
|
||||
hal_bit_t *coord_error; /* RPA: TRUE if coord mode error */
|
||||
hal_bit_t *on_soft_limit; /* RPA: TRUE if outside a limit */
|
||||
|
||||
hal_s32_t *program_line; /* RPA: program line causing current motion */
|
||||
hal_s32_t *motion_type; /* RPA: type (feed/rapid) of currently commanded motion */
|
||||
hal_float_t *current_vel; /* RPI: velocity magnitude in machine units */
|
||||
hal_float_t *requested_vel; /* RPI: requested velocity magnitude in machine units */
|
||||
hal_float_t *distance_to_go;/* RPI: distance to go in current move*/
|
||||
|
||||
hal_bit_t debug_bit_0; /* RPA: generic param, for debugging */
|
||||
hal_bit_t debug_bit_1; /* RPA: generic param, for debugging */
|
||||
hal_float_t debug_float_0; /* RPA: generic param, for debugging */
|
||||
hal_float_t debug_float_1; /* RPA: generic param, for debugging */
|
||||
hal_float_t debug_float_2; /* RPA: generic param, for debugging */
|
||||
hal_float_t debug_float_3; /* RPA: generic param, for debugging */
|
||||
hal_s32_t debug_s32_0; /* RPA: generic param, for debugging */
|
||||
hal_s32_t debug_s32_1; /* RPA: generic param, for debugging */
|
||||
|
||||
hal_bit_t *synch_do[EMCMOT_MAX_DIO]; /* WPI array: output pins for motion synched IO */
|
||||
hal_bit_t *synch_di[EMCMOT_MAX_DIO]; /* RPI array: input pins for motion synched IO */
|
||||
hal_float_t *analog_input[EMCMOT_MAX_AIO]; /* RPI array: input pins for analog Inputs */
|
||||
hal_float_t *analog_output[EMCMOT_MAX_AIO]; /* RPI array: output pins for analog Inputs */
|
||||
hal_bit_t *misc_error[EMCMOT_MAX_MISC_ERROR]; /* RPI array: output pins for misc error Inputs */
|
||||
|
||||
// FIXME - debug only, remove later
|
||||
hal_float_t traj_pos_out; /* RPA: traj internals, for debugging */
|
||||
hal_float_t traj_vel_out; /* RPA: traj internals, for debugging */
|
||||
hal_u32_t traj_active_tc; /* RPA: traj internals, for debugging */
|
||||
hal_float_t tc_pos[4]; /* RPA: traj internals, for debugging */
|
||||
hal_float_t tc_vel[4]; /* RPA: traj internals, for debugging */
|
||||
hal_float_t tc_acc[4]; /* RPA: traj internals, for debugging */
|
||||
|
||||
// realtime overrun detection
|
||||
hal_u32_t *last_period; /* pin: last period in clocks */
|
||||
hal_float_t *last_period_ns; /* pin: last period in nanoseconds */
|
||||
|
||||
hal_float_t *tooloffset_x;
|
||||
hal_float_t *tooloffset_y;
|
||||
hal_float_t *tooloffset_z;
|
||||
hal_float_t *tooloffset_a;
|
||||
hal_float_t *tooloffset_b;
|
||||
hal_float_t *tooloffset_c;
|
||||
hal_float_t *tooloffset_u;
|
||||
hal_float_t *tooloffset_v;
|
||||
hal_float_t *tooloffset_w;
|
||||
|
||||
spindle_hal_t spindle[EMCMOT_MAX_SPINDLES]; /*spindle data */
|
||||
joint_hal_t joint[EMCMOT_MAX_JOINTS]; /* data for each joint */
|
||||
extrajoint_hal_t ejoint[EMCMOT_MAX_EXTRAJOINTS]; /* data for each extrajoint */
|
||||
|
||||
hal_bit_t *eoffset_active; /* ext offsets active */
|
||||
hal_bit_t *eoffset_limited; /* ext offsets exceed limit */
|
||||
|
||||
hal_float_t *feed_upm; /* feed G-code units per minute*/
|
||||
hal_float_t *feed_inches_per_minute; /* feed inches per minute*/
|
||||
hal_float_t *feed_inches_per_second; /* feed inches per second*/
|
||||
hal_float_t *feed_mm_per_minute; /* feed mm per minute*/
|
||||
hal_float_t *feed_mm_per_second; /* feed mm per second*/
|
||||
|
||||
hal_float_t *switchkins_type;
|
||||
/* Interp State Pins */
|
||||
hal_s32_t *interp_line_number;
|
||||
hal_s32_t *interp_motion_type;
|
||||
hal_float_t *interp_feedrate;
|
||||
|
||||
/* New Geometric Metadata Pins */
|
||||
hal_float_t *interp_arc_radius;
|
||||
hal_float_t *interp_arc_center_x;
|
||||
hal_float_t *interp_arc_center_y;
|
||||
hal_float_t *interp_arc_center_z;
|
||||
hal_float_t *interp_straight_heading;
|
||||
hal_float_t *interp_normal_heading;
|
||||
hal_bit_t *iscircle;
|
||||
} emcmot_hal_data_t;
|
||||
|
||||
/***********************************************************************
|
||||
* GLOBAL VARIABLE DECLARATIONS *
|
||||
************************************************************************/
|
||||
|
||||
/* pointer to emcmot_hal_data_t struct in HAL shmem, with all HAL data */
|
||||
extern emcmot_hal_data_t *emcmot_hal_data;
|
||||
|
||||
/* pointer to array of joint structs with all joint data */
|
||||
/* the actual array may be in shared memory or in kernel space, as
|
||||
determined by the init code in motion.c */
|
||||
extern emcmot_joint_t joints[EMCMOT_MAX_JOINTS];
|
||||
|
||||
/* Variable defs */
|
||||
extern KINEMATICS_FORWARD_FLAGS fflags;
|
||||
extern KINEMATICS_INVERSE_FLAGS iflags;
|
||||
/* these variable have the 1/servo cycle time */
|
||||
|
||||
/* Struct pointers */
|
||||
extern struct emcmot_struct_t *emcmotStruct;
|
||||
extern struct emcmot_command_t *emcmotCommand;
|
||||
extern struct emcmot_status_t *emcmotStatus;
|
||||
extern struct emcmot_config_t *emcmotConfig;
|
||||
extern struct emcmot_internal_t *emcmotInternal;
|
||||
extern struct emcmot_error_t *emcmotError;
|
||||
|
||||
/***********************************************************************
|
||||
* PUBLIC FUNCTION PROTOTYPES *
|
||||
************************************************************************/
|
||||
|
||||
/* function definitions */
|
||||
extern void emcmotCommandHandler(void *arg, long period);
|
||||
extern void emcmotController(void *arg, long period);
|
||||
extern void emcmotSetCycleTime(unsigned long nsec);
|
||||
|
||||
/* these are related to synchronized I/O */
|
||||
extern void emcmotDioWrite(int index, char value);
|
||||
extern void emcmotAioWrite(int index, double value);
|
||||
|
||||
extern void emcmotSetRotaryUnlock(int axis, int unlock);
|
||||
extern int emcmotGetRotaryIsUnlocked(int axis);
|
||||
|
||||
//
|
||||
// Try to change the Motion mode to Teleop.
|
||||
//
|
||||
// This function can be called at any time. Returns without changing
|
||||
// mode if Teleop is not currently allowed. This code doesn't actually
|
||||
// make the transition, it just sets a flag requesting the transition.
|
||||
// The real transition to Teleop mode is done in emcmotController().
|
||||
//
|
||||
void switch_to_teleop_mode(void);
|
||||
|
||||
/* recalculates jog limits */
|
||||
extern void refresh_jog_limits(emcmot_joint_t *joint,int joint_num);
|
||||
/* handles 'homed' flags, see command.c for details */
|
||||
extern void clearHomes(int joint_num);
|
||||
|
||||
extern void emcmot_config_change(void);
|
||||
extern void reportError(const char *fmt, ...) __attribute__((format(printf,1,2))); /* Use the rtapi_print call */
|
||||
|
||||
|
||||
int joint_is_lockable(int joint_num);
|
||||
|
||||
#define ALL_JOINTS emcmotConfig->numJoints
|
||||
// number of kinematics-only joints:
|
||||
#define NO_OF_KINS_JOINTS (ALL_JOINTS - emcmotConfig->numExtraJoints)
|
||||
#define IS_EXTRA_JOINT(jno) (jno >= NO_OF_KINS_JOINTS)
|
||||
// 0-based Joint numbering:
|
||||
// kinematic-only jno.s: [0 ... (NO_OF_KINS_JOINTS -1) ]
|
||||
// extrajoint jno.s: [NO_OF_KINS_JOINTS ... (ALL_JOINTS -1) ]
|
||||
|
||||
/* rtapi_get_time() returns a nanosecond value. In time, we should use a u64
|
||||
value for all calcs and only do the conversion to seconds when it is
|
||||
really needed. */
|
||||
#define etime() (((double) rtapi_get_time()) / 1.0e9)
|
||||
|
||||
/* macros for reading, writing bit flags */
|
||||
|
||||
/* motion flags */
|
||||
|
||||
#define GET_MOTION_ERROR_FLAG() (emcmotStatus->motionFlag & EMCMOT_MOTION_ERROR_BIT ? 1 : 0)
|
||||
|
||||
#define SET_MOTION_ERROR_FLAG(fl) if (fl) emcmotStatus->motionFlag |= EMCMOT_MOTION_ERROR_BIT; else emcmotStatus->motionFlag &= ~EMCMOT_MOTION_ERROR_BIT;
|
||||
|
||||
#define GET_MOTION_COORD_FLAG() (emcmotStatus->motionFlag & EMCMOT_MOTION_COORD_BIT ? 1 : 0)
|
||||
|
||||
#define SET_MOTION_COORD_FLAG(fl) if (fl) emcmotStatus->motionFlag |= EMCMOT_MOTION_COORD_BIT; else emcmotStatus->motionFlag &= ~EMCMOT_MOTION_COORD_BIT;
|
||||
|
||||
#define GET_MOTION_TELEOP_FLAG() (emcmotStatus->motionFlag & EMCMOT_MOTION_TELEOP_BIT ? 1 : 0)
|
||||
|
||||
#define SET_MOTION_TELEOP_FLAG(fl) if (fl) emcmotStatus->motionFlag |= EMCMOT_MOTION_TELEOP_BIT; else emcmotStatus->motionFlag &= ~EMCMOT_MOTION_TELEOP_BIT;
|
||||
|
||||
#define GET_MOTION_INPOS_FLAG() (emcmotStatus->motionFlag & EMCMOT_MOTION_INPOS_BIT ? 1 : 0)
|
||||
|
||||
#define SET_MOTION_INPOS_FLAG(fl) if (fl) emcmotStatus->motionFlag |= EMCMOT_MOTION_INPOS_BIT; else emcmotStatus->motionFlag &= ~EMCMOT_MOTION_INPOS_BIT;
|
||||
|
||||
#define GET_MOTION_ENABLE_FLAG() (emcmotStatus->motionFlag & EMCMOT_MOTION_ENABLE_BIT ? 1 : 0)
|
||||
|
||||
#define SET_MOTION_ENABLE_FLAG(fl) if (fl) emcmotStatus->motionFlag |= EMCMOT_MOTION_ENABLE_BIT; else emcmotStatus->motionFlag &= ~EMCMOT_MOTION_ENABLE_BIT;
|
||||
|
||||
#define GET_TRAJ_PLANNER_TYPE() (emcmotStatus->planner_type)
|
||||
|
||||
#define SET_TRAK_PLANNER_TYPE(tp) (emcmotStatus->planner_type = tp)
|
||||
|
||||
/* joint flags */
|
||||
|
||||
#define GET_JOINT_ENABLE_FLAG(joint) ((joint)->flag & EMCMOT_JOINT_ENABLE_BIT ? 1 : 0)
|
||||
|
||||
#define SET_JOINT_ENABLE_FLAG(joint,fl) if (fl) (joint)->flag |= EMCMOT_JOINT_ENABLE_BIT; else (joint)->flag &= ~EMCMOT_JOINT_ENABLE_BIT;
|
||||
|
||||
#define SET_JOINT_ACTIVE_FLAG(joint,fl) if (fl) (joint)->flag |= EMCMOT_JOINT_ACTIVE_BIT; else (joint)->flag &= ~EMCMOT_JOINT_ACTIVE_BIT;
|
||||
|
||||
#define SET_JOINT_INPOS_FLAG(joint,fl) if (fl) (joint)->flag |= EMCMOT_JOINT_INPOS_BIT; else (joint)->flag &= ~EMCMOT_JOINT_INPOS_BIT;
|
||||
|
||||
#define GET_JOINT_ERROR_FLAG(joint) ((joint)->flag & EMCMOT_JOINT_ERROR_BIT ? 1 : 0)
|
||||
|
||||
#define SET_JOINT_ERROR_FLAG(joint,fl) if (fl) (joint)->flag |= EMCMOT_JOINT_ERROR_BIT; else (joint)->flag &= ~EMCMOT_JOINT_ERROR_BIT;
|
||||
|
||||
#define GET_JOINT_PHL_FLAG(joint) ((joint)->flag & EMCMOT_JOINT_MAX_HARD_LIMIT_BIT ? 1 : 0)
|
||||
|
||||
#define SET_JOINT_PHL_FLAG(joint,fl) if (fl) (joint)->flag |= EMCMOT_JOINT_MAX_HARD_LIMIT_BIT; else (joint)->flag &= ~EMCMOT_JOINT_MAX_HARD_LIMIT_BIT;
|
||||
|
||||
#define GET_JOINT_NHL_FLAG(joint) ((joint)->flag & EMCMOT_JOINT_MIN_HARD_LIMIT_BIT ? 1 : 0)
|
||||
|
||||
#define SET_JOINT_NHL_FLAG(joint,fl) if (fl) (joint)->flag |= EMCMOT_JOINT_MIN_HARD_LIMIT_BIT; else (joint)->flag &= ~EMCMOT_JOINT_MIN_HARD_LIMIT_BIT;
|
||||
|
||||
|
||||
#define GET_JOINT_FERROR_FLAG(joint) ((joint)->flag & EMCMOT_JOINT_FERROR_BIT ? 1 : 0)
|
||||
|
||||
#define SET_JOINT_FERROR_FLAG(joint,fl) if (fl) (joint)->flag |= EMCMOT_JOINT_FERROR_BIT; else (joint)->flag &= ~EMCMOT_JOINT_FERROR_BIT;
|
||||
|
||||
#define GET_JOINT_FAULT_FLAG(joint) ((joint)->flag & EMCMOT_JOINT_FAULT_BIT ? 1 : 0)
|
||||
|
||||
#define SET_JOINT_FAULT_FLAG(joint,fl) if (fl) (joint)->flag |= EMCMOT_JOINT_FAULT_BIT; else (joint)->flag &= ~EMCMOT_JOINT_FAULT_BIT;
|
||||
|
||||
#if defined(__KERNEL__)
|
||||
#define HAVE_CPU_KHZ
|
||||
#endif
|
||||
|
||||
#endif /* MOT_PRIV_H */
|
||||
780
wasm-port/vendor/linuxcnc/src/emc/motion/motion.h
vendored
Normal file
780
wasm-port/vendor/linuxcnc/src/emc/motion/motion.h
vendored
Normal file
@@ -0,0 +1,780 @@
|
||||
/********************************************************************
|
||||
* Description: motion.h
|
||||
* Data structures used throughout emc2.
|
||||
*
|
||||
* Author:
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
*
|
||||
* Copyright (c) 2004 All rights reserved
|
||||
********************************************************************/
|
||||
|
||||
/* jmk says: This file is a mess! */
|
||||
|
||||
/*
|
||||
|
||||
Misc ramblings:
|
||||
|
||||
The terms axis and joint are used inconsistently throughout EMC.
|
||||
For all new code, the usages are as follows:
|
||||
|
||||
axis - one of the nine degrees of freedom, x, y, z, a, b, c, u, v, w
|
||||
these refer to axes in Cartesian space, which may or
|
||||
may not match up with joints (see below). On Cartesian
|
||||
machines they do match up, but for hexapods, robots, and
|
||||
other non-Cartesian machines they don't.
|
||||
joint - one of the physical degrees of freedom of the machine
|
||||
these might be linear (leadscrews) or rotary (rotary
|
||||
tables, robot arm joints). There can be any number of
|
||||
joints. The kinematics code is responsible for translating
|
||||
from axis space to joint space and back.
|
||||
|
||||
There are three main kinds of data needed by the motion controller
|
||||
|
||||
1) data shared with higher level stuff - commands, status, etc.
|
||||
2) data that is local to the motion controller
|
||||
3) data shared with lower level stuff - hal pins
|
||||
|
||||
In addition, some internal data (2) should be shared for trouble
|
||||
shooting purposes, even though it is "internal" to the motion
|
||||
controller. Depending on the type of data, it can either be
|
||||
treated as type (1), and made available to the higher level
|
||||
code, or it can be treated as type (3), and made available to
|
||||
the hal, so that halscope can monitor it.
|
||||
|
||||
This file should ONLY contain structures and declarations for
|
||||
type (1) items - those that are shared with higher level code.
|
||||
|
||||
Type (2) items should be declared in mot_priv.h, along
|
||||
with type (3) items.
|
||||
|
||||
In the interest of retaining my sanity, I'm not gonna attempt
|
||||
to move everything to its proper location yet....
|
||||
|
||||
However, all new items will be defined in the proper place,
|
||||
and some existing items may be moved from one struct definition
|
||||
to another.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef MOTION_H
|
||||
#define MOTION_H
|
||||
|
||||
#include <rtapi_stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <rtapi_bool.h>
|
||||
#include <rtapi_limits.h>
|
||||
#include <posemath.h> /* PmCartesian, PmPose, pmCartMag() */
|
||||
#include <emcpos.h> /* EmcPose */
|
||||
#include "../kinematics/cubic.h" /* CUBIC_STRUCT, CUBIC_COEFF */
|
||||
#include <emcmotcfg.h> /* EMCMOT_MAX_JOINTS */
|
||||
#include <kinematics.h>
|
||||
|
||||
#include "simple_tp.h"
|
||||
#include "state_tag.h"
|
||||
#include "../tp/tp_types.h"
|
||||
|
||||
// define a special value to denote an invalid motion ID
|
||||
// NB: do not ever generate a motion id of MOTION_INVALID_ID
|
||||
// this should be really be tested for in command.c
|
||||
|
||||
#define MOTION_INVALID_ID INT_MIN
|
||||
#define MOTION_ID_VALID(x) ((x) != MOTION_INVALID_ID)
|
||||
|
||||
#include <rtapi.h> /* must precede rtapi_atomic.h in kernel mode */
|
||||
#include <rtapi_atomic.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This enum lists all the possible commands */
|
||||
|
||||
typedef enum {
|
||||
EMCMOT_ABORT = 1, /* abort all motion */
|
||||
EMCMOT_ENABLE, /* enable servos for active joints */
|
||||
EMCMOT_DISABLE, /* disable servos for active joints */
|
||||
|
||||
EMCMOT_PAUSE, /* pause motion */
|
||||
EMCMOT_REVERSE, /* run reverse motion */
|
||||
EMCMOT_FORWARD, /* run reverse motion */
|
||||
EMCMOT_RESUME, /* resume motion */
|
||||
EMCMOT_STEP, /* resume motion until id encountered */
|
||||
EMCMOT_FREE, /* set mode to free (joint) motion */
|
||||
EMCMOT_COORD, /* set mode to coordinated motion */
|
||||
EMCMOT_TELEOP, /* set mode to teleop */
|
||||
|
||||
EMCMOT_SPINDLE_SCALE, /* set scale factor for spindle speed */
|
||||
EMCMOT_SS_ENABLE, /* enable/disable scaling the spindle speed */
|
||||
EMCMOT_FEED_SCALE, /* set scale factor for feedrate */
|
||||
EMCMOT_RAPID_SCALE, /* set scale factor for rapids */
|
||||
EMCMOT_FS_ENABLE, /* enable/disable scaling feedrate */
|
||||
EMCMOT_FH_ENABLE, /* enable/disable feed_hold */
|
||||
EMCMOT_AF_ENABLE, /* enable/disable adaptive feedrate */
|
||||
EMCMOT_OVERRIDE_LIMITS, /* temporarily ignore limits until jog done */
|
||||
|
||||
EMCMOT_SET_LINE, /* queue up a linear move */
|
||||
EMCMOT_SET_CIRCLE, /* queue up a circular move */
|
||||
EMCMOT_SET_TELEOP_VECTOR, /* Move at a given velocity but in
|
||||
world cartesian coordinates, not
|
||||
in joint space like EMCMOT_JOG_* */
|
||||
EMCMOT_CLEAR_PROBE_FLAGS, /* clears probeTripped flag */
|
||||
EMCMOT_PROBE, /* go to pos, stop if probe trips, record
|
||||
trip pos */
|
||||
EMCMOT_RIGID_TAP, /* go to pos, with sync to spindle speed,
|
||||
then return to initial pos */
|
||||
|
||||
EMCMOT_SET_VEL, /* set the velocity for subsequent moves */
|
||||
EMCMOT_SET_VEL_LIMIT, /* set the max vel for all moves (tooltip) */
|
||||
EMCMOT_SET_ACC, /* set the max accel for moves (tooltip) */
|
||||
EMCMOT_SET_JERK, /* set the max jerk for moves (tooltip) */
|
||||
EMCMOT_SET_PLANNER_TYPE, /* set planner type (0=trapezoidal, 1=S-curve) */
|
||||
EMCMOT_SET_TERM_COND, /* set termination condition (stop, blend) */
|
||||
EMCMOT_SET_NUM_JOINTS, /* set the number of joints */
|
||||
EMCMOT_SET_NUM_SPINDLES, /* set the number of spindles */
|
||||
EMCMOT_SET_WORLD_HOME, /* set pose for world home */
|
||||
|
||||
EMCMOT_SET_DEBUG, /* sets the debug level */
|
||||
EMCMOT_SET_DOUT, /* sets or unsets a DIO, this can be immediate or synched with motion */
|
||||
EMCMOT_SET_AOUT, /* sets or unsets a AIO, this can be immediate or synched with motion */
|
||||
EMCMOT_SET_SPINDLESYNC, /* synchronize motion to spindle encoder */
|
||||
EMCMOT_SPINDLE_ON, /* start the spindle */
|
||||
EMCMOT_SPINDLE_OFF, /* stop the spindle */
|
||||
EMCMOT_SPINDLE_INCREASE, /* spindle faster */
|
||||
EMCMOT_SPINDLE_DECREASE, /* spindle slower */
|
||||
EMCMOT_SPINDLE_BRAKE_ENGAGE, /* engage the spindle brake */
|
||||
EMCMOT_SPINDLE_BRAKE_RELEASE, /* release the spindle brake */
|
||||
EMCMOT_SPINDLE_ORIENT, /* orient the spindle */
|
||||
EMCMOT_SET_OFFSET, /* set tool offsets */
|
||||
EMCMOT_SET_MAX_FEED_OVERRIDE,
|
||||
EMCMOT_SETUP_ARC_BLENDS,
|
||||
|
||||
EMCMOT_SET_PROBE_ERR_INHIBIT,
|
||||
EMCMOT_ENABLE_WATCHDOG, /* enable watchdog sound, parport */
|
||||
EMCMOT_DISABLE_WATCHDOG, /* enable watchdog sound, parport */
|
||||
EMCMOT_JOG_CONT, /* continuous jog */
|
||||
EMCMOT_JOG_INCR, /* incremental jog */
|
||||
EMCMOT_JOG_ABS, /* absolute jog */
|
||||
|
||||
EMCMOT_JOG_ABORT, /* abort one joint num or axis num */
|
||||
EMCMOT_JOINT_ACTIVATE, /* make joint active */
|
||||
EMCMOT_JOINT_DEACTIVATE, /* make joint inactive */
|
||||
EMCMOT_JOINT_HOME, /* home a joint or all joints */
|
||||
EMCMOT_JOINT_UNHOME, /* unhome a joint or all joints*/
|
||||
EMCMOT_SET_JOINT_POSITION_LIMITS, /* set the joint position +/- limits */
|
||||
EMCMOT_SET_JOINT_BACKLASH, /* set the joint backlash */
|
||||
EMCMOT_SET_JOINT_MIN_FERROR, /* minimum following error, input units */
|
||||
EMCMOT_SET_JOINT_MAX_FERROR, /* maximum following error, input units */
|
||||
EMCMOT_SET_JOINT_VEL_LIMIT, /* set the max joint vel */
|
||||
EMCMOT_SET_JOINT_ACC_LIMIT, /* set the max joint accel */
|
||||
EMCMOT_SET_JOINT_HOMING_PARAMS, /* sets joint homing parameters */
|
||||
EMCMOT_SET_JOINT_JERK_LIMIT, /* set the max joint jerk */
|
||||
EMCMOT_UPDATE_JOINT_HOMING_PARAMS, /* updates some joint homing parameters */
|
||||
EMCMOT_SET_JOINT_MOTOR_OFFSET, /* set the offset between joint and motor */
|
||||
EMCMOT_SET_JOINT_COMP, /* set a compensation triplet for a joint (nominal, forw., rev.) */
|
||||
|
||||
EMCMOT_SET_AXIS_POSITION_LIMITS, /* set the axis position +/- limits */
|
||||
EMCMOT_SET_AXIS_VEL_LIMIT, /* set the max axis vel */
|
||||
EMCMOT_SET_AXIS_ACC_LIMIT, /* set the max axis acc */
|
||||
EMCMOT_SET_AXIS_LOCKING_JOINT, /* set the axis locking joint */
|
||||
EMCMOT_SET_AXIS_JERK_LIMIT, /* set the max axis jerk */
|
||||
|
||||
EMCMOT_SET_SPINDLE_PARAMS, /* One command to set all spindle params */
|
||||
|
||||
} cmd_code_t;
|
||||
|
||||
/* this enum lists the possible results of a command */
|
||||
|
||||
typedef enum {
|
||||
EMCMOT_COMMAND_OK = 0, /* cmd honored */
|
||||
EMCMOT_COMMAND_UNKNOWN_COMMAND, /* cmd not understood */
|
||||
EMCMOT_COMMAND_INVALID_COMMAND, /* cmd can't be handled now */
|
||||
EMCMOT_COMMAND_INVALID_PARAMS, /* bad cmd params */
|
||||
EMCMOT_COMMAND_BAD_EXEC /* error trying to initiate */
|
||||
} cmd_status_t;
|
||||
|
||||
/* termination conditions for queued motions */
|
||||
#define EMCMOT_TERM_COND_STOP 1
|
||||
#define EMCMOT_TERM_COND_BLEND 2
|
||||
#define EMCMOT_TERM_COND_TANGENT 3
|
||||
|
||||
/*********************************
|
||||
COMMAND STRUCTURE
|
||||
*********************************/
|
||||
|
||||
/* This is the command structure. There is one of these in shared
|
||||
memory, and all commands from higher level code come thru it.
|
||||
*/
|
||||
typedef struct emcmot_command_t {
|
||||
cmd_code_t command; /* command code (enum) */
|
||||
int commandNum; /* increment this for new command */
|
||||
double motor_offset; /* offset from joint to motor position */
|
||||
double maxLimit; /* pos value for position limit, output */
|
||||
double minLimit; /* neg value for position limit, output */
|
||||
double min_pos_speed; /* spindle minimum positive speed */
|
||||
double max_neg_speed; /* spindle maximum negative speed */
|
||||
EmcPose pos; /* line/circle endpt, or teleop vector */
|
||||
PmCartesian center; /* center for circle */
|
||||
PmCartesian normal; /* normal vec for circle */
|
||||
int turn; /* turns for circle or joint number for a locking indexer*/
|
||||
double vel; /* max velocity */
|
||||
double ini_maxvel; /* max velocity allowed by machine
|
||||
constraints (the INI file) */
|
||||
int motion_type; /* this move is because of traverse, feed, arc, or toolchange */
|
||||
double spindlesync; /* user units per spindle revolution, 0 = no sync */
|
||||
double acc; /* max acceleration */
|
||||
double jerk; /* jerk for traj */
|
||||
double ini_maxjerk;
|
||||
int planner_type; /* planner type: 0 = trapezoidal, 1 = S-curve */
|
||||
double backlash; /* amount of backlash */
|
||||
int id; /* id for motion */
|
||||
int termCond; /* termination condition */
|
||||
double tolerance; /* tolerance for path deviation in CONTINUOUS mode */
|
||||
int joint; /* which joint index to use for below */
|
||||
int axis; /* which axis index to use for below */
|
||||
int spindle; /* which spindle to use */
|
||||
double scale; /* velocity scale or spindle_speed scale arg */
|
||||
double offset; /* input, output, or home offset arg */
|
||||
double home; /* joint home position */
|
||||
double home_final_vel; /* joint velocity for moving from OFFSET to HOME */
|
||||
double search_vel; /* home search velocity */
|
||||
double latch_vel; /* home latch velocity */
|
||||
int flags; /* homing config flags, other boolean args */
|
||||
int home_sequence; /* order in homing sequence */
|
||||
int volatile_home; /* joint should get unhomed when we get unhome -2
|
||||
(generated by task upon estop, etc) */
|
||||
double minFerror; /* min following error */
|
||||
double maxFerror; /* max following error */
|
||||
int wdWait; /* cycle to wait before toggling wd */
|
||||
int debug; /* debug level, from DEBUG in INI file */
|
||||
unsigned char now, out, start, end; /* these are related to synched AOUT/DOUT. now=whether now or synched, out = which gets set, start=start value, end=end value */
|
||||
unsigned char mode; /* used for turning overrides etc. on/off */
|
||||
double comp_nominal, comp_forward, comp_reverse; /* compensation triplet, nominal, forward, reverse */
|
||||
unsigned char probe_type; /* ~1 = error if probe operation is unsuccessful (ngc default)
|
||||
|1 = suppress error, report in # instead
|
||||
~2 = move until probe trips (ngc default)
|
||||
|2 = move until probe clears */
|
||||
int probe_jog_err_inhibit; // setting to inhibit probe tripped while jogging error.
|
||||
int probe_home_err_inhibit; // setting to inhibit probe tripped while homeing error.
|
||||
EmcPose tool_offset; /* TLO */
|
||||
double orientation; /* angle for spindle orient */
|
||||
int state; /*spindle state seems to just be 0 for off and 1 for on andypugh 2025-04-03*/
|
||||
char direction; /* CANON_DIRECTION flag for spindle orient */
|
||||
double timeout; /* of wait for spindle orient to complete */
|
||||
unsigned char wait_for_spindle_at_speed; // EMCMOT_SPINDLE_ON now carries this, for next feed move
|
||||
int arcBlendOptDepth;
|
||||
int arcBlendEnable;
|
||||
int arcBlendFallbackEnable;
|
||||
int arcBlendGapCycles;
|
||||
double arcBlendRampFreq;
|
||||
double arcBlendTangentKinkRatio;
|
||||
double maxFeedScale;
|
||||
double ext_offset_vel; /* velocity for an external axis offset */
|
||||
double ext_offset_acc; /* acceleration for an external axis offset */
|
||||
struct state_tag_t tag;
|
||||
} emcmot_command_t;
|
||||
|
||||
/*! \todo FIXME - these packed bits might be replaced with chars
|
||||
memory is cheap, and being able to access them without those
|
||||
damn macros would be nice
|
||||
*/
|
||||
|
||||
/* motion flag type */
|
||||
typedef unsigned short EMCMOT_MOTION_FLAG;
|
||||
|
||||
/*
|
||||
motion status flag structure-- looks like:
|
||||
|
||||
MSB LSB
|
||||
v---------------v------------------v
|
||||
| | | | T | CE | C | IP | EN |
|
||||
^---------------^------------------^
|
||||
|
||||
where:
|
||||
|
||||
EN is 1 if calculations are enabled, 0 if not
|
||||
IP is 1 if all joints in position, 0 if not
|
||||
C is 1 if coordinated mode, 0 if in free mode
|
||||
CE is 1 if coordinated mode error, 0 if not
|
||||
T is 1 if we are in teleop mode.
|
||||
*/
|
||||
|
||||
/* bit masks */
|
||||
#define EMCMOT_MOTION_ENABLE_BIT 0x0001
|
||||
#define EMCMOT_MOTION_INPOS_BIT 0x0002
|
||||
#define EMCMOT_MOTION_COORD_BIT 0x0004
|
||||
#define EMCMOT_MOTION_ERROR_BIT 0x0008
|
||||
#define EMCMOT_MOTION_TELEOP_BIT 0x0010
|
||||
|
||||
/* joint flag type */
|
||||
typedef unsigned short EMCMOT_JOINT_FLAG;
|
||||
/*
|
||||
joint status flag structure-- looks like:
|
||||
|
||||
MSB LSB
|
||||
----------v-----------------v--------------------v-------------------v
|
||||
| AF | FE | AH | HD | H | HS | NHL | PHL | - | - | ER | IP | AC | EN |
|
||||
----------^-----------------^--------------------^-------------------^
|
||||
|
||||
|
||||
x = unused
|
||||
|
||||
where:
|
||||
|
||||
EN is 1 if joint amplifier is enabled, 0 if not
|
||||
AC is 1 if joint is active for calculations, 0 if not
|
||||
IP is 1 if joint is in position, 0 if not (free mode only)
|
||||
ER is 1 if joint has an error, 0 if not
|
||||
|
||||
PHL is 1 if joint is on maximum hardware limit, 0 if not
|
||||
NHL is 1 if joint is on minimum hardware limit, 0 if not
|
||||
|
||||
HS is 1 if joint home switch is tripped, 0 if not
|
||||
H is 1 if joint is homing, 0 if not
|
||||
HD is 1 if joint has been homed, 0 if not
|
||||
AH is 1 if joint is at home position, 0 if not
|
||||
|
||||
FE is 1 if joint exceeded following error, 0 if not
|
||||
AF is 1 if amplifier is faulted, 0 if not
|
||||
|
||||
Suggestion: Split this in to an Error and a Status flag register..
|
||||
Then a simple test on each of the two flags can be performed
|
||||
rather than testing each bit... Saving on a global per joint
|
||||
fault and ready status flag.
|
||||
*/
|
||||
|
||||
/* bit masks */
|
||||
#define EMCMOT_JOINT_ENABLE_BIT 0x0001
|
||||
#define EMCMOT_JOINT_ACTIVE_BIT 0x0002
|
||||
#define EMCMOT_JOINT_INPOS_BIT 0x0004
|
||||
#define EMCMOT_JOINT_ERROR_BIT 0x0008
|
||||
#define EMCMOT_JOINT_MAX_HARD_LIMIT_BIT 0x0010
|
||||
#define EMCMOT_JOINT_MIN_HARD_LIMIT_BIT 0x0020
|
||||
#define EMCMOT_JOINT_FERROR_BIT 0x0040
|
||||
#define EMCMOT_JOINT_FAULT_BIT 0x0080
|
||||
|
||||
/*! \todo FIXME - the terms "teleop", "coord", and "free" are poorly
|
||||
documented. This is my feeble attempt to understand exactly
|
||||
what they mean.
|
||||
|
||||
According to Fred, teleop is never used with machine tools,
|
||||
although that may not be true for machines with non-trivial
|
||||
kinematics.
|
||||
|
||||
"coord", or coordinated mode, means that all the joints are
|
||||
synchronized, and move together as commanded by the higher
|
||||
level code. It is the normal mode when machining. In
|
||||
coordinated mode, commands are assumed to be in the cartesean
|
||||
reference frame, and if the machine is non-cartesean, the
|
||||
commands are translated by the kinematics to drive each
|
||||
joint in joint space as needed.
|
||||
|
||||
"free" mode means commands are interpreted in joint space.
|
||||
It is used for jogging individual joints, although
|
||||
it does not preclude multiple joints moving at once (I think).
|
||||
Homing is also done in free mode, in fact machines with
|
||||
non-trivial kinematics must be homed before they can go
|
||||
into either coord or teleop mode.
|
||||
|
||||
'teleop' is what you probably want if you are 'jogging'
|
||||
a hexapod. The jog commands as implemented by the motion
|
||||
controller are joint jogs, which work in free mode. But
|
||||
if you want to jog a hexapod or similar machine along
|
||||
one particular cartesean axis, you need to operate more
|
||||
than one joint. That's what 'teleop' is for.
|
||||
|
||||
*/
|
||||
|
||||
/* compensation structures */
|
||||
typedef struct {
|
||||
double nominal; /* nominal (command) position */
|
||||
float fwd_trim; /* correction for forward movement */
|
||||
float rev_trim; /* correction for reverse movement */
|
||||
float fwd_slope; /* slopes between here and next pt */
|
||||
float rev_slope;
|
||||
} emcmot_comp_entry_t;
|
||||
|
||||
|
||||
#define EMCMOT_COMP_SIZE 256
|
||||
typedef struct {
|
||||
int entries; /* number of entries in the array */
|
||||
emcmot_comp_entry_t *entry; /* current entry in array */
|
||||
emcmot_comp_entry_t array[EMCMOT_COMP_SIZE+2];
|
||||
/* +2 because array has -HUGE_VAL and +HUGE_VAL entries at the ends */
|
||||
} emcmot_comp_t;
|
||||
|
||||
/* motion controller states */
|
||||
|
||||
typedef enum {
|
||||
EMCMOT_MOTION_DISABLED = 0,
|
||||
EMCMOT_MOTION_FREE,
|
||||
EMCMOT_MOTION_TELEOP,
|
||||
EMCMOT_MOTION_COORD
|
||||
} motion_state_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
EMCMOT_ORIENT_NONE = 0,
|
||||
EMCMOT_ORIENT_COMPLETE,
|
||||
EMCMOT_ORIENT_IN_PROGRESS,
|
||||
EMCMOT_ORIENT_FAULTED,
|
||||
} orient_state_t;
|
||||
|
||||
/* flags for enabling spindle scaling, feed scaling,
|
||||
adaptive feed, and feed hold */
|
||||
|
||||
#define SS_ENABLED 0x01
|
||||
#define FS_ENABLED 0x02
|
||||
#define AF_ENABLED 0x04
|
||||
#define FH_ENABLED 0x08
|
||||
|
||||
/* This structure contains all of the data associated with
|
||||
a single joint. Note that this structure does not need
|
||||
to be in shared memory (but it can, if desired for debugging
|
||||
reasons). The portions of this structure that are considered
|
||||
"status" and need to be made available to user space are
|
||||
copied to a much smaller struct called emcmot_joint_status_t
|
||||
which is located in shared memory.
|
||||
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
/* configuration info - changes rarely */
|
||||
int type; /* 0 = linear, 1 = rotary */
|
||||
double max_pos_limit; /* upper soft limit on joint pos */
|
||||
double min_pos_limit; /* lower soft limit on joint pos */
|
||||
double max_jog_limit; /* jog limits change when not homed */
|
||||
double min_jog_limit;
|
||||
double vel_limit; /* upper limit of joint speed */
|
||||
double acc_limit; /* upper limit of joint accel */
|
||||
double jerk_limit; /* upper limit of joint jerk */
|
||||
double min_ferror; /* zero speed following error limit */
|
||||
double max_ferror; /* max speed following error limit */
|
||||
double backlash; /* amount of backlash */
|
||||
emcmot_comp_t comp; /* leadscrew correction data */
|
||||
|
||||
/* status info - changes regularly */
|
||||
/* many of these need to be made available to higher levels */
|
||||
/* they can either be copied to the status struct, or an array of
|
||||
joint structs can be made part of the status */
|
||||
EMCMOT_JOINT_FLAG flag; /* see above for bit details */
|
||||
double coarse_pos; /* trajectory point, before interp */
|
||||
double pos_cmd; /* commanded joint position */
|
||||
double vel_cmd; /* commanded joint velocity */
|
||||
double acc_cmd; /* commanded joint acceleration */
|
||||
double jerk_cmd; /* comanded joint jerk */
|
||||
double backlash_corr; /* correction for backlash */
|
||||
double backlash_filt; /* filtered backlash correction */
|
||||
double backlash_vel; /* backlash velocity variable */
|
||||
double motor_pos_cmd; /* commanded position, with comp */
|
||||
double motor_pos_fb; /* position feedback, with comp */
|
||||
double pos_fb; /* position feedback, comp removed */
|
||||
double ferror; /* following error */
|
||||
double ferror_limit; /* limit depends on speed */
|
||||
double ferror_high_mark; /* max following error */
|
||||
simple_tp_t free_tp; /* planner for free mode motion */
|
||||
int kb_jjog_active; /* non-zero during a keyboard jog */
|
||||
int wheel_jjog_active; /* non-zero during a wheel jog */
|
||||
|
||||
/* internal info - changes regularly, not usually accessed from user
|
||||
space */
|
||||
CUBIC_STRUCT cubic; /* cubic interpolator data */
|
||||
|
||||
int on_pos_limit; /* non-zero if on limit */
|
||||
int on_neg_limit; /* non-zero if on limit */
|
||||
|
||||
double motor_offset; /* diff between internal and motor pos, used
|
||||
to set position to zero during homing */
|
||||
int old_jjog_counts; /* prior value, used for deltas */
|
||||
double big_vel; /* used for "debouncing" velocity */
|
||||
} emcmot_joint_t;
|
||||
|
||||
/* This structure contains only the "status" data associated with
|
||||
a joint. "Status" data is that data that should be reported to
|
||||
user space on a continuous basis. An array of these structs is
|
||||
part of the main status structure, and is filled in with data
|
||||
copied from the emcmot_joint_t structs every servo period.
|
||||
|
||||
For now this struct contains more data than it really needs, but
|
||||
paring it down will take time (and probably needs to be done one
|
||||
or two items at a time, with much testing). My main goal right
|
||||
now is to get get the large joint struct out of status.
|
||||
|
||||
*/
|
||||
typedef struct {
|
||||
EMCMOT_JOINT_FLAG flag; /* see above for bit details */
|
||||
bool homed;
|
||||
bool homing;
|
||||
|
||||
double pos_cmd; /* commanded joint position */
|
||||
double pos_fb; /* position feedback, comp removed */
|
||||
double vel_cmd; /* current velocity */
|
||||
double acc_cmd; /* current acceleration */
|
||||
double ferror; /* following error */
|
||||
double ferror_high_mark; /* max following error */
|
||||
|
||||
/*! \todo FIXME - the following are not really "status", but taskintf.cc expects
|
||||
them to be in the status structure. I don't know how or if they are
|
||||
used by the user space code. Ideally they will be removed from here,
|
||||
but each one will need to be investigated individually.
|
||||
*/
|
||||
double backlash; /* amount of backlash */
|
||||
double max_pos_limit; /* upper soft limit on joint pos */
|
||||
double min_pos_limit; /* lower soft limit on joint pos */
|
||||
double min_ferror; /* zero speed following error limit */
|
||||
double max_ferror; /* max speed following error limit */
|
||||
} emcmot_joint_status_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
double speed; // spindle speed in RPMs
|
||||
double scale; // spindle override value
|
||||
double net_scale; // scale or zero if inhibited
|
||||
double css_factor;
|
||||
double xoffset;
|
||||
int state;
|
||||
int direction; // 0 stopped, 1 forward, -1 reverse
|
||||
int brake; // 0 released, 1 engaged
|
||||
int locked; // spindle lock engaged after orient
|
||||
int orient_fault; // fault code from motion.spindle-orient-fault
|
||||
int orient_state; // orient_state_t
|
||||
int spindle_index_enable; /* hooked to a canon encoder index-enable */
|
||||
double spindleRevs; /* position of spindle in revolutions */
|
||||
double spindleSpeedIn; /* velocity of spindle in revolutions per minute */
|
||||
int at_speed;
|
||||
int fault; /* amplifier fault */
|
||||
double max_pos_speed; /* spindle speed limits */
|
||||
double min_pos_speed; /* signed values, so max_neg = 0 */
|
||||
double max_neg_speed; /* and min_neg = -1e99 indicates no limit */
|
||||
double min_neg_speed;
|
||||
double home_angle;
|
||||
double home_search_vel;
|
||||
int home_sequence;
|
||||
double increment;
|
||||
} spindle_status_t;
|
||||
|
||||
typedef struct {
|
||||
double teleop_vel_cmd; /* commanded axis velocity */
|
||||
double max_pos_limit; /* upper soft limit on axis pos */
|
||||
double min_pos_limit; /* lower soft limit on axis pos */
|
||||
} emcmot_axis_status_t;
|
||||
|
||||
/*********************************
|
||||
STATUS STRUCTURE
|
||||
*********************************/
|
||||
|
||||
/* This is the status structure. There is one of these in shared
|
||||
memory, and it reports motion controller status to higher level
|
||||
code in user space. For the most part, this structure contains
|
||||
higher level variables - low level stuff is made visible to the
|
||||
HAL and troubleshooting, etc, is done using the HAL oscilloscope.
|
||||
*/
|
||||
|
||||
/*! \todo FIXME - this struct is broken into two parts... at the top are
|
||||
structure members that I understand, and that are needed for emc2.
|
||||
Other structure members follow. All the later ones need to be
|
||||
evaluated - either they move up, or they go away.
|
||||
*/
|
||||
|
||||
typedef struct emcmot_status_t {
|
||||
unsigned char head; /* flag count for mutex detect */
|
||||
/* these three are updated only when a new command is handled */
|
||||
cmd_code_t commandEcho; /* echo of input command */
|
||||
int commandNumEcho; /* echo of input command number */
|
||||
cmd_status_t commandStatus; /* result of most recent command */
|
||||
/* these are config info, updated when a command changes them */
|
||||
double feed_scale; /* velocity scale factor for all motion but rapids */
|
||||
double rapid_scale; /* velocity scale factor for rapids */
|
||||
unsigned char enables_new; /* flags for FS, SS, etc */
|
||||
/* the above set is the enables in effect for new moves */
|
||||
/* the rest are updated every cycle */
|
||||
double net_feed_scale; /* net scale factor for all motion */
|
||||
unsigned char enables_queued; /* flags for FS, SS, etc */
|
||||
/* the above set is the enables in effect for the
|
||||
currently executing move */
|
||||
motion_state_t motion_state; /* operating state: FREE, COORD, etc. */
|
||||
EMCMOT_MOTION_FLAG motionFlag; /* see above for bit details */
|
||||
EmcPose carte_pos_cmd; /* commanded Cartesian position */
|
||||
int carte_pos_cmd_ok; /* non-zero if command is valid */
|
||||
EmcPose carte_pos_fb; /* actual Cartesian position */
|
||||
int carte_pos_fb_ok; /* non-zero if feedback is valid */
|
||||
EmcPose world_home; /* cartesean coords of home position */
|
||||
emcmot_joint_status_t joint_status[EMCMOT_MAX_JOINTS]; /* all joint status data */
|
||||
emcmot_axis_status_t axis_status[EMCMOT_MAX_AXIS]; /* all axis status data */
|
||||
int spindleSync; /* spindle used for synchronised moves. -1 = none */
|
||||
spindle_status_t spindle_status[EMCMOT_MAX_SPINDLES]; /* all spindle data */
|
||||
|
||||
|
||||
int on_soft_limit; /* non-zero if any joint is on soft limit */
|
||||
|
||||
int probeVal; /* debounced value of probe input */
|
||||
|
||||
int probeTripped; /* Has the probe signal changed since start
|
||||
of probe command? */
|
||||
int probing; /* Currently looking for a probe signal? */
|
||||
unsigned char probe_type;
|
||||
EmcPose probedPos; /* Axis positions stored as soon as possible
|
||||
after last probeTripped */
|
||||
|
||||
|
||||
int synch_di[EMCMOT_MAX_DIO]; /* inputs to the motion controller, queried by G-code */
|
||||
int synch_do[EMCMOT_MAX_DIO]; /* outputs to the motion controller, queried by G-code */
|
||||
double analog_input[EMCMOT_MAX_AIO]; /* inputs to the motion controller, queried by G-code */
|
||||
double analog_output[EMCMOT_MAX_AIO]; /* outputs to the motion controller, queried by G-code */
|
||||
int misc_error[EMCMOT_MAX_MISC_ERROR]; /* Random Error pins*/
|
||||
struct state_tag_t tag; /* Current interp state corresponding
|
||||
to motion line */
|
||||
|
||||
/*! \todo FIXME - all structure members beyond this point are in limbo */
|
||||
|
||||
/* dynamic status-- changes every cycle */
|
||||
uint64_t heartbeat; /* Incremented every time the motion controller is done. */
|
||||
int config_num; /* incremented whenever configuration
|
||||
changed. */
|
||||
int id; /* id for executing motion */
|
||||
int depth; /* motion queue depth */
|
||||
int activeDepth; /* depth of active blend elements */
|
||||
int queueFull; /* Flag to indicate the tc queue is full */
|
||||
int paused; /* Flag to signal motion paused */
|
||||
int overrideLimitMask; /* non-zero means one or more limits ignored */
|
||||
/* 1 << (joint-num*2) = ignore neg limit */
|
||||
/* 2 << (joint-num*2) = ignore pos limit */
|
||||
int reverse_run;
|
||||
|
||||
/* static status-- only changes upon input commands, e.g., config */
|
||||
double vel; /* scalar max vel */
|
||||
double acc; /* scalar max accel */
|
||||
double jerk; /* jerk for traj */
|
||||
int planner_type; /* planner type: 0 = trapezoidal, 1 = S-curve */
|
||||
|
||||
int motionType;
|
||||
double distance_to_go; /* in this move */
|
||||
EmcPose dtg;
|
||||
double current_vel;
|
||||
double requested_vel;
|
||||
|
||||
/* S-curve motion state - for accurate jerk output */
|
||||
double current_acc; /* current path acceleration */
|
||||
double current_jerk; /* current path jerk (accurate value from TP) */
|
||||
double decel_dist; /* S-curve deceleration distance (dlen1) for debugging */
|
||||
PmCartesian current_dir; /* current motion direction unit vector */
|
||||
|
||||
unsigned int tcqlen;
|
||||
EmcPose tool_offset;
|
||||
int atspeed_next_feed; /* at next feed move, wait for spindle to be at speed */
|
||||
unsigned char tail; /* flag count for mutex detect */
|
||||
int external_offsets_applied;
|
||||
EmcPose eoffset_pose;
|
||||
int numExtraJoints;
|
||||
int stepping;
|
||||
bool jogging_active;
|
||||
} emcmot_status_t;
|
||||
|
||||
/*********************************
|
||||
CONFIG STRUCTURE
|
||||
*********************************/
|
||||
|
||||
/* This is the config structure. This is currently in shared memory,
|
||||
but I have no idea why... there are commands to set most of the
|
||||
items in this structure. It seems we should either put the struct
|
||||
in private memory and manipulate it with commands, or we should
|
||||
put it in shared memory and manipulate it directly - not both.
|
||||
The structure contains static or rarely changed information that
|
||||
describes the machine configuration.
|
||||
|
||||
later: I think I get it now - the struct is in shared memory so
|
||||
user space can read the config at any time, but commands are used
|
||||
to change the config so they only take effect when the realtime
|
||||
code processes the command.
|
||||
*/
|
||||
|
||||
/*! \todo FIXME - this struct is broken into two parts... at the top are
|
||||
structure members that I understand, and that are needed for emc2.
|
||||
Other structure members follow. All the later ones need to be
|
||||
evaluated - either they move up, or they go away.
|
||||
*/
|
||||
typedef struct emcmot_config_t {
|
||||
unsigned char head; /* flag count for mutex detect */
|
||||
|
||||
int config_num; /* Incremented everytime configuration
|
||||
changed, should match status.config_num */
|
||||
int numJoints; /* The number of total joints in the system (which
|
||||
must be between 1 and EMCMOT_MAX_JOINTS,
|
||||
inclusive). includes extra joints*/
|
||||
int numExtraJoints; /* The number of extra joints in the system (which
|
||||
must be between 1 and EMCMOT_MAX_EXTRAJOINTS,
|
||||
inclusive). */
|
||||
int numSpindles; /* The number of spindles, 1 to EMCMOT_MAX_SPINDLES */
|
||||
|
||||
KINEMATICS_TYPE kinType;
|
||||
|
||||
int numDIO; /* userdefined number of digital IO. default is 4. (EMCMOT_MAX_DIO=64),
|
||||
but can be altered at motmod insmod time */
|
||||
|
||||
int numAIO; /* userdefined number of analog IO. default is 4. (EMCMOT_MAX_AIO=16),
|
||||
but can be altered at motmod insmod time */
|
||||
|
||||
int numMiscError; /* userdefined number of Misc Errors. default is 0.
|
||||
but can be altered at motmod insmod time */
|
||||
|
||||
/*! \todo FIXME - all structure members beyond this point are in limbo */
|
||||
|
||||
double trajCycleTime; /* the rate at which the trajectory loop
|
||||
runs.... (maybe) */
|
||||
double servoCycleTime; /* the rate of the servo loop - Not the same
|
||||
as the traj time */
|
||||
|
||||
int interpolationRate; /* grep control.c for an explanation....
|
||||
approx line 50 */
|
||||
|
||||
double limitVel; /* scalar upper limit on vel */
|
||||
int debug; /* copy of DEBUG, from INI file */
|
||||
unsigned char tail; /* flag count for mutex detect */
|
||||
int arcBlendOptDepth;
|
||||
int arcBlendEnable;
|
||||
int arcBlendFallbackEnable;
|
||||
int arcBlendGapCycles;
|
||||
double arcBlendRampFreq;
|
||||
double arcBlendTangentKinkRatio;
|
||||
double maxFeedScale;
|
||||
int inhibit_probe_jog_error;
|
||||
int inhibit_probe_home_error;
|
||||
} emcmot_config_t;
|
||||
|
||||
/* error structure - lockfree MPSC ring buffer. See emcmotutil.c. */
|
||||
typedef struct emcmot_error_t {
|
||||
char error[EMCMOT_ERROR_NUM][EMCMOT_ERROR_LEN];
|
||||
rtapi_atomic_ullong write_reserve;
|
||||
rtapi_atomic_ullong write_commit;
|
||||
rtapi_atomic_ullong read_seq;
|
||||
} emcmot_error_t;
|
||||
|
||||
|
||||
typedef struct emcmot_internal_t {
|
||||
unsigned char head; /* flag count for mutex detect */
|
||||
unsigned char tail; /* flag count for mutex detect */
|
||||
int split; /* number of split command reads */
|
||||
int enabling; /* starts up disabled */
|
||||
int coordinating; /* starts up in free mode */
|
||||
int teleoperating; /* starts up in free mode */
|
||||
int overriding; /* non-zero means we've initiated an joint
|
||||
move while overriding limits */
|
||||
TP_STRUCT coord_tp; /* coordinated mode planner */
|
||||
int idForStep; /* status id while stepping */
|
||||
} emcmot_internal_t;
|
||||
|
||||
/* error ring buffer access functions */
|
||||
extern int emcmotErrorInit(emcmot_error_t * errlog);
|
||||
extern int emcmotErrorPut(emcmot_error_t * errlog, const char *error);
|
||||
extern int emcmotErrorPutfv(emcmot_error_t * errlog, const char *fmt, va_list ap);
|
||||
extern int emcmotErrorPutf(emcmot_error_t * errlog, const char *fmt, ...);
|
||||
extern int emcmotErrorGet(emcmot_error_t * errlog, char *error);
|
||||
|
||||
#define GET_JOINT_ACTIVE_FLAG(joint) ((joint)->flag & EMCMOT_JOINT_ACTIVE_BIT ? 1 : 0)
|
||||
#define GET_JOINT_INPOS_FLAG(joint) ((joint)->flag & EMCMOT_JOINT_INPOS_BIT ? 1 : 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* MOTION_H */
|
||||
107
wasm-port/vendor/linuxcnc/src/emc/motion/simple_tp.h
vendored
Normal file
107
wasm-port/vendor/linuxcnc/src/emc/motion/simple_tp.h
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
/********************************************************************
|
||||
* Description: simple_tp.h
|
||||
* A simple, single axis trajectory planner
|
||||
*
|
||||
* Author:
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
*
|
||||
* Copyright (c) 2004 All rights reserved
|
||||
********************************************************************/
|
||||
|
||||
/* simple_tp.c and simple_tp.h define a simple, single axis trajectory
|
||||
planner. It is based on the "free mode trajectory planner" that was
|
||||
originally written as part of EMC2's control.c, but the code has
|
||||
been pulled out of control.c and given a somewhat object oriented
|
||||
API to allow it to be used for both teleop and free mode.
|
||||
*/
|
||||
|
||||
#ifndef SIMPLE_TP_H
|
||||
#define SIMPLE_TP_H
|
||||
|
||||
// stopping criterion:
|
||||
#define TINY_DP(max_acc,period) (max_acc*period*period*0.001)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct simple_tp_t {
|
||||
double pos_cmd; /* position command */
|
||||
double max_vel; /* velocity limit */
|
||||
double max_acc; /* acceleration limit */
|
||||
double max_jerk; /* jerk limit */
|
||||
int enable; /* if zero, motion stops ASAP */
|
||||
double curr_pos; /* current position */
|
||||
double curr_vel; /* current velocity */
|
||||
int active; /* non-zero if motion in progress */
|
||||
|
||||
double curr_acc; /* current acceleration */
|
||||
double curr_jerk; /* current acceleration */
|
||||
double last_move_length; /* current acceleration */
|
||||
|
||||
double last_pos_cmd;
|
||||
|
||||
int use_trapezoid;
|
||||
double curr_max_vel;
|
||||
int total_n;
|
||||
int curr_n;
|
||||
int n0;
|
||||
int n1;
|
||||
int n2;
|
||||
int n3;
|
||||
int n4;
|
||||
int n5;
|
||||
int n6;
|
||||
int fix_verr;
|
||||
double verr;
|
||||
double vc;
|
||||
double ve;
|
||||
double vm;
|
||||
double jm;
|
||||
double j2;
|
||||
double j4;
|
||||
double v1;
|
||||
double v2;
|
||||
double v3;
|
||||
|
||||
double v5;
|
||||
double v6;
|
||||
double v7;
|
||||
|
||||
double a1;
|
||||
double a2;
|
||||
double a3;
|
||||
|
||||
double a5;
|
||||
double a6;
|
||||
double a7;
|
||||
|
||||
double prograss;
|
||||
|
||||
int status;
|
||||
} simple_tp_t;
|
||||
|
||||
/* I could write a bunch of functions to read and write the first four
|
||||
structure members, and to read the last three, but that seems silly.
|
||||
*/
|
||||
|
||||
/* The update() function does all the work. If 'enable' is true, it
|
||||
computes a new value of 'curr_pos', which moves toward 'pos_cmd'
|
||||
while obeying the 'max_vel' and 'max_accel' limits. It also sets
|
||||
'active' if movement is in progress, and clears it when motion
|
||||
stops at the commanded position. The command or either of the
|
||||
limits can be changed at any time. If 'enable' is false, it
|
||||
ramps the velocity to zero, then clears 'active' and sets
|
||||
'pos_cmd' to match 'curr_pos', to avoid motion the next time it
|
||||
is enabled. 'period' is the period between calls, in seconds.
|
||||
*/
|
||||
|
||||
extern void simple_tp_update(simple_tp_t *tp, double period);
|
||||
extern void simple_tp_update_normal(simple_tp_t *tp, double period);
|
||||
extern void simple_scurve_tp_update(simple_tp_t *tp, double period);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SIMPLE_TP_H */
|
||||
304
wasm-port/vendor/linuxcnc/src/emc/nml_intf/emcpose.c
vendored
Normal file
304
wasm-port/vendor/linuxcnc/src/emc/nml_intf/emcpose.c
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
/********************************************************************
|
||||
* Description: emcpose.c
|
||||
*
|
||||
* Miscellaneous functions to handle EmcPose operations
|
||||
* Derived from a work by Fred Proctor & Will Shackleford
|
||||
*
|
||||
* Author: Robert W. Ellenberg
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
*
|
||||
* Copyright (c) 2014 All rights reserved.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include "emcpose.h"
|
||||
#include <posemath.h>
|
||||
#include <rtapi_math.h>
|
||||
|
||||
//#define EMCPOSE_PEDANTIC
|
||||
|
||||
void emcPoseZero(EmcPose * const pos) {
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if(!pos) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
pos->tran.x = 0.0;
|
||||
pos->tran.y = 0.0;
|
||||
pos->tran.z = 0.0;
|
||||
pos->a = 0.0;
|
||||
pos->b = 0.0;
|
||||
pos->c = 0.0;
|
||||
pos->u = 0.0;
|
||||
pos->v = 0.0;
|
||||
pos->w = 0.0;
|
||||
}
|
||||
|
||||
|
||||
int emcPoseAdd(EmcPose const * const p1, EmcPose const * const p2, EmcPose * const out)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!p1 || !p2) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
pmCartCartAdd(&p1->tran, &p2->tran, &out->tran);
|
||||
out->a = p1->a + p2->a;
|
||||
out->b = p1->b + p2->b;
|
||||
out->c = p1->c + p2->c;
|
||||
out->u = p1->u + p2->u;
|
||||
out->v = p1->v + p2->v;
|
||||
out->w = p1->w + p2->w;
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
int emcPoseSub(EmcPose const * const p1, EmcPose const * const p2, EmcPose * const out)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!p1 || !p2) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
pmCartCartSub(&p1->tran, &p2->tran, &out->tran);
|
||||
out->a = p1->a - p2->a;
|
||||
out->b = p1->b - p2->b;
|
||||
out->c = p1->c - p2->c;
|
||||
out->u = p1->u - p2->u;
|
||||
out->v = p1->v - p2->v;
|
||||
out->w = p1->w - p2->w;
|
||||
return EMCPOSE_ERR_OK;
|
||||
|
||||
}
|
||||
|
||||
int emcPoseSelfAdd(EmcPose * const self, EmcPose const * const p2)
|
||||
{
|
||||
return emcPoseAdd(self, p2, self);
|
||||
}
|
||||
|
||||
int emcPoseSelfSub(EmcPose * const self, EmcPose const * const p2)
|
||||
{
|
||||
return emcPoseSub(self, p2, self);
|
||||
}
|
||||
|
||||
int emcPoseToPmCartesian(EmcPose const * const pose,
|
||||
PmCartesian * const xyz, PmCartesian * const abc, PmCartesian * const uvw)
|
||||
{
|
||||
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
if (!xyz | !abc || !uvw) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
//Direct copy of translation struct for xyz
|
||||
*xyz = pose->tran;
|
||||
|
||||
//Convert ABCUVW axes into 2 pairs of 3D lines
|
||||
abc->x = pose->a;
|
||||
abc->y = pose->b;
|
||||
abc->z = pose->c;
|
||||
|
||||
uvw->x = pose->u;
|
||||
uvw->y = pose->v;
|
||||
uvw->z = pose->w;
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Collect PmCartesian elements into 9D EmcPose structure.
|
||||
*/
|
||||
int pmCartesianToEmcPose(PmCartesian const * const xyz,
|
||||
PmCartesian const * const abc, PmCartesian const * const uvw, EmcPose * const pose)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
if (!xyz || !abc || !uvw) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
//Direct copy of translation struct for xyz
|
||||
pose->tran = *xyz;
|
||||
|
||||
pose->a = abc->x;
|
||||
pose->b = abc->y;
|
||||
pose->c = abc->z;
|
||||
|
||||
pose->u = uvw->x;
|
||||
pose->v = uvw->y;
|
||||
pose->w = uvw->z;
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
int emcPoseSetXYZ(PmCartesian const * const xyz, EmcPose * const pose)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
if (!xyz) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
pose->tran.x = xyz->x;
|
||||
pose->tran.y = xyz->y;
|
||||
pose->tran.z = xyz->z;
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
int emcPoseSetABC(PmCartesian const * const abc, EmcPose * const pose)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
if (!abc) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
pose->a = abc->x;
|
||||
pose->b = abc->y;
|
||||
pose->c = abc->z;
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
int emcPoseSetUVW(PmCartesian const * const uvw, EmcPose * const pose)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
if (!uvw) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
pose->u = uvw->x;
|
||||
pose->v = uvw->y;
|
||||
pose->w = uvw->z;
|
||||
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
int emcPoseGetXYZ(EmcPose const * const pose, PmCartesian * const xyz)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
if (!xyz) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
xyz->x = pose->tran.x;
|
||||
xyz->y = pose->tran.y;
|
||||
xyz->z = pose->tran.z;
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
int emcPoseGetABC(EmcPose const * const pose, PmCartesian * const abc)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
if (!abc) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
abc->x = pose->a;
|
||||
abc->y = pose->b;
|
||||
abc->z = pose->c;
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
int emcPoseGetUVW(EmcPose const * const pose, PmCartesian * const uvw)
|
||||
{
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
if (!uvw) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
uvw->x = pose->u;
|
||||
uvw->y = pose->v;
|
||||
uvw->z = pose->w;
|
||||
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the magnitude of an EmcPose position, treating it like a single vector.
|
||||
*/
|
||||
int emcPoseMagnitude(EmcPose const * const pose, double * const out) {
|
||||
|
||||
#ifdef EMCPOSE_PEDANTIC
|
||||
if (!pose) {
|
||||
return EMCPOSE_ERR_INPUT_MISSING;
|
||||
}
|
||||
if (!out) {
|
||||
return EMCPOSE_ERR_OUTPUT_MISSING;
|
||||
}
|
||||
#endif
|
||||
|
||||
double mag = 0.0;
|
||||
mag += pmSq(pose->tran.x);
|
||||
mag += pmSq(pose->tran.y);
|
||||
mag += pmSq(pose->tran.z);
|
||||
mag += pmSq(pose->a);
|
||||
mag += pmSq(pose->b);
|
||||
mag += pmSq(pose->c);
|
||||
mag += pmSq(pose->u);
|
||||
mag += pmSq(pose->v);
|
||||
mag += pmSq(pose->w);
|
||||
mag = pmSqrt(mag);
|
||||
|
||||
*out = mag;
|
||||
return EMCPOSE_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true for a numerically valid pose, or false for an invalid pose (or null pointer).
|
||||
*/
|
||||
int emcPoseValid(EmcPose const * const pose)
|
||||
{
|
||||
|
||||
if (!pose ||
|
||||
isnan(pose->tran.x) ||
|
||||
isnan(pose->tran.y) ||
|
||||
isnan(pose->tran.z) ||
|
||||
isnan(pose->a) ||
|
||||
isnan(pose->b) ||
|
||||
isnan(pose->c) ||
|
||||
isnan(pose->u) ||
|
||||
isnan(pose->v) ||
|
||||
isnan(pose->w)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
25
wasm-port/vendor/linuxcnc/src/emc/nml_intf/motion_types.h
vendored
Normal file
25
wasm-port/vendor/linuxcnc/src/emc/nml_intf/motion_types.h
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef __LINUXCNC_MOTION_TYPES_H
|
||||
#define __LINUXCNC_MOTION_TYPES_H
|
||||
// Copyright 2008, Chris Radek <chris@timeguy.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#define EMC_MOTION_TYPE_TRAVERSE 1
|
||||
#define EMC_MOTION_TYPE_FEED 2
|
||||
#define EMC_MOTION_TYPE_ARC 3
|
||||
#define EMC_MOTION_TYPE_TOOLCHANGE 4
|
||||
#define EMC_MOTION_TYPE_PROBING 5
|
||||
#define EMC_MOTION_TYPE_INDEXROTARY 6
|
||||
|
||||
#endif
|
||||
1871
wasm-port/vendor/linuxcnc/src/emc/tp/blendmath.c
vendored
Normal file
1871
wasm-port/vendor/linuxcnc/src/emc/tp/blendmath.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
281
wasm-port/vendor/linuxcnc/src/emc/tp/blendmath.h
vendored
Normal file
281
wasm-port/vendor/linuxcnc/src/emc/tp/blendmath.h
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
/********************************************************************
|
||||
* Description: blendmath.h
|
||||
* Circular arc blend math functions
|
||||
*
|
||||
* Author: Robert W. Ellenberg
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
*
|
||||
* Copyright (c) 2014 All rights reserved.
|
||||
*
|
||||
* Last change:
|
||||
********************************************************************/
|
||||
#ifndef BLENDMATH_H
|
||||
#define BLENDMATH_H
|
||||
|
||||
#include <posemath.h>
|
||||
|
||||
#include "tc_types.h"
|
||||
#include "sp_scurve.h"
|
||||
|
||||
#define BLEND_ACC_RATIO_TANGENTIAL 0.5
|
||||
#define BLEND_ACC_RATIO_NORMAL (pmSqrt(1.0 - pmSq(BLEND_ACC_RATIO_TANGENTIAL)))
|
||||
#define BLEND_KINK_FACTOR 0.25
|
||||
|
||||
typedef enum {
|
||||
BLEND_NONE,
|
||||
BLEND_LINE_LINE,
|
||||
BLEND_LINE_ARC,
|
||||
BLEND_ARC_LINE,
|
||||
BLEND_ARC_ARC,
|
||||
} blend_type_t;
|
||||
|
||||
/**
|
||||
* 3D Input geometry for a spherical blend arc.
|
||||
* This structure contains all of the basic geometry in 3D for a blend arc.
|
||||
*/
|
||||
typedef struct {
|
||||
PmCartesian u1; /* unit vector along line 1 */
|
||||
PmCartesian u2; /* unit vector along line 2 */
|
||||
PmCartesian P; /* Intersection point */
|
||||
PmCartesian normal; /* normal unit vector to plane containing lines */
|
||||
PmCartesian binormal; /* binormal unit vector to plane containing lines */
|
||||
PmCartesian u_tan1; /* Actual tangent vector to 1 (used for arcs only) */
|
||||
PmCartesian u_tan2; /* Actual tangent vector to 2 (used for arcs only) */
|
||||
PmCartesian center1; /* Local approximation of center for arc 1 */
|
||||
PmCartesian center2; /* Local approximation of center for arc 2 */
|
||||
double radius1; /* Local approximation of radius */
|
||||
double radius2;
|
||||
double theta_tan;
|
||||
double v_max1; /* maximum velocity in direction u_tan1 */
|
||||
double v_max2; /* maximum velocity in direction u_tan2 */
|
||||
|
||||
} BlendGeom3;
|
||||
|
||||
/**
|
||||
* 9D Input geometry for a spherical blend arc.
|
||||
*/
|
||||
#ifdef BLEND_9D
|
||||
typedef struct {
|
||||
//Not implemented yet
|
||||
} BlendGeom9;
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Blend arc parameters (abstracted).
|
||||
* This structure holds blend arc parameters that have been abstracted from the
|
||||
* physical geometry. This data is used to find the maximum radius given the
|
||||
* constraints on the blend. By abstracting the parameters from the geometry,
|
||||
* the same calculations can be used with any input geometry (lines, arcs, 6 or
|
||||
* 9 dimensional lines).
|
||||
*/
|
||||
typedef struct {
|
||||
double tolerance; /* Net blend tolerance (min of line 1 and 2) */
|
||||
double L1; /* Available part of line 1 to blend over */
|
||||
double L2; /* Available part of line 2 to blend over */
|
||||
double v_req; /* requested velocity for the blend arc */
|
||||
double a_max; /* max acceleration allowed for blend */
|
||||
|
||||
/* These fields are considered "output", and may be refactored into a
|
||||
* separate structure in the future */
|
||||
|
||||
double theta; /* Intersection angle, half of angle between -u1 and u2 */
|
||||
double phi; /* supplement of intersection angle, angle between u1 and u2 */
|
||||
double a_n_max; /* max normal acceleration allowed */
|
||||
|
||||
double R_plan; /* planned radius for blend arc */
|
||||
double d_plan; /* distance along each line to arc endpoints */
|
||||
|
||||
double v_goal; /* desired velocity at max feed override */
|
||||
double v_plan; /* planned max velocity at max feed override */
|
||||
double v_actual; /* velocity at feedscale = 1.0 */
|
||||
double s_arc; /* arc length */
|
||||
int consume; /* Consume the previous segment */
|
||||
double line_length;
|
||||
//Arc specific stuff
|
||||
int convex1;
|
||||
int convex2;
|
||||
double phi1_max;
|
||||
double phi2_max;
|
||||
|
||||
} BlendParameters;
|
||||
|
||||
|
||||
/**
|
||||
* Output geometry in 3D.
|
||||
* Stores the three points representing a simple 3D spherical arc.
|
||||
*/
|
||||
typedef struct {
|
||||
PmCartesian arc_start; /* start point for blend arc */
|
||||
PmCartesian arc_end; /* end point for blend arc */
|
||||
PmCartesian arc_center; /* center point for blend arc */
|
||||
double trim1; /* length (line) or angle (arc) to cut from prev_tc */
|
||||
double trim2; /* length (line) or angle (arc) to cut from tc */
|
||||
} BlendPoints3;
|
||||
|
||||
|
||||
|
||||
#ifdef BLEND_9D
|
||||
typedef struct {
|
||||
//Not implemented yet
|
||||
} BlendPoints9;
|
||||
#endif
|
||||
|
||||
double findMaxTangentAngle(double v, double acc, double cycle_time);
|
||||
|
||||
double findKinkAccel(double kink_angle, double v_plan, double cycle_time);
|
||||
|
||||
double fsign(double f);
|
||||
|
||||
int clip_min(double * const x, double min);
|
||||
|
||||
int clip_max(double * const x, double max);
|
||||
|
||||
double saturate(double x, double max);
|
||||
|
||||
double bisaturate(double x, double max, double min);
|
||||
|
||||
int sat_inplace(double * const x, double max);
|
||||
|
||||
int checkTangentAngle(PmCircle const * const circ, SphericalArc const * const arc, BlendGeom3 const * const geom, BlendParameters const * const param, double cycle_time, int at_end);
|
||||
|
||||
int findIntersectionAngle(PmCartesian const * const u1,
|
||||
PmCartesian const * const u2, double * const theta);
|
||||
|
||||
double pmCartMin(PmCartesian const * const in);
|
||||
|
||||
int calculateInscribedDiameter(PmCartesian const * const normal,
|
||||
PmCartesian const * const bounds, double * const diameter);
|
||||
|
||||
int findAccelScale(PmCartesian const * const acc,
|
||||
PmCartesian const * const bounds,
|
||||
PmCartesian * const scale);
|
||||
|
||||
int pmUnitCartsColinear(PmCartesian const * const u1,
|
||||
PmCartesian const * const u2);
|
||||
|
||||
int pmCartCartParallel(PmCartesian const * const u1,
|
||||
PmCartesian const * const u2,
|
||||
double tol);
|
||||
|
||||
int pmCartCartAntiParallel(PmCartesian const * const u1,
|
||||
PmCartesian const * const u2,
|
||||
double tol);
|
||||
|
||||
int pmCircLineCoplanar(PmCircle const * const circ,
|
||||
PmCartLine const * const line, double tol);
|
||||
|
||||
int blendCoplanarCheck(PmCartesian const * const normal,
|
||||
PmCartesian const * const u1_tan,
|
||||
PmCartesian const * const u2_tan,
|
||||
double tol);
|
||||
|
||||
int blendCalculateNormals3(BlendGeom3 * const geom);
|
||||
|
||||
int blendComputeParameters(BlendParameters * const param);
|
||||
|
||||
int blendCheckConsume(BlendParameters * const param,
|
||||
BlendPoints3 const * const points,
|
||||
TC_STRUCT const * const prev_tc, int gap_cycles);
|
||||
|
||||
int blendFindPoints3(BlendPoints3 * const points, BlendGeom3 const * const geom,
|
||||
BlendParameters const * const param);
|
||||
|
||||
int blendGeom3Init(BlendGeom3 * const geom,
|
||||
TC_STRUCT const * const prev_tc,
|
||||
TC_STRUCT const * const tc);
|
||||
|
||||
int blendParamKinematics(BlendGeom3 * const geom,
|
||||
BlendParameters * const param,
|
||||
TC_STRUCT const * const prev_tc,
|
||||
TC_STRUCT const * const tc,
|
||||
PmCartesian const * const acc_bound,
|
||||
PmCartesian const * const vel_bound,
|
||||
double maxFeedScale);
|
||||
|
||||
int blendInit3FromLineLine(BlendGeom3 * const geom, BlendParameters * const param,
|
||||
TC_STRUCT const * const prev_tc,
|
||||
TC_STRUCT const * const tc,
|
||||
PmCartesian const * const acc_bound,
|
||||
PmCartesian const * const vel_bound,
|
||||
double maxFeedScale);
|
||||
|
||||
int blendInit3FromLineArc(BlendGeom3 * const geom, BlendParameters * const param,
|
||||
TC_STRUCT const * const prev_tc,
|
||||
TC_STRUCT const * const tc,
|
||||
PmCartesian const * const acc_bound,
|
||||
PmCartesian const * const vel_bound,
|
||||
double maxFeedScale);
|
||||
|
||||
int blendInit3FromArcLine(BlendGeom3 * const geom, BlendParameters * const param,
|
||||
TC_STRUCT const * const prev_tc,
|
||||
TC_STRUCT const * const tc,
|
||||
PmCartesian const * const acc_bound,
|
||||
PmCartesian const * const vel_bound,
|
||||
double maxFeedScale);
|
||||
|
||||
int blendInit3FromArcArc(BlendGeom3 * const geom, BlendParameters * const param,
|
||||
TC_STRUCT const * const prev_tc,
|
||||
TC_STRUCT const * const tc,
|
||||
PmCartesian const * const acc_bound,
|
||||
PmCartesian const * const vel_bound,
|
||||
double maxFeedScale);
|
||||
|
||||
int blendArcArcPostProcess(BlendPoints3 * const points, BlendPoints3 const * const points_in,
|
||||
BlendParameters * const param, BlendGeom3 const * const geom,
|
||||
PmCircle const * const circ1, PmCircle const * const circ2);
|
||||
|
||||
int blendLineArcPostProcess(BlendPoints3 * const points, BlendPoints3 const * const points_in,
|
||||
BlendParameters * const param, BlendGeom3 const * const geom,
|
||||
PmCartLine const * const line1, PmCircle const * const circ2);
|
||||
|
||||
int blendArcLinePostProcess(BlendPoints3 * const points, BlendPoints3 const * const points_in,
|
||||
BlendParameters * const param, BlendGeom3 const * const geom,
|
||||
PmCircle const * const circ1, PmCartLine const * const line2);
|
||||
|
||||
int arcFromBlendPoints3(SphericalArc * const arc, BlendPoints3 const * const points,
|
||||
BlendGeom3 const * const geom, BlendParameters const * const param);
|
||||
|
||||
//Not implemented yet
|
||||
int blendGeom3Print(BlendGeom3 const * const geom);
|
||||
int blendParamPrint(BlendParameters const * const param);
|
||||
int blendPoints3Print(BlendPoints3 const * const points);
|
||||
|
||||
double pmCartAbsMax(PmCartesian const * const v);
|
||||
|
||||
int findSpiralArcLengthFit(PmCircle const * const circle,
|
||||
SpiralArcLengthFit * const fit);
|
||||
int pmCircleAngleFromProgress(PmCircle const * const circle,
|
||||
SpiralArcLengthFit const * const fit,
|
||||
double progress,
|
||||
double * const angle);
|
||||
double pmCircleEffectiveMinRadius(const PmCircle *circle);
|
||||
|
||||
static inline double findVPeak(double a_t_max, double distance)
|
||||
{
|
||||
return pmSqrt(a_t_max * distance);
|
||||
}
|
||||
|
||||
|
||||
static inline double findSCurveVPeak(double a_t_max, double j_t_max, double distance)
|
||||
{
|
||||
// Parameter validation
|
||||
if (a_t_max <= 0.0 || j_t_max <= 0.0 || distance <= 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double triangular_v = findVPeak(a_t_max, distance);
|
||||
|
||||
double req_v;
|
||||
int result = findSCurveVSpeed(distance, a_t_max, j_t_max, &req_v);
|
||||
|
||||
// If the S-curve calculation fails, revert to the simpler triangular velocity calculation.
|
||||
if (result != 1) {
|
||||
return triangular_v;
|
||||
}
|
||||
|
||||
// Take the smaller value between the S-curve velocity and the triangular velocity.
|
||||
return fmin(req_v, triangular_v);
|
||||
}
|
||||
#endif
|
||||
138
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/block.c
vendored
Normal file
138
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/block.c
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "block.h"
|
||||
|
||||
static inline double cruckig_profile_total_duration(const CRuckigProfile *p) {
|
||||
return p->t_sum[6] + p->brake.duration + p->accel.duration;
|
||||
}
|
||||
|
||||
static void remove_profile(CRuckigProfile *valid_profiles, size_t *valid_profile_counter, size_t index) {
|
||||
for (size_t i = index; i < *valid_profile_counter - 1; ++i) {
|
||||
valid_profiles[i] = valid_profiles[i + 1];
|
||||
}
|
||||
*valid_profile_counter -= 1;
|
||||
}
|
||||
|
||||
static void interval_from_profiles(CRuckigInterval *iv, const CRuckigProfile *profile_left, const CRuckigProfile *profile_right) {
|
||||
const double left_duration = cruckig_profile_total_duration(profile_left);
|
||||
const double right_duration = cruckig_profile_total_duration(profile_right);
|
||||
if (left_duration < right_duration) {
|
||||
iv->left = left_duration;
|
||||
iv->right = right_duration;
|
||||
iv->profile = *profile_right;
|
||||
} else {
|
||||
iv->left = right_duration;
|
||||
iv->right = left_duration;
|
||||
iv->profile = *profile_left;
|
||||
}
|
||||
iv->valid = true;
|
||||
}
|
||||
|
||||
void cruckig_block_init(CRuckigBlock *block) {
|
||||
cruckig_profile_init(&block->p_min);
|
||||
block->t_min = 0.0;
|
||||
block->a.valid = false;
|
||||
block->b.valid = false;
|
||||
}
|
||||
|
||||
void cruckig_block_set_min_profile(CRuckigBlock *block, const CRuckigProfile *profile) {
|
||||
block->p_min = *profile;
|
||||
block->t_min = cruckig_profile_total_duration(profile);
|
||||
block->a.valid = false;
|
||||
block->b.valid = false;
|
||||
}
|
||||
|
||||
bool cruckig_block_calculate(CRuckigBlock *block, CRuckigProfile *valid_profiles,
|
||||
size_t valid_profile_counter, size_t max_profiles) {
|
||||
(void)max_profiles;
|
||||
|
||||
if (valid_profile_counter == 1) {
|
||||
cruckig_block_set_min_profile(block, &valid_profiles[0]);
|
||||
return true;
|
||||
|
||||
} else if (valid_profile_counter == 2) {
|
||||
if (fabs(valid_profiles[0].t_sum[6] - valid_profiles[1].t_sum[6]) < 8 * DBL_EPSILON) {
|
||||
cruckig_block_set_min_profile(block, &valid_profiles[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* numerical_robust = true */
|
||||
{
|
||||
const size_t idx_min = (valid_profiles[0].t_sum[6] < valid_profiles[1].t_sum[6]) ? 0 : 1;
|
||||
const size_t idx_else_1 = (idx_min + 1) % 2;
|
||||
|
||||
cruckig_block_set_min_profile(block, &valid_profiles[idx_min]);
|
||||
interval_from_profiles(&block->a, &valid_profiles[idx_min], &valid_profiles[idx_else_1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Only happens due to numerical issues */
|
||||
} else if (valid_profile_counter == 4) {
|
||||
/* Find "identical" profiles */
|
||||
if (fabs(valid_profiles[0].t_sum[6] - valid_profiles[1].t_sum[6]) < 32 * DBL_EPSILON && valid_profiles[0].direction != valid_profiles[1].direction) {
|
||||
remove_profile(valid_profiles, &valid_profile_counter, 1);
|
||||
} else if (fabs(valid_profiles[2].t_sum[6] - valid_profiles[3].t_sum[6]) < 256 * DBL_EPSILON && valid_profiles[2].direction != valid_profiles[3].direction) {
|
||||
remove_profile(valid_profiles, &valid_profile_counter, 3);
|
||||
} else if (fabs(valid_profiles[0].t_sum[6] - valid_profiles[3].t_sum[6]) < 256 * DBL_EPSILON && valid_profiles[0].direction != valid_profiles[3].direction) {
|
||||
remove_profile(valid_profiles, &valid_profile_counter, 3);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (valid_profile_counter % 2 == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find index of fastest profile */
|
||||
size_t idx_min = 0;
|
||||
for (size_t i = 1; i < valid_profile_counter; ++i) {
|
||||
if (valid_profiles[i].t_sum[6] < valid_profiles[idx_min].t_sum[6]) {
|
||||
idx_min = i;
|
||||
}
|
||||
}
|
||||
|
||||
cruckig_block_set_min_profile(block, &valid_profiles[idx_min]);
|
||||
|
||||
if (valid_profile_counter == 3) {
|
||||
const size_t idx_else_1 = (idx_min + 1) % 3;
|
||||
const size_t idx_else_2 = (idx_min + 2) % 3;
|
||||
|
||||
interval_from_profiles(&block->a, &valid_profiles[idx_else_1], &valid_profiles[idx_else_2]);
|
||||
return true;
|
||||
|
||||
} else if (valid_profile_counter == 5) {
|
||||
const size_t idx_else_1 = (idx_min + 1) % 5;
|
||||
const size_t idx_else_2 = (idx_min + 2) % 5;
|
||||
const size_t idx_else_3 = (idx_min + 3) % 5;
|
||||
const size_t idx_else_4 = (idx_min + 4) % 5;
|
||||
|
||||
if (valid_profiles[idx_else_1].direction == valid_profiles[idx_else_2].direction) {
|
||||
interval_from_profiles(&block->a, &valid_profiles[idx_else_1], &valid_profiles[idx_else_2]);
|
||||
interval_from_profiles(&block->b, &valid_profiles[idx_else_3], &valid_profiles[idx_else_4]);
|
||||
} else {
|
||||
interval_from_profiles(&block->a, &valid_profiles[idx_else_1], &valid_profiles[idx_else_4]);
|
||||
interval_from_profiles(&block->b, &valid_profiles[idx_else_2], &valid_profiles[idx_else_3]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* cruckig_block_is_blocked is now inlined in block.h */
|
||||
|
||||
const CRuckigProfile* cruckig_block_get_profile(const CRuckigBlock *block, double t) {
|
||||
if (block->b.valid && t >= block->b.right) {
|
||||
return &block->b.profile;
|
||||
}
|
||||
if (block->a.valid && t >= block->a.right) {
|
||||
return &block->a.profile;
|
||||
}
|
||||
return &block->p_min;
|
||||
}
|
||||
43
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/block.h
vendored
Normal file
43
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/block.h
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_BLOCK_H
|
||||
#define CRUCKIG_BLOCK_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
#include "profile.h"
|
||||
|
||||
typedef struct {
|
||||
double left, right;
|
||||
CRuckigProfile profile;
|
||||
bool valid;
|
||||
} CRuckigInterval;
|
||||
|
||||
typedef struct {
|
||||
CRuckigProfile p_min;
|
||||
double t_min;
|
||||
CRuckigInterval a;
|
||||
CRuckigInterval b;
|
||||
} CRuckigBlock;
|
||||
|
||||
void cruckig_block_init(CRuckigBlock *block);
|
||||
void cruckig_block_set_min_profile(CRuckigBlock *block, const CRuckigProfile *profile);
|
||||
|
||||
/* Calculate block from valid profiles. Returns true if successful. */
|
||||
bool cruckig_block_calculate(CRuckigBlock *block, CRuckigProfile *valid_profiles,
|
||||
size_t valid_profile_counter, size_t max_profiles);
|
||||
|
||||
/* Inlined for hot-path performance (called in tight synchronization loop) */
|
||||
CRUCKIG_FORCE_INLINE bool cruckig_block_is_blocked(const CRuckigBlock *block, double t) {
|
||||
return (t < block->t_min)
|
||||
|| (block->a.valid && block->a.left < t && t < block->a.right)
|
||||
|| (block->b.valid && block->b.left < t && t < block->b.right);
|
||||
}
|
||||
|
||||
const CRuckigProfile* cruckig_block_get_profile(const CRuckigBlock *block, double t);
|
||||
|
||||
#endif /* CRUCKIG_BLOCK_H */
|
||||
201
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/brake.c
vendored
Normal file
201
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/brake.c
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "brake.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const double brake_eps = 2.2e-14;
|
||||
|
||||
void cruckig_brake_init(CRuckigBrakeProfile *bp) {
|
||||
bp->duration = 0.0;
|
||||
bp->t[0] = 0.0;
|
||||
bp->t[1] = 0.0;
|
||||
bp->j[0] = 0.0;
|
||||
bp->j[1] = 0.0;
|
||||
bp->a[0] = 0.0;
|
||||
bp->a[1] = 0.0;
|
||||
bp->v[0] = 0.0;
|
||||
bp->v[1] = 0.0;
|
||||
bp->p[0] = 0.0;
|
||||
bp->p[1] = 0.0;
|
||||
}
|
||||
|
||||
static inline double brake_v_at_t(double v0, double a0, double j, double t) {
|
||||
return v0 + t * (a0 + j * t / 2);
|
||||
}
|
||||
|
||||
static inline double brake_v_at_a_zero(double v0, double a0, double j) {
|
||||
return v0 + (a0 * a0) / (2 * j);
|
||||
}
|
||||
|
||||
static void acceleration_brake(CRuckigBrakeProfile *bp, double v0, double a0,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax);
|
||||
static void velocity_brake(CRuckigBrakeProfile *bp, double v0, double a0,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax);
|
||||
|
||||
static void acceleration_brake(CRuckigBrakeProfile *bp, double v0, double a0,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax) {
|
||||
bp->j[0] = -jMax;
|
||||
|
||||
const double t_to_a_max = (a0 - aMax) / jMax;
|
||||
const double t_to_a_zero = a0 / jMax;
|
||||
|
||||
const double v_at_a_max = brake_v_at_t(v0, a0, -jMax, t_to_a_max);
|
||||
const double v_at_a_zero_val = brake_v_at_t(v0, a0, -jMax, t_to_a_zero);
|
||||
|
||||
if ((v_at_a_zero_val > vMax && jMax > 0) || (v_at_a_zero_val < vMax && jMax < 0)) {
|
||||
velocity_brake(bp, v0, a0, vMax, vMin, aMax, aMin, jMax);
|
||||
|
||||
} else if ((v_at_a_max < vMin && jMax > 0) || (v_at_a_max > vMin && jMax < 0)) {
|
||||
const double t_to_v_min = -(v_at_a_max - vMin) / aMax;
|
||||
const double t_to_v_max = -aMax / (2 * jMax) - (v_at_a_max - vMax) / aMax;
|
||||
|
||||
bp->t[0] = t_to_a_max + brake_eps;
|
||||
{
|
||||
double val = t_to_v_min < (t_to_v_max - brake_eps) ? t_to_v_min : (t_to_v_max - brake_eps);
|
||||
bp->t[1] = val > 0.0 ? val : 0.0;
|
||||
}
|
||||
|
||||
} else {
|
||||
bp->t[0] = t_to_a_max + brake_eps;
|
||||
}
|
||||
}
|
||||
|
||||
static void velocity_brake(CRuckigBrakeProfile *bp, double v0, double a0,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax) {
|
||||
(void)aMax;
|
||||
bp->j[0] = -jMax;
|
||||
const double t_to_a_min = (a0 - aMin) / jMax;
|
||||
const double t_to_v_max = a0 / jMax + sqrt(a0 * a0 + 2 * jMax * (v0 - vMax)) / fabs(jMax);
|
||||
const double t_to_v_min = a0 / jMax + sqrt(a0 * a0 / 2 + jMax * (v0 - vMin)) / fabs(jMax);
|
||||
const double t_min_to_v_max = t_to_v_max < t_to_v_min ? t_to_v_max : t_to_v_min;
|
||||
|
||||
if (t_to_a_min < t_min_to_v_max) {
|
||||
const double v_at_a_min = brake_v_at_t(v0, a0, -jMax, t_to_a_min);
|
||||
const double t_to_v_max_with_constant = -(v_at_a_min - vMax) / aMin;
|
||||
const double t_to_v_min_with_constant = aMin / (2 * jMax) - (v_at_a_min - vMin) / aMin;
|
||||
|
||||
bp->t[0] = (t_to_a_min - brake_eps) > 0.0 ? (t_to_a_min - brake_eps) : 0.0;
|
||||
{
|
||||
double val = t_to_v_max_with_constant < t_to_v_min_with_constant ? t_to_v_max_with_constant : t_to_v_min_with_constant;
|
||||
bp->t[1] = val > 0.0 ? val : 0.0;
|
||||
}
|
||||
|
||||
} else {
|
||||
bp->t[0] = (t_min_to_v_max - brake_eps) > 0.0 ? (t_min_to_v_max - brake_eps) : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void cruckig_brake_get_position_brake_trajectory(CRuckigBrakeProfile *bp, double v0, double a0,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax) {
|
||||
bp->t[0] = 0.0;
|
||||
bp->t[1] = 0.0;
|
||||
bp->j[0] = 0.0;
|
||||
bp->j[1] = 0.0;
|
||||
|
||||
if (jMax == 0.0 || aMax == 0.0 || aMin == 0.0) {
|
||||
return; /* Ignore braking for zero-limits */
|
||||
}
|
||||
|
||||
if (a0 > aMax) {
|
||||
acceleration_brake(bp, v0, a0, vMax, vMin, aMax, aMin, jMax);
|
||||
|
||||
} else if (a0 < aMin) {
|
||||
acceleration_brake(bp, v0, a0, vMin, vMax, aMin, aMax, -jMax);
|
||||
|
||||
} else if ((v0 > vMax && brake_v_at_a_zero(v0, a0, -jMax) > vMin) || (a0 > 0 && brake_v_at_a_zero(v0, a0, jMax) > vMax)) {
|
||||
velocity_brake(bp, v0, a0, vMax, vMin, aMax, aMin, jMax);
|
||||
|
||||
} else if ((v0 < vMin && brake_v_at_a_zero(v0, a0, jMax) < vMax) || (a0 < 0 && brake_v_at_a_zero(v0, a0, -jMax) < vMin)) {
|
||||
velocity_brake(bp, v0, a0, vMin, vMax, aMin, aMax, -jMax);
|
||||
}
|
||||
}
|
||||
|
||||
void cruckig_brake_get_second_order_position_brake_trajectory(CRuckigBrakeProfile *bp, double v0,
|
||||
double vMax, double vMin, double aMax, double aMin) {
|
||||
bp->t[0] = 0.0;
|
||||
bp->t[1] = 0.0;
|
||||
bp->j[0] = 0.0;
|
||||
bp->j[1] = 0.0;
|
||||
bp->a[0] = 0.0;
|
||||
bp->a[1] = 0.0;
|
||||
|
||||
if (aMax == 0.0 || aMin == 0.0) {
|
||||
return; /* Ignore braking for zero-limits */
|
||||
}
|
||||
|
||||
if (v0 > vMax) {
|
||||
bp->a[0] = aMin;
|
||||
bp->t[0] = (vMax - v0) / aMin + brake_eps;
|
||||
|
||||
} else if (v0 < vMin) {
|
||||
bp->a[0] = aMax;
|
||||
bp->t[0] = (vMin - v0) / aMax + brake_eps;
|
||||
}
|
||||
}
|
||||
|
||||
void cruckig_brake_get_velocity_brake_trajectory(CRuckigBrakeProfile *bp, double a0,
|
||||
double aMax, double aMin, double jMax) {
|
||||
bp->t[0] = 0.0;
|
||||
bp->t[1] = 0.0;
|
||||
bp->j[0] = 0.0;
|
||||
bp->j[1] = 0.0;
|
||||
|
||||
if (jMax == 0.0) {
|
||||
return; /* Ignore braking for zero-limits */
|
||||
}
|
||||
|
||||
if (a0 > aMax) {
|
||||
bp->j[0] = -jMax;
|
||||
bp->t[0] = (a0 - aMax) / jMax + brake_eps;
|
||||
|
||||
} else if (a0 < aMin) {
|
||||
bp->j[0] = jMax;
|
||||
bp->t[0] = -(a0 - aMin) / jMax + brake_eps;
|
||||
}
|
||||
}
|
||||
|
||||
void cruckig_brake_get_second_order_velocity_brake_trajectory(CRuckigBrakeProfile *bp) {
|
||||
bp->t[0] = 0.0;
|
||||
bp->t[1] = 0.0;
|
||||
bp->j[0] = 0.0;
|
||||
bp->j[1] = 0.0;
|
||||
}
|
||||
|
||||
void cruckig_brake_finalize(CRuckigBrakeProfile *bp, double *ps, double *vs, double *as) {
|
||||
if (bp->t[0] <= 0.0 && bp->t[1] <= 0.0) {
|
||||
bp->duration = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
bp->duration = bp->t[0];
|
||||
bp->p[0] = *ps;
|
||||
bp->v[0] = *vs;
|
||||
bp->a[0] = *as;
|
||||
cruckig_integrate(bp->t[0], *ps, *vs, *as, bp->j[0], ps, vs, as);
|
||||
|
||||
if (bp->t[1] > 0.0) {
|
||||
bp->duration += bp->t[1];
|
||||
bp->p[1] = *ps;
|
||||
bp->v[1] = *vs;
|
||||
bp->a[1] = *as;
|
||||
cruckig_integrate(bp->t[1], *ps, *vs, *as, bp->j[1], ps, vs, as);
|
||||
}
|
||||
}
|
||||
|
||||
void cruckig_brake_finalize_second_order(CRuckigBrakeProfile *bp, double *ps, double *vs, double *as) {
|
||||
if (bp->t[0] <= 0.0) {
|
||||
bp->duration = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
bp->duration = bp->t[0];
|
||||
bp->p[0] = *ps;
|
||||
bp->v[0] = *vs;
|
||||
cruckig_integrate(bp->t[0], *ps, *vs, bp->a[0], 0.0, ps, vs, as);
|
||||
}
|
||||
38
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/brake.h
vendored
Normal file
38
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/brake.h
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_BRAKE_H
|
||||
#define CRUCKIG_BRAKE_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
|
||||
/* Two-phase brake profile */
|
||||
typedef struct {
|
||||
double duration;
|
||||
double t[2];
|
||||
double j[2];
|
||||
double a[2];
|
||||
double v[2];
|
||||
double p[2];
|
||||
} CRuckigBrakeProfile;
|
||||
|
||||
void cruckig_brake_init(CRuckigBrakeProfile *bp);
|
||||
|
||||
/* Calculate brake trajectories */
|
||||
void cruckig_brake_get_position_brake_trajectory(CRuckigBrakeProfile *bp, double v0, double a0,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax);
|
||||
void cruckig_brake_get_second_order_position_brake_trajectory(CRuckigBrakeProfile *bp, double v0,
|
||||
double vMax, double vMin, double aMax, double aMin);
|
||||
void cruckig_brake_get_velocity_brake_trajectory(CRuckigBrakeProfile *bp, double a0,
|
||||
double aMax, double aMin, double jMax);
|
||||
void cruckig_brake_get_second_order_velocity_brake_trajectory(CRuckigBrakeProfile *bp);
|
||||
|
||||
/* Finalize by integrating */
|
||||
void cruckig_brake_finalize(CRuckigBrakeProfile *bp, double *ps, double *vs, double *as);
|
||||
void cruckig_brake_finalize_second_order(CRuckigBrakeProfile *bp, double *ps, double *vs, double *as);
|
||||
|
||||
#endif /* CRUCKIG_BRAKE_H */
|
||||
950
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/calculator.c
vendored
Normal file
950
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/calculator.c
vendored
Normal file
@@ -0,0 +1,950 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#include "calculator.h"
|
||||
#include "position.h"
|
||||
#include "velocity.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
static const double eps = DBL_EPSILON;
|
||||
|
||||
CRuckigCalculator* cruckig_calculator_create(size_t dofs) {
|
||||
CRuckigCalculator *calc = (CRuckigCalculator*)cruckig_calloc(1, sizeof(CRuckigCalculator));
|
||||
if (!calc) return NULL;
|
||||
|
||||
calc->degrees_of_freedom = dofs;
|
||||
|
||||
calc->new_phase_control = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
calc->pd = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
calc->possible_t_syncs = (double*)cruckig_calloc(3 * dofs + 1, sizeof(double));
|
||||
calc->idx = (size_t*)cruckig_calloc(3 * dofs + 1, sizeof(size_t));
|
||||
calc->blocks = (CRuckigBlock*)cruckig_calloc(dofs, sizeof(CRuckigBlock));
|
||||
calc->inp_min_velocity = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
calc->inp_min_acceleration = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
calc->inp_per_dof_control_interface = (CRuckigControlInterface*)cruckig_calloc(dofs, sizeof(CRuckigControlInterface));
|
||||
calc->inp_per_dof_synchronization = (CRuckigSynchronization*)cruckig_calloc(dofs, sizeof(CRuckigSynchronization));
|
||||
calc->segment_input = NULL; /* Created on demand for waypoint calculation */
|
||||
|
||||
if (!calc->new_phase_control || !calc->pd || !calc->possible_t_syncs ||
|
||||
!calc->idx || !calc->blocks || !calc->inp_min_velocity ||
|
||||
!calc->inp_min_acceleration || !calc->inp_per_dof_control_interface ||
|
||||
!calc->inp_per_dof_synchronization) {
|
||||
cruckig_calculator_destroy(calc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < dofs; ++i) {
|
||||
cruckig_block_init(&calc->blocks[i]);
|
||||
}
|
||||
|
||||
return calc;
|
||||
}
|
||||
|
||||
void cruckig_calculator_destroy(CRuckigCalculator *calc) {
|
||||
if (!calc) return;
|
||||
cruckig_free(calc->new_phase_control);
|
||||
cruckig_free(calc->pd);
|
||||
cruckig_free(calc->possible_t_syncs);
|
||||
cruckig_free(calc->idx);
|
||||
cruckig_free(calc->blocks);
|
||||
cruckig_free(calc->inp_min_velocity);
|
||||
cruckig_free(calc->inp_min_acceleration);
|
||||
cruckig_free(calc->inp_per_dof_control_interface);
|
||||
cruckig_free(calc->inp_per_dof_synchronization);
|
||||
cruckig_input_destroy(calc->segment_input);
|
||||
cruckig_free(calc);
|
||||
}
|
||||
|
||||
/* Is the trajectory (in principle) phase synchronizable? */
|
||||
static bool is_input_collinear(CRuckigCalculator *calc,
|
||||
const CRuckigInputParameter *inp,
|
||||
CRuckigDirection limiting_direction,
|
||||
size_t limiting_dof)
|
||||
{
|
||||
const size_t dofs = calc->degrees_of_freedom;
|
||||
|
||||
/* Compute pd = target_position - current_position */
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
calc->pd[dof] = inp->target_position[dof] - inp->current_position[dof];
|
||||
}
|
||||
|
||||
/* Find scale vector and scale DOF */
|
||||
const double *scale_vector = NULL;
|
||||
size_t scale_dof = 0;
|
||||
bool scale_dof_found = false;
|
||||
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (calc->inp_per_dof_synchronization[dof] != CRuckigSyncPhase) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (calc->inp_per_dof_control_interface[dof] == CRuckigPosition && fabs(calc->pd[dof]) > eps) {
|
||||
scale_vector = calc->pd;
|
||||
scale_dof = dof;
|
||||
scale_dof_found = true;
|
||||
break;
|
||||
} else if (fabs(inp->current_velocity[dof]) > eps) {
|
||||
scale_vector = inp->current_velocity;
|
||||
scale_dof = dof;
|
||||
scale_dof_found = true;
|
||||
break;
|
||||
} else if (fabs(inp->current_acceleration[dof]) > eps) {
|
||||
scale_vector = inp->current_acceleration;
|
||||
scale_dof = dof;
|
||||
scale_dof_found = true;
|
||||
break;
|
||||
} else if (fabs(inp->target_velocity[dof]) > eps) {
|
||||
scale_vector = inp->target_velocity;
|
||||
scale_dof = dof;
|
||||
scale_dof_found = true;
|
||||
break;
|
||||
} else if (fabs(inp->target_acceleration[dof]) > eps) {
|
||||
scale_vector = inp->target_acceleration;
|
||||
scale_dof = dof;
|
||||
scale_dof_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scale_dof_found) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const double scale = scale_vector[scale_dof];
|
||||
const double pd_scale = calc->pd[scale_dof] / scale;
|
||||
const double v0_scale = inp->current_velocity[scale_dof] / scale;
|
||||
const double vf_scale = inp->target_velocity[scale_dof] / scale;
|
||||
const double a0_scale = inp->current_acceleration[scale_dof] / scale;
|
||||
const double af_scale = inp->target_acceleration[scale_dof] / scale;
|
||||
|
||||
const double scale_limiting = scale_vector[limiting_dof];
|
||||
double control_limiting;
|
||||
if (isinf(inp->max_jerk[limiting_dof])) {
|
||||
control_limiting = (limiting_direction == DirectionUP)
|
||||
? inp->max_acceleration[limiting_dof]
|
||||
: calc->inp_min_acceleration[limiting_dof];
|
||||
} else {
|
||||
control_limiting = (limiting_direction == DirectionUP)
|
||||
? inp->max_jerk[limiting_dof]
|
||||
: -inp->max_jerk[limiting_dof];
|
||||
}
|
||||
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (calc->inp_per_dof_synchronization[dof] != CRuckigSyncPhase) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const double current_scale = scale_vector[dof];
|
||||
if (
|
||||
(calc->inp_per_dof_control_interface[dof] == CRuckigPosition && fabs(calc->pd[dof] - pd_scale * current_scale) > eps)
|
||||
|| fabs(inp->current_velocity[dof] - v0_scale * current_scale) > eps
|
||||
|| fabs(inp->current_acceleration[dof] - a0_scale * current_scale) > eps
|
||||
|| fabs(inp->target_velocity[dof] - vf_scale * current_scale) > eps
|
||||
|| fabs(inp->target_acceleration[dof] - af_scale * current_scale) > eps
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
calc->new_phase_control[dof] = control_limiting * current_scale / scale_limiting;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Simple insertion sort for index array by values */
|
||||
static void sort_indices(size_t *idx_arr, const double *values, size_t count) {
|
||||
for (size_t i = 1; i < count; ++i) {
|
||||
size_t key = idx_arr[i];
|
||||
double key_val = values[key];
|
||||
size_t j = i;
|
||||
while (j > 0 && values[idx_arr[j - 1]] > key_val) {
|
||||
idx_arr[j] = idx_arr[j - 1];
|
||||
--j;
|
||||
}
|
||||
idx_arr[j] = key;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* synchronize: Find a valid synchronization time.
|
||||
* Returns true if found; sets t_sync, limiting_dof, and updates profiles.
|
||||
*
|
||||
* has_t_min: whether t_min is valid
|
||||
* t_min: minimum duration
|
||||
* limiting_dof_out: set to the limiting DOF index; has_limiting_dof set to true/false
|
||||
*/
|
||||
static bool synchronize(CRuckigCalculator *calc,
|
||||
bool has_t_min, double t_min,
|
||||
double *t_sync,
|
||||
bool *has_limiting_dof, size_t *limiting_dof_out,
|
||||
CRuckigProfile *profiles,
|
||||
bool discrete_duration, double delta_time)
|
||||
{
|
||||
const size_t dofs = calc->degrees_of_freedom;
|
||||
|
||||
/* Fill possible_t_syncs */
|
||||
bool any_interval = false;
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (calc->inp_per_dof_synchronization[dof] == CRuckigSyncNone) {
|
||||
calc->possible_t_syncs[dof] = 0.0;
|
||||
calc->possible_t_syncs[dofs + dof] = INFINITY;
|
||||
calc->possible_t_syncs[2 * dofs + dof] = INFINITY;
|
||||
continue;
|
||||
}
|
||||
|
||||
calc->possible_t_syncs[dof] = calc->blocks[dof].t_min;
|
||||
calc->possible_t_syncs[dofs + dof] = calc->blocks[dof].a.valid
|
||||
? calc->blocks[dof].a.right : INFINITY;
|
||||
calc->possible_t_syncs[2 * dofs + dof] = calc->blocks[dof].b.valid
|
||||
? calc->blocks[dof].b.right : INFINITY;
|
||||
any_interval = any_interval || calc->blocks[dof].a.valid || calc->blocks[dof].b.valid;
|
||||
}
|
||||
calc->possible_t_syncs[3 * dofs] = has_t_min ? t_min : INFINITY;
|
||||
any_interval = any_interval || has_t_min;
|
||||
|
||||
/* Discrete duration rounding */
|
||||
if (discrete_duration) {
|
||||
size_t count = 3 * dofs + 1;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (isinf(calc->possible_t_syncs[i])) continue;
|
||||
double remainder = fmod(calc->possible_t_syncs[i], delta_time);
|
||||
if (remainder > eps) {
|
||||
calc->possible_t_syncs[i] += delta_time - remainder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize and sort indices */
|
||||
size_t idx_end_count = any_interval ? (3 * dofs + 1) : dofs;
|
||||
for (size_t i = 0; i < idx_end_count; ++i) {
|
||||
calc->idx[i] = i;
|
||||
}
|
||||
sort_indices(calc->idx, calc->possible_t_syncs, idx_end_count);
|
||||
|
||||
/* Start at dofs-1 (skip the dofs-1 smallest t_min values since we need ALL dofs at or past their t_min) */
|
||||
size_t start_idx = (dofs >= 1) ? (dofs - 1) : 0;
|
||||
for (size_t iter = start_idx; iter < idx_end_count; ++iter) {
|
||||
size_t i = calc->idx[iter];
|
||||
double possible_t_sync = calc->possible_t_syncs[i];
|
||||
|
||||
/* Check if any DOF is blocked */
|
||||
bool is_blocked = false;
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (calc->inp_per_dof_synchronization[dof] == CRuckigSyncNone) {
|
||||
continue;
|
||||
}
|
||||
if (cruckig_block_is_blocked(&calc->blocks[dof], possible_t_sync)) {
|
||||
is_blocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double t_min_or_zero = has_t_min ? t_min : 0.0;
|
||||
if (is_blocked || possible_t_sync < t_min_or_zero || isinf(possible_t_sync)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*t_sync = possible_t_sync;
|
||||
|
||||
if (i == 3 * dofs) {
|
||||
/* Optional t_min was the winning candidate */
|
||||
*has_limiting_dof = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Determine which DOF and which block part */
|
||||
size_t quot = i / dofs;
|
||||
size_t rem = i % dofs;
|
||||
*limiting_dof_out = rem;
|
||||
*has_limiting_dof = true;
|
||||
|
||||
switch (quot) {
|
||||
case 0:
|
||||
profiles[rem] = calc->blocks[rem].p_min;
|
||||
break;
|
||||
case 1:
|
||||
profiles[rem] = calc->blocks[rem].a.profile;
|
||||
break;
|
||||
case 2:
|
||||
profiles[rem] = calc->blocks[rem].b.profile;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CRUCKIG_HOT
|
||||
/*
|
||||
* Find the optimal profile for a single DOF (Step 1).
|
||||
* Separated to keep large Step1 structs (~3.6KB) off the main function's stack,
|
||||
* which matters for the kernel's limited stack size.
|
||||
*/
|
||||
static bool find_profile_step1(
|
||||
CRuckigCalculator *calc,
|
||||
const CRuckigInputParameter *inp,
|
||||
CRuckigProfile *p,
|
||||
size_t dof)
|
||||
{
|
||||
switch (calc->inp_per_dof_control_interface[dof]) {
|
||||
case CRuckigPosition: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
CRuckigPositionThirdOrderStep1 *step1 = &calc->step1_workspace.pos3_step1;
|
||||
cruckig_pos3_step1_init(step1,
|
||||
p->p[0], p->v[0], p->a[0], p->pf, p->vf, p->af,
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
return cruckig_pos3_step1_get_profile(step1, p, &calc->blocks[dof]);
|
||||
} else if (!isinf(inp->max_acceleration[dof])) {
|
||||
CRuckigPositionSecondOrderStep1 *step1 = &calc->step1_workspace.pos2_step1;
|
||||
cruckig_pos2_step1_init(step1,
|
||||
p->p[0], p->v[0], p->pf, p->vf,
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
return cruckig_pos2_step1_get_profile(step1, p, &calc->blocks[dof]);
|
||||
} else {
|
||||
CRuckigPositionFirstOrderStep1 *step1 = &calc->step1_workspace.pos1_step1;
|
||||
cruckig_pos1_step1_init(step1,
|
||||
p->p[0], p->pf,
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof]);
|
||||
return cruckig_pos1_step1_get_profile(step1, p, &calc->blocks[dof]);
|
||||
}
|
||||
} break;
|
||||
case CRuckigVelocity: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
CRuckigVelocityThirdOrderStep1 *step1 = &calc->step1_workspace.vel3_step1;
|
||||
cruckig_vel3_step1_init(step1,
|
||||
p->v[0], p->a[0], p->vf, p->af,
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
return cruckig_vel3_step1_get_profile(step1, p, &calc->blocks[dof]);
|
||||
} else {
|
||||
CRuckigVelocitySecondOrderStep1 *step1 = &calc->step1_workspace.vel2_step1;
|
||||
cruckig_vel2_step1_init(step1,
|
||||
p->v[0], p->vf,
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
return cruckig_vel2_step1_get_profile(step1, p, &calc->blocks[dof]);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CRuckigResult cruckig_calculator_calculate(CRuckigCalculator *calc,
|
||||
const CRuckigInputParameter *inp,
|
||||
CRuckigTrajectory *traj,
|
||||
double delta_time,
|
||||
bool *was_interrupted)
|
||||
{
|
||||
*was_interrupted = false;
|
||||
const size_t dofs = calc->degrees_of_freedom;
|
||||
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
CRuckigProfile *p = &traj->profiles[dof];
|
||||
|
||||
calc->inp_min_velocity[dof] = inp->min_velocity
|
||||
? inp->min_velocity[dof] : -inp->max_velocity[dof];
|
||||
calc->inp_min_acceleration[dof] = inp->min_acceleration
|
||||
? inp->min_acceleration[dof] : -inp->max_acceleration[dof];
|
||||
calc->inp_per_dof_control_interface[dof] = inp->per_dof_control_interface
|
||||
? inp->per_dof_control_interface[dof] : inp->control_interface;
|
||||
calc->inp_per_dof_synchronization[dof] = inp->per_dof_synchronization
|
||||
? inp->per_dof_synchronization[dof] : inp->synchronization;
|
||||
|
||||
if (!inp->enabled[dof]) {
|
||||
p->p[7] = inp->current_position[dof];
|
||||
p->v[7] = inp->current_velocity[dof];
|
||||
p->a[7] = inp->current_acceleration[dof];
|
||||
p->t_sum[6] = 0.0;
|
||||
calc->blocks[dof].t_min = 0.0;
|
||||
calc->blocks[dof].a.valid = false;
|
||||
calc->blocks[dof].b.valid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Calculate brake (if input exceeds or will exceed limits) */
|
||||
switch (calc->inp_per_dof_control_interface[dof]) {
|
||||
case CRuckigPosition: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
cruckig_brake_get_position_brake_trajectory(&p->brake,
|
||||
inp->current_velocity[dof], inp->current_acceleration[dof],
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
} else if (!isinf(inp->max_acceleration[dof])) {
|
||||
cruckig_brake_get_second_order_position_brake_trajectory(&p->brake,
|
||||
inp->current_velocity[dof],
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
}
|
||||
cruckig_profile_set_boundary(p,
|
||||
inp->current_position[dof], inp->current_velocity[dof],
|
||||
inp->current_acceleration[dof],
|
||||
inp->target_position[dof], inp->target_velocity[dof],
|
||||
inp->target_acceleration[dof]);
|
||||
} break;
|
||||
case CRuckigVelocity: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
cruckig_brake_get_velocity_brake_trajectory(&p->brake,
|
||||
inp->current_acceleration[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
} else {
|
||||
cruckig_brake_get_second_order_velocity_brake_trajectory(&p->brake);
|
||||
}
|
||||
cruckig_profile_set_boundary_for_velocity(p,
|
||||
inp->current_position[dof], inp->current_velocity[dof],
|
||||
inp->current_acceleration[dof],
|
||||
inp->target_velocity[dof], inp->target_acceleration[dof]);
|
||||
} break;
|
||||
}
|
||||
|
||||
/* Finalize pre-trajectory */
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
cruckig_brake_finalize(&p->brake, &p->p[0], &p->v[0], &p->a[0]);
|
||||
} else if (!isinf(inp->max_acceleration[dof])) {
|
||||
cruckig_brake_finalize_second_order(&p->brake, &p->p[0], &p->v[0], &p->a[0]);
|
||||
}
|
||||
|
||||
if (!find_profile_step1(calc, inp, p, dof)) {
|
||||
bool has_zero_limits = (inp->max_acceleration[dof] == 0.0 ||
|
||||
calc->inp_min_acceleration[dof] == 0.0 ||
|
||||
inp->max_jerk[dof] == 0.0);
|
||||
if (has_zero_limits) {
|
||||
return CRuckigErrorZeroLimits;
|
||||
} else {
|
||||
return CRuckigErrorExecutionTimeCalculation;
|
||||
}
|
||||
}
|
||||
|
||||
traj->independent_min_durations[dof] = calc->blocks[dof].t_min;
|
||||
}
|
||||
|
||||
const bool discrete_duration = (inp->duration_discretization == CRuckigDiscrete);
|
||||
|
||||
if (dofs == 1 && !inp->has_minimum_duration && !discrete_duration) {
|
||||
traj->duration = calc->blocks[0].t_min;
|
||||
traj->profiles[0] = calc->blocks[0].p_min;
|
||||
traj->cumulative_times[0] = traj->duration;
|
||||
return CRuckigWorking;
|
||||
}
|
||||
|
||||
/* Synchronize */
|
||||
bool has_limiting_dof = false;
|
||||
size_t limiting_dof = 0;
|
||||
bool found_synchronization = synchronize(calc,
|
||||
inp->has_minimum_duration, inp->minimum_duration,
|
||||
&traj->duration, &has_limiting_dof, &limiting_dof,
|
||||
traj->profiles, discrete_duration, delta_time);
|
||||
|
||||
if (!found_synchronization) {
|
||||
bool has_zero_limits = false;
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (inp->max_acceleration[dof] == 0.0 ||
|
||||
calc->inp_min_acceleration[dof] == 0.0 ||
|
||||
inp->max_jerk[dof] == 0.0) {
|
||||
has_zero_limits = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_zero_limits) {
|
||||
return CRuckigErrorZeroLimits;
|
||||
} else {
|
||||
return CRuckigErrorSynchronizationCalculation;
|
||||
}
|
||||
}
|
||||
|
||||
/* None Synchronization */
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (inp->enabled[dof] && calc->inp_per_dof_synchronization[dof] == CRuckigSyncNone) {
|
||||
traj->profiles[dof] = calc->blocks[dof].p_min;
|
||||
if (calc->blocks[dof].t_min > traj->duration) {
|
||||
traj->duration = calc->blocks[dof].t_min;
|
||||
has_limiting_dof = true;
|
||||
limiting_dof = dof;
|
||||
}
|
||||
}
|
||||
}
|
||||
traj->cumulative_times[0] = traj->duration;
|
||||
|
||||
/* Check maximal duration */
|
||||
if (traj->duration > 7.6e3) {
|
||||
return CRuckigErrorTrajectoryDuration;
|
||||
}
|
||||
|
||||
if (traj->duration == 0.0) {
|
||||
/* Copy all profiles for end state */
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
traj->profiles[dof] = calc->blocks[dof].p_min;
|
||||
}
|
||||
return CRuckigWorking;
|
||||
}
|
||||
|
||||
/* Check if all synchronizations are None */
|
||||
if (!discrete_duration) {
|
||||
bool all_none = true;
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (calc->inp_per_dof_synchronization[dof] != CRuckigSyncNone) {
|
||||
all_none = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_none) {
|
||||
return CRuckigWorking;
|
||||
}
|
||||
}
|
||||
|
||||
/* Phase Synchronization */
|
||||
if (has_limiting_dof) {
|
||||
bool any_phase = false;
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (calc->inp_per_dof_synchronization[dof] == CRuckigSyncPhase) {
|
||||
any_phase = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (any_phase) {
|
||||
const CRuckigProfile *p_limiting = &traj->profiles[limiting_dof];
|
||||
if (is_input_collinear(calc, inp, p_limiting->direction, limiting_dof)) {
|
||||
bool found_time_synchronization = true;
|
||||
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (!inp->enabled[dof] || dof == limiting_dof ||
|
||||
calc->inp_per_dof_synchronization[dof] != CRuckigSyncPhase) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CRuckigProfile *p = &traj->profiles[dof];
|
||||
double t_profile = traj->duration - p->brake.duration - p->accel.duration;
|
||||
|
||||
/* Copy timing information from limiting DOF */
|
||||
memcpy(p->t, p_limiting->t, sizeof(p->t));
|
||||
p->control_signs = p_limiting->control_signs;
|
||||
|
||||
switch (calc->inp_per_dof_control_interface[dof]) {
|
||||
case CRuckigPosition: {
|
||||
switch (p->control_signs) {
|
||||
case ControlSignsUDDU: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
found_time_synchronization &= cruckig_profile_check_with_timing_full(p,
|
||||
ControlSignsUDDU, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
} else if (!isinf(inp->max_acceleration[dof])) {
|
||||
found_time_synchronization &= cruckig_profile_check_for_second_order_with_timing_full(p,
|
||||
ControlSignsUDDU, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
-calc->new_phase_control[dof],
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
} else {
|
||||
found_time_synchronization &= cruckig_profile_check_for_first_order_with_timing_full(p,
|
||||
ControlSignsUDDU, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof]);
|
||||
}
|
||||
} break;
|
||||
case ControlSignsUDUD: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
found_time_synchronization &= cruckig_profile_check_with_timing_full(p,
|
||||
ControlSignsUDUD, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
} else {
|
||||
found_time_synchronization &= cruckig_profile_check_for_second_order_with_timing_full(p,
|
||||
ControlSignsUDUD, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
-calc->new_phase_control[dof],
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
case CRuckigVelocity: {
|
||||
switch (p->control_signs) {
|
||||
case ControlSignsUDDU: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
found_time_synchronization &= cruckig_profile_check_for_velocity_with_timing_full(p,
|
||||
ControlSignsUDDU, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
} else {
|
||||
found_time_synchronization &= cruckig_profile_check_for_second_order_velocity_with_timing_full(p,
|
||||
ControlSignsUDDU, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
}
|
||||
} break;
|
||||
case ControlSignsUDUD: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
found_time_synchronization &= cruckig_profile_check_for_velocity_with_timing_full(p,
|
||||
ControlSignsUDUD, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
} else {
|
||||
found_time_synchronization &= cruckig_profile_check_for_second_order_velocity_with_timing_full(p,
|
||||
ControlSignsUDUD, ReachedLimitsNONE,
|
||||
t_profile, calc->new_phase_control[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
p->limits = p_limiting->limits; /* After check method call */
|
||||
}
|
||||
|
||||
if (found_time_synchronization) {
|
||||
bool all_phase_or_none = true;
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (calc->inp_per_dof_synchronization[dof] != CRuckigSyncPhase &&
|
||||
calc->inp_per_dof_synchronization[dof] != CRuckigSyncNone) {
|
||||
all_phase_or_none = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_phase_or_none) {
|
||||
return CRuckigWorking;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Time Synchronization (Step 2) */
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
bool skip_synchronization = ((has_limiting_dof && dof == limiting_dof) ||
|
||||
calc->inp_per_dof_synchronization[dof] == CRuckigSyncNone) &&
|
||||
!discrete_duration;
|
||||
if (!inp->enabled[dof] || skip_synchronization) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CRuckigProfile *p = &traj->profiles[dof];
|
||||
double t_profile = traj->duration - p->brake.duration - p->accel.duration;
|
||||
|
||||
if (calc->inp_per_dof_synchronization[dof] == CRuckigSyncTimeIfNecessary &&
|
||||
fabs(inp->target_velocity[dof]) < eps &&
|
||||
fabs(inp->target_acceleration[dof]) < eps) {
|
||||
*p = calc->blocks[dof].p_min;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if the final time corresponds to an extremal profile from step 1 */
|
||||
if (fabs(t_profile - calc->blocks[dof].t_min) < 2 * eps) {
|
||||
*p = calc->blocks[dof].p_min;
|
||||
continue;
|
||||
} else if (calc->blocks[dof].a.valid && fabs(t_profile - calc->blocks[dof].a.right) < 2 * eps) {
|
||||
*p = calc->blocks[dof].a.profile;
|
||||
continue;
|
||||
} else if (calc->blocks[dof].b.valid && fabs(t_profile - calc->blocks[dof].b.right) < 2 * eps) {
|
||||
*p = calc->blocks[dof].b.profile;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool found_time_synchronization = false;
|
||||
switch (calc->inp_per_dof_control_interface[dof]) {
|
||||
case CRuckigPosition: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
CRuckigPositionThirdOrderStep2 step2;
|
||||
cruckig_pos3_step2_init(&step2,
|
||||
t_profile, p->p[0], p->v[0], p->a[0], p->pf, p->vf, p->af,
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
found_time_synchronization = cruckig_pos3_step2_get_profile(&step2, p);
|
||||
} else if (!isinf(inp->max_acceleration[dof])) {
|
||||
CRuckigPositionSecondOrderStep2 step2;
|
||||
cruckig_pos2_step2_init(&step2,
|
||||
t_profile, p->p[0], p->v[0], p->pf, p->vf,
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof],
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
found_time_synchronization = cruckig_pos2_step2_get_profile(&step2, p);
|
||||
} else {
|
||||
CRuckigPositionFirstOrderStep2 step2;
|
||||
cruckig_pos1_step2_init(&step2,
|
||||
t_profile, p->p[0], p->pf,
|
||||
inp->max_velocity[dof], calc->inp_min_velocity[dof]);
|
||||
found_time_synchronization = cruckig_pos1_step2_get_profile(&step2, p);
|
||||
}
|
||||
} break;
|
||||
case CRuckigVelocity: {
|
||||
if (!isinf(inp->max_jerk[dof])) {
|
||||
CRuckigVelocityThirdOrderStep2 step2;
|
||||
cruckig_vel3_step2_init(&step2,
|
||||
t_profile, p->v[0], p->a[0], p->vf, p->af,
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof],
|
||||
inp->max_jerk[dof]);
|
||||
found_time_synchronization = cruckig_vel3_step2_get_profile(&step2, p);
|
||||
} else {
|
||||
CRuckigVelocitySecondOrderStep2 step2;
|
||||
cruckig_vel2_step2_init(&step2,
|
||||
t_profile, p->v[0], p->vf,
|
||||
inp->max_acceleration[dof], calc->inp_min_acceleration[dof]);
|
||||
found_time_synchronization = cruckig_vel2_step2_get_profile(&step2, p);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
if (!found_time_synchronization) {
|
||||
return CRuckigErrorSynchronizationCalculation;
|
||||
}
|
||||
}
|
||||
|
||||
return CRuckigWorking;
|
||||
}
|
||||
|
||||
/*
|
||||
* Multi-segment waypoint calculation.
|
||||
*
|
||||
* Strategy: sequential segment planning. For each segment between consecutive
|
||||
* waypoints, use the existing single-segment planner. The end state of segment i
|
||||
* becomes the start state of segment i+1. At intermediate waypoints, velocity
|
||||
* and acceleration pass through continuously (zero target velocity at waypoints
|
||||
* for robustness, with option to optimize).
|
||||
*/
|
||||
CRuckigResult cruckig_calculator_calculate_waypoints(CRuckigCalculator *calc,
|
||||
const CRuckigInputParameter *inp,
|
||||
CRuckigTrajectory *traj,
|
||||
double delta_time,
|
||||
bool *was_interrupted)
|
||||
{
|
||||
const size_t dofs = calc->degrees_of_freedom;
|
||||
const size_t nwp = inp->num_intermediate_waypoints;
|
||||
const size_t nsec = nwp + 1; /* Number of sections */
|
||||
|
||||
/* Resize trajectory for multi-section */
|
||||
if (!cruckig_trajectory_resize(traj, nsec)) {
|
||||
return CRuckigError;
|
||||
}
|
||||
|
||||
/* Create reusable segment input if needed */
|
||||
if (!calc->segment_input) {
|
||||
calc->segment_input = cruckig_input_create(dofs);
|
||||
if (!calc->segment_input) return CRuckigError;
|
||||
}
|
||||
|
||||
CRuckigInputParameter *seg = calc->segment_input;
|
||||
|
||||
/* Build a temporary single-section trajectory for each segment */
|
||||
CRuckigTrajectory *seg_traj = cruckig_trajectory_create(dofs);
|
||||
if (!seg_traj) return CRuckigError;
|
||||
|
||||
double cumulative_time = 0.0;
|
||||
CRuckigResult final_result = CRuckigWorking;
|
||||
|
||||
for (size_t s = 0; s < nsec; ++s) {
|
||||
/* Set segment input: copy global settings */
|
||||
seg->control_interface = CRuckigPosition;
|
||||
seg->synchronization = inp->synchronization;
|
||||
seg->duration_discretization = CRuckigContinuous;
|
||||
seg->has_minimum_duration = false;
|
||||
|
||||
/* Per-section minimum duration */
|
||||
if (inp->per_section_minimum_duration) {
|
||||
seg->minimum_duration = inp->per_section_minimum_duration[s];
|
||||
seg->has_minimum_duration = true;
|
||||
}
|
||||
|
||||
/* Set start state */
|
||||
if (s == 0) {
|
||||
/* First segment starts from input current state */
|
||||
memcpy(seg->current_position, inp->current_position, dofs * sizeof(double));
|
||||
memcpy(seg->current_velocity, inp->current_velocity, dofs * sizeof(double));
|
||||
memcpy(seg->current_acceleration, inp->current_acceleration, dofs * sizeof(double));
|
||||
}
|
||||
/* else: current state was set by previous iteration's end state */
|
||||
|
||||
/* Set target state */
|
||||
if (s < nwp) {
|
||||
/* Target is the next intermediate waypoint */
|
||||
const double *wp = inp->intermediate_positions + s * dofs;
|
||||
memcpy(seg->target_position, wp, dofs * sizeof(double));
|
||||
/* Zero velocity/acceleration at intermediate waypoints */
|
||||
memset(seg->target_velocity, 0, dofs * sizeof(double));
|
||||
memset(seg->target_acceleration, 0, dofs * sizeof(double));
|
||||
} else {
|
||||
/* Last segment targets the final position */
|
||||
memcpy(seg->target_position, inp->target_position, dofs * sizeof(double));
|
||||
memcpy(seg->target_velocity, inp->target_velocity, dofs * sizeof(double));
|
||||
memcpy(seg->target_acceleration, inp->target_acceleration, dofs * sizeof(double));
|
||||
}
|
||||
|
||||
/* Set kinematic constraints (per-section or global) */
|
||||
if (inp->per_section_max_velocity) {
|
||||
memcpy(seg->max_velocity, inp->per_section_max_velocity + s * dofs, dofs * sizeof(double));
|
||||
} else {
|
||||
memcpy(seg->max_velocity, inp->max_velocity, dofs * sizeof(double));
|
||||
}
|
||||
if (inp->per_section_max_acceleration) {
|
||||
memcpy(seg->max_acceleration, inp->per_section_max_acceleration + s * dofs, dofs * sizeof(double));
|
||||
} else {
|
||||
memcpy(seg->max_acceleration, inp->max_acceleration, dofs * sizeof(double));
|
||||
}
|
||||
if (inp->per_section_max_jerk) {
|
||||
memcpy(seg->max_jerk, inp->per_section_max_jerk + s * dofs, dofs * sizeof(double));
|
||||
} else {
|
||||
memcpy(seg->max_jerk, inp->max_jerk, dofs * sizeof(double));
|
||||
}
|
||||
|
||||
/* Optional min limits */
|
||||
if (inp->per_section_min_velocity) {
|
||||
if (!seg->min_velocity) seg->min_velocity = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
memcpy(seg->min_velocity, inp->per_section_min_velocity + s * dofs, dofs * sizeof(double));
|
||||
} else if (inp->min_velocity) {
|
||||
if (!seg->min_velocity) seg->min_velocity = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
memcpy(seg->min_velocity, inp->min_velocity, dofs * sizeof(double));
|
||||
} else {
|
||||
cruckig_free(seg->min_velocity);
|
||||
seg->min_velocity = NULL;
|
||||
}
|
||||
|
||||
if (inp->per_section_min_acceleration) {
|
||||
if (!seg->min_acceleration) seg->min_acceleration = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
memcpy(seg->min_acceleration, inp->per_section_min_acceleration + s * dofs, dofs * sizeof(double));
|
||||
} else if (inp->min_acceleration) {
|
||||
if (!seg->min_acceleration) seg->min_acceleration = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
memcpy(seg->min_acceleration, inp->min_acceleration, dofs * sizeof(double));
|
||||
} else {
|
||||
cruckig_free(seg->min_acceleration);
|
||||
seg->min_acceleration = NULL;
|
||||
}
|
||||
|
||||
/* Enable all DOFs for segment */
|
||||
for (size_t d = 0; d < dofs; ++d) seg->enabled[d] = true;
|
||||
|
||||
/* Calculate this segment */
|
||||
bool seg_interrupted = false;
|
||||
CRuckigResult seg_result = cruckig_calculator_calculate(calc, seg, seg_traj,
|
||||
delta_time, &seg_interrupted);
|
||||
if (seg_result != CRuckigWorking) {
|
||||
cruckig_trajectory_destroy(seg_traj);
|
||||
*was_interrupted = false;
|
||||
return seg_result;
|
||||
}
|
||||
|
||||
/* Copy segment profiles into the multi-section trajectory */
|
||||
double seg_duration = cruckig_trajectory_get_duration(seg_traj);
|
||||
cumulative_time += seg_duration;
|
||||
traj->cumulative_times[s] = cumulative_time;
|
||||
|
||||
for (size_t d = 0; d < dofs; ++d) {
|
||||
traj->profiles[s * dofs + d] = seg_traj->profiles[d];
|
||||
if (s == 0) {
|
||||
traj->independent_min_durations[d] = seg_traj->independent_min_durations[d];
|
||||
}
|
||||
}
|
||||
|
||||
/* Set next segment's start state from this segment's end state */
|
||||
if (s < nsec - 1) {
|
||||
for (size_t d = 0; d < dofs; ++d) {
|
||||
const CRuckigProfile *p = &seg_traj->profiles[d];
|
||||
seg->current_position[d] = p->p[7];
|
||||
seg->current_velocity[d] = p->v[7];
|
||||
seg->current_acceleration[d] = p->a[7];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
traj->duration = cumulative_time;
|
||||
cruckig_trajectory_destroy(seg_traj);
|
||||
|
||||
/* Position limits check */
|
||||
if (inp->max_position || inp->min_position ||
|
||||
inp->per_section_max_position || inp->per_section_min_position)
|
||||
{
|
||||
/* Sample trajectory and check bounds */
|
||||
double *pos = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
double *vel = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
double *acc = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
size_t sec;
|
||||
|
||||
bool violated = false;
|
||||
/* Check at fine time steps */
|
||||
double dt_check = (delta_time > 0.0) ? delta_time : 0.001;
|
||||
for (double t = 0.0; t <= cumulative_time && !violated; t += dt_check) {
|
||||
cruckig_trajectory_at_time(traj, t, pos, vel, acc, NULL, &sec);
|
||||
|
||||
for (size_t d = 0; d < dofs; ++d) {
|
||||
double p_max = INFINITY, p_min = -INFINITY;
|
||||
|
||||
if (inp->max_position) p_max = inp->max_position[d];
|
||||
if (inp->min_position) p_min = inp->min_position[d];
|
||||
|
||||
/* Per-section position limits */
|
||||
if (sec < nsec) {
|
||||
if (inp->per_section_max_position) {
|
||||
double sec_max = inp->per_section_max_position[sec * dofs + d];
|
||||
if (sec_max < p_max) p_max = sec_max;
|
||||
}
|
||||
if (inp->per_section_min_position) {
|
||||
double sec_min = inp->per_section_min_position[sec * dofs + d];
|
||||
if (sec_min > p_min) p_min = sec_min;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos[d] > p_max + 1e-8 || pos[d] < p_min - 1e-8) {
|
||||
violated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Also check position extrema */
|
||||
if (!violated) {
|
||||
cruckig_trajectory_get_position_extrema(traj);
|
||||
for (size_t d = 0; d < dofs; ++d) {
|
||||
double p_max = INFINITY, p_min = -INFINITY;
|
||||
if (inp->max_position) p_max = inp->max_position[d];
|
||||
if (inp->min_position) p_min = inp->min_position[d];
|
||||
|
||||
if (traj->position_extrema[d].max > p_max + 1e-8 ||
|
||||
traj->position_extrema[d].min < p_min - 1e-8) {
|
||||
violated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cruckig_free(pos);
|
||||
cruckig_free(vel);
|
||||
cruckig_free(acc);
|
||||
|
||||
if (violated) {
|
||||
final_result = CRuckigErrorPositionalLimits;
|
||||
}
|
||||
}
|
||||
|
||||
*was_interrupted = false;
|
||||
return final_result;
|
||||
}
|
||||
|
||||
CRuckigResult cruckig_calculator_continue(CRuckigCalculator *calc,
|
||||
const CRuckigInputParameter *inp,
|
||||
CRuckigTrajectory *traj,
|
||||
double delta_time,
|
||||
bool *was_interrupted)
|
||||
{
|
||||
/* For now, continue_calculation simply re-runs the full calculation.
|
||||
* A future optimization could resume from partial state. */
|
||||
if (inp->num_intermediate_waypoints > 0 && inp->control_interface == CRuckigPosition) {
|
||||
return cruckig_calculator_calculate_waypoints(calc, inp, traj, delta_time, was_interrupted);
|
||||
}
|
||||
return cruckig_calculator_calculate(calc, inp, traj, delta_time, was_interrupted);
|
||||
}
|
||||
71
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/calculator.h
vendored
Normal file
71
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/calculator.h
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_CALCULATOR_H
|
||||
#define CRUCKIG_CALCULATOR_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
#include "result.h"
|
||||
#include "block.h"
|
||||
#include "input_parameter.h"
|
||||
#include "trajectory.h"
|
||||
#include "position.h"
|
||||
#include "velocity.h"
|
||||
|
||||
typedef struct {
|
||||
size_t degrees_of_freedom;
|
||||
|
||||
double *new_phase_control;
|
||||
double *pd;
|
||||
double *possible_t_syncs;
|
||||
size_t *idx;
|
||||
|
||||
CRuckigBlock *blocks;
|
||||
double *inp_min_velocity;
|
||||
double *inp_min_acceleration;
|
||||
CRuckigControlInterface *inp_per_dof_control_interface;
|
||||
CRuckigSynchronization *inp_per_dof_synchronization;
|
||||
|
||||
/* Scratch space for waypoint calculation */
|
||||
CRuckigInputParameter *segment_input; /* Reusable per-segment input */
|
||||
|
||||
/* Step1 workspace: kept off the stack to stay within kernel frame limits.
|
||||
* Only one Step1 type is active at a time, so a union suffices. */
|
||||
union {
|
||||
CRuckigPositionThirdOrderStep1 pos3_step1;
|
||||
CRuckigPositionSecondOrderStep1 pos2_step1;
|
||||
CRuckigPositionFirstOrderStep1 pos1_step1;
|
||||
CRuckigVelocityThirdOrderStep1 vel3_step1;
|
||||
CRuckigVelocitySecondOrderStep1 vel2_step1;
|
||||
} step1_workspace;
|
||||
} CRuckigCalculator;
|
||||
|
||||
CRuckigCalculator* cruckig_calculator_create(size_t dofs);
|
||||
void cruckig_calculator_destroy(CRuckigCalculator *calc);
|
||||
|
||||
/* Single-segment calculation (existing, backward compatible) */
|
||||
CRuckigResult cruckig_calculator_calculate(CRuckigCalculator *calc,
|
||||
const CRuckigInputParameter *inp,
|
||||
CRuckigTrajectory *traj,
|
||||
double delta_time,
|
||||
bool *was_interrupted);
|
||||
|
||||
/* Multi-segment waypoint calculation */
|
||||
CRuckigResult cruckig_calculator_calculate_waypoints(CRuckigCalculator *calc,
|
||||
const CRuckigInputParameter *inp,
|
||||
CRuckigTrajectory *traj,
|
||||
double delta_time,
|
||||
bool *was_interrupted);
|
||||
|
||||
/* Continue an interrupted calculation */
|
||||
CRuckigResult cruckig_calculator_continue(CRuckigCalculator *calc,
|
||||
const CRuckigInputParameter *inp,
|
||||
CRuckigTrajectory *traj,
|
||||
double delta_time,
|
||||
bool *was_interrupted);
|
||||
|
||||
#endif /* CRUCKIG_CALCULATOR_H */
|
||||
173
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/cruckig.c
vendored
Normal file
173
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/cruckig.c
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "cruckig.h"
|
||||
|
||||
|
||||
static CRuckig* cruckig_create_internal(size_t dofs, double delta_time, size_t max_waypoints) {
|
||||
CRuckig *r = (CRuckig*)cruckig_calloc(1, sizeof(CRuckig));
|
||||
if (!r) return NULL;
|
||||
|
||||
r->degrees_of_freedom = dofs;
|
||||
r->delta_time = delta_time;
|
||||
r->max_number_of_waypoints = max_waypoints;
|
||||
|
||||
r->calculator = cruckig_calculator_create(dofs);
|
||||
if (!r->calculator) {
|
||||
cruckig_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r->current_input = cruckig_input_create(dofs);
|
||||
if (!r->current_input) {
|
||||
cruckig_calculator_destroy(r->calculator);
|
||||
cruckig_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r->current_input_initialized = false;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
CRuckig* cruckig_create(size_t dofs, double delta_time) {
|
||||
return cruckig_create_internal(dofs, delta_time, 0);
|
||||
}
|
||||
|
||||
CRuckig* cruckig_create_waypoints(size_t dofs, double delta_time, size_t max_waypoints) {
|
||||
return cruckig_create_internal(dofs, delta_time, max_waypoints);
|
||||
}
|
||||
|
||||
void cruckig_destroy(CRuckig *r) {
|
||||
if (!r) return;
|
||||
cruckig_calculator_destroy(r->calculator);
|
||||
cruckig_input_destroy(r->current_input);
|
||||
cruckig_free(r);
|
||||
}
|
||||
|
||||
void cruckig_reset(CRuckig *r) {
|
||||
if (!r) return;
|
||||
r->current_input_initialized = false;
|
||||
}
|
||||
|
||||
static inline bool use_waypoints(const CRuckigInputParameter *input) {
|
||||
return input->num_intermediate_waypoints > 0 &&
|
||||
input->control_interface == CRuckigPosition;
|
||||
}
|
||||
|
||||
bool cruckig_validate_input(const CRuckig *r, const CRuckigInputParameter *input,
|
||||
bool check_current_within_limits,
|
||||
bool check_target_within_limits)
|
||||
{
|
||||
if (!r || !input) return false;
|
||||
|
||||
if (!cruckig_input_validate(input, check_current_within_limits, check_target_within_limits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r->delta_time <= 0.0 && input->duration_discretization != CRuckigContinuous) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate waypoint count against max */
|
||||
if (input->num_intermediate_waypoints > r->max_number_of_waypoints &&
|
||||
r->max_number_of_waypoints > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static CRuckigResult dispatch_calculate(CRuckig *r, const CRuckigInputParameter *input,
|
||||
CRuckigTrajectory *trajectory, bool *was_interrupted)
|
||||
{
|
||||
if (use_waypoints(input)) {
|
||||
/* Ensure trajectory has enough capacity */
|
||||
size_t nsec = input->num_intermediate_waypoints + 1;
|
||||
if (!cruckig_trajectory_resize(trajectory, nsec)) {
|
||||
return CRuckigError;
|
||||
}
|
||||
return cruckig_calculator_calculate_waypoints(r->calculator, input, trajectory,
|
||||
r->delta_time, was_interrupted);
|
||||
} else {
|
||||
/* Single-segment: ensure single section */
|
||||
if (trajectory->num_sections != 1) {
|
||||
cruckig_trajectory_resize(trajectory, 1);
|
||||
}
|
||||
return cruckig_calculator_calculate(r->calculator, input, trajectory,
|
||||
r->delta_time, was_interrupted);
|
||||
}
|
||||
}
|
||||
|
||||
CRuckigResult cruckig_calculate(CRuckig *r, const CRuckigInputParameter *input,
|
||||
CRuckigTrajectory *trajectory)
|
||||
{
|
||||
if (!r || !input || !trajectory) return CRuckigError;
|
||||
|
||||
if (!cruckig_validate_input(r, input, false, true)) {
|
||||
return CRuckigErrorInvalidInput;
|
||||
}
|
||||
|
||||
bool was_interrupted = false;
|
||||
return dispatch_calculate(r, input, trajectory, &was_interrupted);
|
||||
}
|
||||
|
||||
static double get_time_us(void) {
|
||||
/* Timing measurement for interrupt budget feature.
|
||||
* Not used by LinuxCNC (only cruckig_update, not cruckig_calculate). */
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
CRUCKIG_HOT
|
||||
CRuckigResult cruckig_update(CRuckig *r, const CRuckigInputParameter *input,
|
||||
CRuckigOutputParameter *output)
|
||||
{
|
||||
if (CRUCKIG_UNLIKELY(!r || !input || !output)) return CRuckigError;
|
||||
|
||||
double start_us = get_time_us();
|
||||
|
||||
output->new_calculation = false;
|
||||
|
||||
CRuckigResult result = CRuckigWorking;
|
||||
if (!r->current_input_initialized || !cruckig_input_is_equal(input, r->current_input)) {
|
||||
if (!cruckig_validate_input(r, input, false, true)) {
|
||||
return CRuckigErrorInvalidInput;
|
||||
}
|
||||
|
||||
result = dispatch_calculate(r, input, output->trajectory,
|
||||
&output->was_calculation_interrupted);
|
||||
if (result != CRuckigWorking && result != CRuckigErrorPositionalLimits) {
|
||||
return result;
|
||||
}
|
||||
|
||||
cruckig_input_copy(r->current_input, input);
|
||||
r->current_input_initialized = true;
|
||||
output->time = 0.0;
|
||||
output->new_section = 0;
|
||||
output->new_calculation = true;
|
||||
}
|
||||
|
||||
size_t old_section = output->new_section;
|
||||
output->time += r->delta_time;
|
||||
cruckig_trajectory_at_time(output->trajectory, output->time,
|
||||
output->new_position, output->new_velocity,
|
||||
output->new_acceleration, output->new_jerk,
|
||||
&output->new_section);
|
||||
output->did_section_change = (output->new_section > old_section);
|
||||
|
||||
double stop_us = get_time_us();
|
||||
output->calculation_duration = stop_us - start_us;
|
||||
|
||||
cruckig_output_pass_to_input(output, r->current_input);
|
||||
|
||||
if (output->time > cruckig_trajectory_get_duration(output->trajectory)) {
|
||||
return CRuckigFinished;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
54
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/cruckig.h
vendored
Normal file
54
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/cruckig.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_CRUCKIG_H
|
||||
#define CRUCKIG_CRUCKIG_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
|
||||
#include "result.h"
|
||||
#include "input_parameter.h"
|
||||
#include "output_parameter.h"
|
||||
#include "trajectory.h"
|
||||
#include "calculator.h"
|
||||
|
||||
/* Main cruckig instance */
|
||||
typedef struct {
|
||||
size_t degrees_of_freedom;
|
||||
double delta_time;
|
||||
size_t max_number_of_waypoints;
|
||||
|
||||
CRuckigCalculator *calculator;
|
||||
CRuckigInputParameter *current_input;
|
||||
bool current_input_initialized;
|
||||
} CRuckig;
|
||||
|
||||
/* Create and destroy (backward compatible: 0 waypoints) */
|
||||
CRuckig* cruckig_create(size_t dofs, double delta_time);
|
||||
|
||||
/* Create with waypoint support */
|
||||
CRuckig* cruckig_create_waypoints(size_t dofs, double delta_time, size_t max_waypoints);
|
||||
|
||||
void cruckig_destroy(CRuckig *r);
|
||||
|
||||
/* Reset (force recalculation on next update) */
|
||||
void cruckig_reset(CRuckig *r);
|
||||
|
||||
/* Calculate trajectory (offline, auto-dispatches to waypoint calculator if needed) */
|
||||
CRuckigResult cruckig_calculate(CRuckig *r, const CRuckigInputParameter *input,
|
||||
CRuckigTrajectory *trajectory);
|
||||
|
||||
/* Update (online, call every delta_time) */
|
||||
CRuckigResult cruckig_update(CRuckig *r, const CRuckigInputParameter *input,
|
||||
CRuckigOutputParameter *output);
|
||||
|
||||
/* Validate input */
|
||||
bool cruckig_validate_input(const CRuckig *r, const CRuckigInputParameter *input,
|
||||
bool check_current_within_limits,
|
||||
bool check_target_within_limits);
|
||||
|
||||
#endif /* CRUCKIG_CRUCKIG_H */
|
||||
55
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/cruckig_internal.h
vendored
Normal file
55
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/cruckig_internal.h
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* cruckig_internal.h - Internal header for cruckig
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*
|
||||
* Provides RTAPI-portable types, memory allocation, math, string
|
||||
* functions, and compiler hint macros for cruckig internals.
|
||||
*
|
||||
* All cruckig headers should include this as their first include.
|
||||
* C files should NOT include this directly -- they get it through
|
||||
* their corresponding header.
|
||||
*/
|
||||
#ifndef CRUCKIG_CRUCKIG_INTERNAL_H
|
||||
#define CRUCKIG_CRUCKIG_INTERNAL_H
|
||||
|
||||
/* RTAPI provides bool, size_t, math, string, and memory allocation
|
||||
* portably across userspace and kernel builds. */
|
||||
#include <rtapi.h>
|
||||
#include <rtapi_bool.h>
|
||||
#include <rtapi_math.h>
|
||||
#include <rtapi_string.h>
|
||||
#include <rtapi_slab.h>
|
||||
#include <float.h>
|
||||
|
||||
/* INFINITY: not provided by rtapi_math.h in kernel space */
|
||||
#ifndef INFINITY
|
||||
#define INFINITY __builtin_inf()
|
||||
#endif
|
||||
|
||||
/* Memory allocation: always use rtapi_slab wrappers */
|
||||
#define cruckig_malloc(sz) rtapi_kmalloc(sz, RTAPI_GFP_KERNEL)
|
||||
#define cruckig_calloc(n, sz) rtapi_kzalloc((n) * (sz), RTAPI_GFP_KERNEL)
|
||||
#define cruckig_realloc(p, sz) rtapi_krealloc(p, sz, RTAPI_GFP_KERNEL)
|
||||
#define cruckig_free(p) rtapi_kfree(p)
|
||||
|
||||
/* Branch prediction hints */
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define CRUCKIG_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define CRUCKIG_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# define CRUCKIG_FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# define CRUCKIG_HOT __attribute__((hot))
|
||||
# define CRUCKIG_RESTRICT __restrict__
|
||||
# define CRUCKIG_PREFETCH(addr) __builtin_prefetch(addr, 0, 1)
|
||||
#else
|
||||
# define CRUCKIG_LIKELY(x) (x)
|
||||
# define CRUCKIG_UNLIKELY(x) (x)
|
||||
# define CRUCKIG_FORCE_INLINE static inline
|
||||
# define CRUCKIG_HOT
|
||||
# define CRUCKIG_RESTRICT restrict
|
||||
# define CRUCKIG_PREFETCH(addr) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif /* CRUCKIG_CRUCKIG_INTERNAL_H */
|
||||
408
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/input_parameter.c
vendored
Normal file
408
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/input_parameter.c
vendored
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#include "input_parameter.h"
|
||||
|
||||
|
||||
static double v_at_a_zero(double v0, double a0, double j) {
|
||||
return v0 + (a0 * a0) / (2.0 * j);
|
||||
}
|
||||
|
||||
CRuckigInputParameter* cruckig_input_create(size_t dofs) {
|
||||
CRuckigInputParameter *inp = (CRuckigInputParameter*)cruckig_calloc(1, sizeof(CRuckigInputParameter));
|
||||
if (!inp) return NULL;
|
||||
|
||||
inp->degrees_of_freedom = dofs;
|
||||
inp->control_interface = CRuckigPosition;
|
||||
inp->synchronization = CRuckigSyncTime;
|
||||
inp->duration_discretization = CRuckigContinuous;
|
||||
|
||||
inp->current_position = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
inp->current_velocity = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
inp->current_acceleration = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
inp->target_position = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
inp->target_velocity = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
inp->target_acceleration = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
inp->max_velocity = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
inp->max_acceleration = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
inp->max_jerk = (double*)cruckig_malloc(dofs * sizeof(double));
|
||||
inp->enabled = (bool*)cruckig_malloc(dofs * sizeof(bool));
|
||||
|
||||
if (!inp->current_position || !inp->current_velocity || !inp->current_acceleration ||
|
||||
!inp->target_position || !inp->target_velocity || !inp->target_acceleration ||
|
||||
!inp->max_velocity || !inp->max_acceleration || !inp->max_jerk || !inp->enabled) {
|
||||
cruckig_input_destroy(inp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize defaults matching C++ */
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
inp->max_acceleration[dof] = INFINITY;
|
||||
inp->max_jerk[dof] = INFINITY;
|
||||
inp->enabled[dof] = true;
|
||||
}
|
||||
|
||||
inp->min_velocity = NULL;
|
||||
inp->min_acceleration = NULL;
|
||||
inp->per_dof_control_interface = NULL;
|
||||
inp->per_dof_synchronization = NULL;
|
||||
inp->minimum_duration = -1.0;
|
||||
inp->has_minimum_duration = false;
|
||||
|
||||
/* Pro fields: initialize to defaults */
|
||||
inp->intermediate_positions = NULL;
|
||||
inp->num_intermediate_waypoints = 0;
|
||||
inp->per_section_max_velocity = NULL;
|
||||
inp->per_section_max_acceleration = NULL;
|
||||
inp->per_section_max_jerk = NULL;
|
||||
inp->per_section_min_velocity = NULL;
|
||||
inp->per_section_min_acceleration = NULL;
|
||||
inp->per_section_max_position = NULL;
|
||||
inp->per_section_min_position = NULL;
|
||||
inp->max_position = NULL;
|
||||
inp->min_position = NULL;
|
||||
inp->per_section_minimum_duration = NULL;
|
||||
inp->interrupt_calculation_duration = 0.0;
|
||||
|
||||
return inp;
|
||||
}
|
||||
|
||||
void cruckig_input_destroy(CRuckigInputParameter *inp) {
|
||||
if (!inp) return;
|
||||
cruckig_free(inp->current_position);
|
||||
cruckig_free(inp->current_velocity);
|
||||
cruckig_free(inp->current_acceleration);
|
||||
cruckig_free(inp->target_position);
|
||||
cruckig_free(inp->target_velocity);
|
||||
cruckig_free(inp->target_acceleration);
|
||||
cruckig_free(inp->max_velocity);
|
||||
cruckig_free(inp->max_acceleration);
|
||||
cruckig_free(inp->max_jerk);
|
||||
cruckig_free(inp->enabled);
|
||||
cruckig_free(inp->min_velocity);
|
||||
cruckig_free(inp->min_acceleration);
|
||||
cruckig_free(inp->per_dof_control_interface);
|
||||
cruckig_free(inp->per_dof_synchronization);
|
||||
/* Pro fields */
|
||||
cruckig_free(inp->intermediate_positions);
|
||||
cruckig_free(inp->per_section_max_velocity);
|
||||
cruckig_free(inp->per_section_max_acceleration);
|
||||
cruckig_free(inp->per_section_max_jerk);
|
||||
cruckig_free(inp->per_section_min_velocity);
|
||||
cruckig_free(inp->per_section_min_acceleration);
|
||||
cruckig_free(inp->per_section_max_position);
|
||||
cruckig_free(inp->per_section_min_position);
|
||||
cruckig_free(inp->max_position);
|
||||
cruckig_free(inp->min_position);
|
||||
cruckig_free(inp->per_section_minimum_duration);
|
||||
cruckig_free(inp);
|
||||
}
|
||||
|
||||
void cruckig_input_set_intermediate_positions(CRuckigInputParameter *inp,
|
||||
const double *positions,
|
||||
size_t num_waypoints)
|
||||
{
|
||||
if (!inp) return;
|
||||
const size_t dofs = inp->degrees_of_freedom;
|
||||
|
||||
cruckig_free(inp->intermediate_positions);
|
||||
if (num_waypoints == 0 || !positions) {
|
||||
inp->intermediate_positions = NULL;
|
||||
inp->num_intermediate_waypoints = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t total = num_waypoints * dofs;
|
||||
inp->intermediate_positions = (double*)cruckig_malloc(total * sizeof(double));
|
||||
memcpy(inp->intermediate_positions, positions, total * sizeof(double));
|
||||
inp->num_intermediate_waypoints = num_waypoints;
|
||||
}
|
||||
|
||||
bool cruckig_input_validate(const CRuckigInputParameter *inp,
|
||||
bool check_current_within_limits,
|
||||
bool check_target_within_limits)
|
||||
{
|
||||
if (!inp) return false;
|
||||
const size_t dofs = inp->degrees_of_freedom;
|
||||
|
||||
/* Waypoint-specific validation */
|
||||
if (inp->num_intermediate_waypoints > 0) {
|
||||
/* Waypoints require Position control interface */
|
||||
if (inp->control_interface != CRuckigPosition) return false;
|
||||
/* Waypoints incompatible with Discrete discretization */
|
||||
if (inp->duration_discretization == CRuckigDiscrete) return false;
|
||||
/* Waypoints incompatible with minimum_duration */
|
||||
if (inp->has_minimum_duration) return false;
|
||||
|
||||
/* Infinite jerk not supported with waypoints */
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
if (isinf(inp->max_jerk[dof])) return false;
|
||||
if (isinf(inp->max_acceleration[dof])) return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
const double jMax = inp->max_jerk[dof];
|
||||
if (isnan(jMax) || jMax < 0.0) return false;
|
||||
|
||||
const double aMax = inp->max_acceleration[dof];
|
||||
if (isnan(aMax) || aMax < 0.0) return false;
|
||||
|
||||
const double aMin = inp->min_acceleration ? inp->min_acceleration[dof] : -aMax;
|
||||
if (isnan(aMin) || aMin > 0.0) return false;
|
||||
|
||||
const double a0 = inp->current_acceleration[dof];
|
||||
if (isnan(a0)) return false;
|
||||
const double af = inp->target_acceleration[dof];
|
||||
if (isnan(af)) return false;
|
||||
|
||||
if (check_current_within_limits) {
|
||||
if (a0 > aMax) return false;
|
||||
if (a0 < aMin) return false;
|
||||
}
|
||||
if (check_target_within_limits) {
|
||||
if (af > aMax) return false;
|
||||
if (af < aMin) return false;
|
||||
}
|
||||
|
||||
const double v0 = inp->current_velocity[dof];
|
||||
if (isnan(v0)) return false;
|
||||
const double vf = inp->target_velocity[dof];
|
||||
if (isnan(vf)) return false;
|
||||
|
||||
CRuckigControlInterface ci = inp->per_dof_control_interface
|
||||
? inp->per_dof_control_interface[dof]
|
||||
: inp->control_interface;
|
||||
|
||||
if (ci == CRuckigPosition) {
|
||||
const double p0 = inp->current_position[dof];
|
||||
if (isnan(p0)) return false;
|
||||
const double pf = inp->target_position[dof];
|
||||
if (isnan(pf)) return false;
|
||||
|
||||
const double vMax = inp->max_velocity[dof];
|
||||
if (isnan(vMax) || vMax < 0.0) return false;
|
||||
|
||||
const double vMin = inp->min_velocity ? inp->min_velocity[dof] : -vMax;
|
||||
if (isnan(vMin) || vMin > 0.0) return false;
|
||||
|
||||
if (check_current_within_limits) {
|
||||
if (v0 > vMax) return false;
|
||||
if (v0 < vMin) return false;
|
||||
}
|
||||
if (check_target_within_limits) {
|
||||
if (vf > vMax) return false;
|
||||
if (vf < vMin) return false;
|
||||
}
|
||||
|
||||
if (check_current_within_limits) {
|
||||
if (a0 > 0 && jMax > 0 && v_at_a_zero(v0, a0, jMax) > vMax)
|
||||
return false;
|
||||
if (a0 < 0 && jMax > 0 && v_at_a_zero(v0, a0, -jMax) < vMin)
|
||||
return false;
|
||||
}
|
||||
if (check_target_within_limits) {
|
||||
if (af < 0 && jMax > 0 && v_at_a_zero(vf, af, jMax) > vMax)
|
||||
return false;
|
||||
if (af > 0 && jMax > 0 && v_at_a_zero(vf, af, -jMax) < vMin)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cruckig_input_is_equal(const CRuckigInputParameter *a, const CRuckigInputParameter *b) {
|
||||
if (!a || !b) return (a == b);
|
||||
if (a->degrees_of_freedom != b->degrees_of_freedom) return false;
|
||||
|
||||
const size_t dofs = a->degrees_of_freedom;
|
||||
const size_t dsz = dofs * sizeof(double);
|
||||
|
||||
if (memcmp(a->current_position, b->current_position, dsz) != 0) return false;
|
||||
if (memcmp(a->current_velocity, b->current_velocity, dsz) != 0) return false;
|
||||
if (memcmp(a->current_acceleration, b->current_acceleration, dsz) != 0) return false;
|
||||
if (memcmp(a->target_position, b->target_position, dsz) != 0) return false;
|
||||
if (memcmp(a->target_velocity, b->target_velocity, dsz) != 0) return false;
|
||||
if (memcmp(a->target_acceleration, b->target_acceleration, dsz) != 0) return false;
|
||||
if (memcmp(a->max_velocity, b->max_velocity, dsz) != 0) return false;
|
||||
if (memcmp(a->max_acceleration, b->max_acceleration, dsz) != 0) return false;
|
||||
if (memcmp(a->max_jerk, b->max_jerk, dsz) != 0) return false;
|
||||
|
||||
if (memcmp(a->enabled, b->enabled, dofs * sizeof(bool)) != 0) return false;
|
||||
|
||||
/* Compare optional min_velocity */
|
||||
if ((a->min_velocity == NULL) != (b->min_velocity == NULL)) return false;
|
||||
if (a->min_velocity && memcmp(a->min_velocity, b->min_velocity, dsz) != 0) return false;
|
||||
|
||||
/* Compare optional min_acceleration */
|
||||
if ((a->min_acceleration == NULL) != (b->min_acceleration == NULL)) return false;
|
||||
if (a->min_acceleration && memcmp(a->min_acceleration, b->min_acceleration, dsz) != 0) return false;
|
||||
|
||||
/* Compare optional per_dof_control_interface */
|
||||
if ((a->per_dof_control_interface == NULL) != (b->per_dof_control_interface == NULL)) return false;
|
||||
if (a->per_dof_control_interface &&
|
||||
memcmp(a->per_dof_control_interface, b->per_dof_control_interface,
|
||||
dofs * sizeof(CRuckigControlInterface)) != 0) return false;
|
||||
|
||||
/* Compare optional per_dof_synchronization */
|
||||
if ((a->per_dof_synchronization == NULL) != (b->per_dof_synchronization == NULL)) return false;
|
||||
if (a->per_dof_synchronization &&
|
||||
memcmp(a->per_dof_synchronization, b->per_dof_synchronization,
|
||||
dofs * sizeof(CRuckigSynchronization)) != 0) return false;
|
||||
|
||||
if (a->control_interface != b->control_interface) return false;
|
||||
if (a->synchronization != b->synchronization) return false;
|
||||
if (a->duration_discretization != b->duration_discretization) return false;
|
||||
|
||||
if (a->has_minimum_duration != b->has_minimum_duration) return false;
|
||||
if (a->has_minimum_duration && a->minimum_duration != b->minimum_duration) return false;
|
||||
|
||||
/* Compare Pro fields */
|
||||
if (a->num_intermediate_waypoints != b->num_intermediate_waypoints) return false;
|
||||
if (a->num_intermediate_waypoints > 0) {
|
||||
size_t wp_sz = a->num_intermediate_waypoints * dofs * sizeof(double);
|
||||
if (memcmp(a->intermediate_positions, b->intermediate_positions, wp_sz) != 0) return false;
|
||||
}
|
||||
|
||||
/* Compare position limits */
|
||||
if ((a->max_position == NULL) != (b->max_position == NULL)) return false;
|
||||
if (a->max_position && memcmp(a->max_position, b->max_position, dsz) != 0) return false;
|
||||
if ((a->min_position == NULL) != (b->min_position == NULL)) return false;
|
||||
if (a->min_position && memcmp(a->min_position, b->min_position, dsz) != 0) return false;
|
||||
|
||||
/* Compare per-section constraints */
|
||||
size_t nsec = a->num_intermediate_waypoints + 1;
|
||||
size_t sec_dsz = nsec * dofs * sizeof(double);
|
||||
|
||||
#define CMP_OPT_SEC(field) \
|
||||
if ((a->field == NULL) != (b->field == NULL)) return false; \
|
||||
if (a->field && memcmp(a->field, b->field, sec_dsz) != 0) return false;
|
||||
|
||||
CMP_OPT_SEC(per_section_max_velocity)
|
||||
CMP_OPT_SEC(per_section_max_acceleration)
|
||||
CMP_OPT_SEC(per_section_max_jerk)
|
||||
CMP_OPT_SEC(per_section_min_velocity)
|
||||
CMP_OPT_SEC(per_section_min_acceleration)
|
||||
CMP_OPT_SEC(per_section_max_position)
|
||||
CMP_OPT_SEC(per_section_min_position)
|
||||
#undef CMP_OPT_SEC
|
||||
|
||||
if ((a->per_section_minimum_duration == NULL) != (b->per_section_minimum_duration == NULL)) return false;
|
||||
if (a->per_section_minimum_duration &&
|
||||
memcmp(a->per_section_minimum_duration, b->per_section_minimum_duration,
|
||||
nsec * sizeof(double)) != 0) return false;
|
||||
|
||||
if (a->interrupt_calculation_duration != b->interrupt_calculation_duration) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Helper to copy an optional flat array */
|
||||
static void copy_opt_array(double **dst, const double *src, size_t count) {
|
||||
if (src) {
|
||||
size_t sz = count * sizeof(double);
|
||||
if (!*dst) {
|
||||
*dst = (double*)cruckig_malloc(sz);
|
||||
}
|
||||
memcpy(*dst, src, sz);
|
||||
} else {
|
||||
cruckig_free(*dst);
|
||||
*dst = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void cruckig_input_copy(CRuckigInputParameter *dst, const CRuckigInputParameter *src) {
|
||||
if (!dst || !src) return;
|
||||
if (dst == src) return;
|
||||
|
||||
const size_t dofs = src->degrees_of_freedom;
|
||||
const size_t dsz = dofs * sizeof(double);
|
||||
|
||||
/* dst must already be allocated with same dofs */
|
||||
dst->degrees_of_freedom = dofs;
|
||||
dst->control_interface = src->control_interface;
|
||||
dst->synchronization = src->synchronization;
|
||||
dst->duration_discretization = src->duration_discretization;
|
||||
|
||||
memcpy(dst->current_position, src->current_position, dsz);
|
||||
memcpy(dst->current_velocity, src->current_velocity, dsz);
|
||||
memcpy(dst->current_acceleration, src->current_acceleration, dsz);
|
||||
memcpy(dst->target_position, src->target_position, dsz);
|
||||
memcpy(dst->target_velocity, src->target_velocity, dsz);
|
||||
memcpy(dst->target_acceleration, src->target_acceleration, dsz);
|
||||
memcpy(dst->max_velocity, src->max_velocity, dsz);
|
||||
memcpy(dst->max_acceleration, src->max_acceleration, dsz);
|
||||
memcpy(dst->max_jerk, src->max_jerk, dsz);
|
||||
memcpy(dst->enabled, src->enabled, dofs * sizeof(bool));
|
||||
|
||||
copy_opt_array(&dst->min_velocity, src->min_velocity, dofs);
|
||||
copy_opt_array(&dst->min_acceleration, src->min_acceleration, dofs);
|
||||
|
||||
/* Handle optional per_dof_control_interface */
|
||||
if (src->per_dof_control_interface) {
|
||||
if (!dst->per_dof_control_interface) {
|
||||
dst->per_dof_control_interface = (CRuckigControlInterface*)cruckig_malloc(dofs * sizeof(CRuckigControlInterface));
|
||||
}
|
||||
memcpy(dst->per_dof_control_interface, src->per_dof_control_interface,
|
||||
dofs * sizeof(CRuckigControlInterface));
|
||||
} else {
|
||||
cruckig_free(dst->per_dof_control_interface);
|
||||
dst->per_dof_control_interface = NULL;
|
||||
}
|
||||
|
||||
/* Handle optional per_dof_synchronization */
|
||||
if (src->per_dof_synchronization) {
|
||||
if (!dst->per_dof_synchronization) {
|
||||
dst->per_dof_synchronization = (CRuckigSynchronization*)cruckig_malloc(dofs * sizeof(CRuckigSynchronization));
|
||||
}
|
||||
memcpy(dst->per_dof_synchronization, src->per_dof_synchronization,
|
||||
dofs * sizeof(CRuckigSynchronization));
|
||||
} else {
|
||||
cruckig_free(dst->per_dof_synchronization);
|
||||
dst->per_dof_synchronization = NULL;
|
||||
}
|
||||
|
||||
dst->minimum_duration = src->minimum_duration;
|
||||
dst->has_minimum_duration = src->has_minimum_duration;
|
||||
|
||||
/* Copy Pro fields */
|
||||
if (src->num_intermediate_waypoints > 0 && src->intermediate_positions) {
|
||||
size_t wp_sz = src->num_intermediate_waypoints * dofs;
|
||||
copy_opt_array(&dst->intermediate_positions, src->intermediate_positions, wp_sz);
|
||||
dst->num_intermediate_waypoints = src->num_intermediate_waypoints;
|
||||
} else {
|
||||
cruckig_free(dst->intermediate_positions);
|
||||
dst->intermediate_positions = NULL;
|
||||
dst->num_intermediate_waypoints = 0;
|
||||
}
|
||||
|
||||
copy_opt_array(&dst->max_position, src->max_position, dofs);
|
||||
copy_opt_array(&dst->min_position, src->min_position, dofs);
|
||||
|
||||
/* Per-section arrays */
|
||||
size_t nsec = src->num_intermediate_waypoints + 1;
|
||||
size_t sec_count = nsec * dofs;
|
||||
|
||||
copy_opt_array(&dst->per_section_max_velocity, src->per_section_max_velocity, sec_count);
|
||||
copy_opt_array(&dst->per_section_max_acceleration, src->per_section_max_acceleration, sec_count);
|
||||
copy_opt_array(&dst->per_section_max_jerk, src->per_section_max_jerk, sec_count);
|
||||
copy_opt_array(&dst->per_section_min_velocity, src->per_section_min_velocity, sec_count);
|
||||
copy_opt_array(&dst->per_section_min_acceleration, src->per_section_min_acceleration, sec_count);
|
||||
copy_opt_array(&dst->per_section_max_position, src->per_section_max_position, sec_count);
|
||||
copy_opt_array(&dst->per_section_min_position, src->per_section_min_position, sec_count);
|
||||
|
||||
if (src->per_section_minimum_duration) {
|
||||
copy_opt_array(&dst->per_section_minimum_duration, src->per_section_minimum_duration, nsec);
|
||||
} else {
|
||||
cruckig_free(dst->per_section_minimum_duration);
|
||||
dst->per_section_minimum_duration = NULL;
|
||||
}
|
||||
|
||||
dst->interrupt_calculation_duration = src->interrupt_calculation_duration;
|
||||
}
|
||||
94
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/input_parameter.h
vendored
Normal file
94
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/input_parameter.h
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_INPUT_PARAMETER_H
|
||||
#define CRUCKIG_INPUT_PARAMETER_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
#include "result.h"
|
||||
|
||||
typedef struct {
|
||||
size_t degrees_of_freedom;
|
||||
|
||||
CRuckigControlInterface control_interface;
|
||||
CRuckigSynchronization synchronization;
|
||||
CRuckigDurationDiscretization duration_discretization;
|
||||
|
||||
/* Current state */
|
||||
double *current_position;
|
||||
double *current_velocity;
|
||||
double *current_acceleration;
|
||||
|
||||
/* Target state */
|
||||
double *target_position;
|
||||
double *target_velocity;
|
||||
double *target_acceleration;
|
||||
|
||||
/* Kinematic constraints */
|
||||
double *max_velocity;
|
||||
double *max_acceleration;
|
||||
double *max_jerk;
|
||||
|
||||
/* Optional min limits (NULL = use -max) */
|
||||
double *min_velocity; /* NULL or array of dofs */
|
||||
double *min_acceleration; /* NULL or array of dofs */
|
||||
|
||||
/* Per-DOF enable flags */
|
||||
bool *enabled;
|
||||
|
||||
/* Optional per-DOF control interface / synchronization (NULL = use global) */
|
||||
CRuckigControlInterface *per_dof_control_interface; /* NULL or array of dofs */
|
||||
CRuckigSynchronization *per_dof_synchronization; /* NULL or array of dofs */
|
||||
|
||||
/* Optional minimum trajectory duration (-1 = not set) */
|
||||
double minimum_duration;
|
||||
bool has_minimum_duration;
|
||||
|
||||
/* ---- Pro features ---- */
|
||||
|
||||
/* Intermediate waypoints: flat array of num_waypoints * dofs doubles.
|
||||
* Each waypoint is dofs consecutive doubles. NULL if no waypoints. */
|
||||
double *intermediate_positions;
|
||||
size_t num_intermediate_waypoints;
|
||||
|
||||
/* Per-section kinematic constraints: flat arrays of (num_waypoints+1) * dofs.
|
||||
* Section i constraints at offset i*dofs. NULL = use global. */
|
||||
double *per_section_max_velocity;
|
||||
double *per_section_max_acceleration;
|
||||
double *per_section_max_jerk;
|
||||
double *per_section_min_velocity;
|
||||
double *per_section_min_acceleration;
|
||||
|
||||
/* Per-section position limits: flat arrays of (num_waypoints+1) * dofs. */
|
||||
double *per_section_max_position;
|
||||
double *per_section_min_position;
|
||||
|
||||
/* Global position limits during trajectory (NULL = no limits) */
|
||||
double *max_position; /* NULL or array of dofs */
|
||||
double *min_position; /* NULL or array of dofs */
|
||||
|
||||
/* Per-section minimum duration: array of (num_waypoints+1). NULL = no constraint. */
|
||||
double *per_section_minimum_duration;
|
||||
|
||||
/* Calculation interruption budget in microseconds. 0 = no interruption. */
|
||||
double interrupt_calculation_duration;
|
||||
} CRuckigInputParameter;
|
||||
|
||||
CRuckigInputParameter* cruckig_input_create(size_t dofs);
|
||||
void cruckig_input_destroy(CRuckigInputParameter *inp);
|
||||
bool cruckig_input_validate(const CRuckigInputParameter *inp,
|
||||
bool check_current_within_limits,
|
||||
bool check_target_within_limits);
|
||||
bool cruckig_input_is_equal(const CRuckigInputParameter *a, const CRuckigInputParameter *b);
|
||||
void cruckig_input_copy(CRuckigInputParameter *dst, const CRuckigInputParameter *src);
|
||||
|
||||
/* Set intermediate waypoints. Copies the data. positions is num_waypoints * dofs doubles. */
|
||||
void cruckig_input_set_intermediate_positions(CRuckigInputParameter *inp,
|
||||
const double *positions,
|
||||
size_t num_waypoints);
|
||||
|
||||
#endif /* CRUCKIG_INPUT_PARAMETER_H */
|
||||
104
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/output_parameter.c
vendored
Normal file
104
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/output_parameter.c
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#include "output_parameter.h"
|
||||
|
||||
|
||||
CRuckigOutputParameter* cruckig_output_create(size_t dofs) {
|
||||
CRuckigOutputParameter *out = (CRuckigOutputParameter*)cruckig_calloc(1, sizeof(CRuckigOutputParameter));
|
||||
if (!out) return NULL;
|
||||
|
||||
out->degrees_of_freedom = dofs;
|
||||
|
||||
out->trajectory = cruckig_trajectory_create(dofs);
|
||||
if (!out->trajectory) {
|
||||
cruckig_free(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out->new_position = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
out->new_velocity = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
out->new_acceleration = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
out->new_jerk = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
|
||||
if (!out->new_position || !out->new_velocity ||
|
||||
!out->new_acceleration || !out->new_jerk) {
|
||||
cruckig_output_destroy(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out->time = 0.0;
|
||||
out->new_section = 0;
|
||||
out->did_section_change = false;
|
||||
out->new_calculation = false;
|
||||
out->was_calculation_interrupted = false;
|
||||
out->calculation_duration = 0.0;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void cruckig_output_destroy(CRuckigOutputParameter *out) {
|
||||
if (!out) return;
|
||||
cruckig_trajectory_destroy(out->trajectory);
|
||||
cruckig_free(out->new_position);
|
||||
cruckig_free(out->new_velocity);
|
||||
cruckig_free(out->new_acceleration);
|
||||
cruckig_free(out->new_jerk);
|
||||
cruckig_free(out);
|
||||
}
|
||||
|
||||
void cruckig_output_pass_to_input(const CRuckigOutputParameter *out, CRuckigInputParameter *inp) {
|
||||
if (!out || !inp) return;
|
||||
|
||||
const size_t dofs = out->degrees_of_freedom;
|
||||
const size_t dsz = dofs * sizeof(double);
|
||||
|
||||
memcpy(inp->current_position, out->new_position, dsz);
|
||||
memcpy(inp->current_velocity, out->new_velocity, dsz);
|
||||
memcpy(inp->current_acceleration, out->new_acceleration, dsz);
|
||||
|
||||
/* If section changed and we have intermediate waypoints, remove the first waypoint */
|
||||
if (out->did_section_change && inp->num_intermediate_waypoints > 0) {
|
||||
size_t remaining = inp->num_intermediate_waypoints - 1;
|
||||
if (remaining == 0) {
|
||||
cruckig_free(inp->intermediate_positions);
|
||||
inp->intermediate_positions = NULL;
|
||||
inp->num_intermediate_waypoints = 0;
|
||||
} else {
|
||||
/* Shift waypoints forward by one */
|
||||
memmove(inp->intermediate_positions,
|
||||
inp->intermediate_positions + dofs,
|
||||
remaining * dofs * sizeof(double));
|
||||
inp->num_intermediate_waypoints = remaining;
|
||||
}
|
||||
|
||||
/* Also shift per-section constraints if present */
|
||||
size_t old_nsec = remaining + 2; /* was num_waypoints+1 sections */
|
||||
size_t new_nsec = remaining + 1;
|
||||
|
||||
#define SHIFT_PER_SEC(field) \
|
||||
if (inp->field) { \
|
||||
memmove(inp->field, inp->field + dofs, new_nsec * dofs * sizeof(double)); \
|
||||
}
|
||||
|
||||
SHIFT_PER_SEC(per_section_max_velocity)
|
||||
SHIFT_PER_SEC(per_section_max_acceleration)
|
||||
SHIFT_PER_SEC(per_section_max_jerk)
|
||||
SHIFT_PER_SEC(per_section_min_velocity)
|
||||
SHIFT_PER_SEC(per_section_min_acceleration)
|
||||
SHIFT_PER_SEC(per_section_max_position)
|
||||
SHIFT_PER_SEC(per_section_min_position)
|
||||
#undef SHIFT_PER_SEC
|
||||
|
||||
if (inp->per_section_minimum_duration) {
|
||||
memmove(inp->per_section_minimum_duration,
|
||||
inp->per_section_minimum_duration + 1,
|
||||
new_nsec * sizeof(double));
|
||||
}
|
||||
(void)old_nsec;
|
||||
}
|
||||
}
|
||||
37
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/output_parameter.h
vendored
Normal file
37
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/output_parameter.h
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_OUTPUT_PARAMETER_H
|
||||
#define CRUCKIG_OUTPUT_PARAMETER_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
#include "trajectory.h"
|
||||
#include "input_parameter.h"
|
||||
|
||||
typedef struct {
|
||||
size_t degrees_of_freedom;
|
||||
|
||||
CRuckigTrajectory *trajectory;
|
||||
|
||||
double *new_position;
|
||||
double *new_velocity;
|
||||
double *new_acceleration;
|
||||
double *new_jerk;
|
||||
|
||||
double time;
|
||||
size_t new_section;
|
||||
bool did_section_change;
|
||||
bool new_calculation;
|
||||
bool was_calculation_interrupted;
|
||||
double calculation_duration; /* microseconds */
|
||||
} CRuckigOutputParameter;
|
||||
|
||||
CRuckigOutputParameter* cruckig_output_create(size_t dofs);
|
||||
void cruckig_output_destroy(CRuckigOutputParameter *out);
|
||||
void cruckig_output_pass_to_input(const CRuckigOutputParameter *out, CRuckigInputParameter *inp);
|
||||
|
||||
#endif /* CRUCKIG_OUTPUT_PARAMETER_H */
|
||||
103
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position.h
vendored
Normal file
103
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position.h
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_POSITION_H
|
||||
#define CRUCKIG_POSITION_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
#include "profile.h"
|
||||
#include "block.h"
|
||||
|
||||
/* ---- Third Order Step 1 ---- */
|
||||
typedef struct {
|
||||
double v0, a0, vf, af;
|
||||
double _vMax, _vMin, _aMax, _aMin, _jMax;
|
||||
double pd;
|
||||
double v0_v0, vf_vf;
|
||||
double a0_a0, a0_p3, a0_p4;
|
||||
double af_af, af_p3, af_p4;
|
||||
double jMax_jMax;
|
||||
CRuckigProfile valid_profiles[6];
|
||||
} CRuckigPositionThirdOrderStep1;
|
||||
|
||||
void cruckig_pos3_step1_init(CRuckigPositionThirdOrderStep1 *s,
|
||||
double p0, double v0, double a0,
|
||||
double pf, double vf, double af,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax);
|
||||
bool cruckig_pos3_step1_get_profile(CRuckigPositionThirdOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block);
|
||||
|
||||
/* ---- Third Order Step 2 ---- */
|
||||
typedef struct {
|
||||
double v0, a0, tf, vf, af;
|
||||
double _vMax, _vMin, _aMax, _aMin, _jMax;
|
||||
double pd;
|
||||
double tf_tf, tf_p3, tf_p4;
|
||||
double vd, vd_vd;
|
||||
double ad, ad_ad;
|
||||
double v0_v0, vf_vf;
|
||||
double a0_a0, a0_p3, a0_p4, a0_p5, a0_p6;
|
||||
double af_af, af_p3, af_p4, af_p5, af_p6;
|
||||
double jMax_jMax;
|
||||
double g1, g2;
|
||||
} CRuckigPositionThirdOrderStep2;
|
||||
|
||||
void cruckig_pos3_step2_init(CRuckigPositionThirdOrderStep2 *s,
|
||||
double tf, double p0, double v0, double a0,
|
||||
double pf, double vf, double af,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax);
|
||||
bool cruckig_pos3_step2_get_profile(CRuckigPositionThirdOrderStep2 *s, CRuckigProfile *profile);
|
||||
|
||||
/* ---- Second Order Step 1 ---- */
|
||||
typedef struct {
|
||||
double v0, vf;
|
||||
double _vMax, _vMin, _aMax, _aMin;
|
||||
double pd;
|
||||
CRuckigProfile valid_profiles[4];
|
||||
} CRuckigPositionSecondOrderStep1;
|
||||
|
||||
void cruckig_pos2_step1_init(CRuckigPositionSecondOrderStep1 *s,
|
||||
double p0, double v0, double pf, double vf,
|
||||
double vMax, double vMin, double aMax, double aMin);
|
||||
bool cruckig_pos2_step1_get_profile(CRuckigPositionSecondOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block);
|
||||
|
||||
/* ---- Second Order Step 2 ---- */
|
||||
typedef struct {
|
||||
double v0, tf, vf;
|
||||
double _vMax, _vMin, _aMax, _aMin;
|
||||
double pd, vd;
|
||||
} CRuckigPositionSecondOrderStep2;
|
||||
|
||||
void cruckig_pos2_step2_init(CRuckigPositionSecondOrderStep2 *s,
|
||||
double tf, double p0, double v0, double pf, double vf,
|
||||
double vMax, double vMin, double aMax, double aMin);
|
||||
bool cruckig_pos2_step2_get_profile(CRuckigPositionSecondOrderStep2 *s, CRuckigProfile *profile);
|
||||
|
||||
/* ---- First Order Step 1 ---- */
|
||||
typedef struct {
|
||||
double _vMax, _vMin;
|
||||
double pd;
|
||||
} CRuckigPositionFirstOrderStep1;
|
||||
|
||||
void cruckig_pos1_step1_init(CRuckigPositionFirstOrderStep1 *s,
|
||||
double p0, double pf, double vMax, double vMin);
|
||||
bool cruckig_pos1_step1_get_profile(CRuckigPositionFirstOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block);
|
||||
|
||||
/* ---- First Order Step 2 ---- */
|
||||
typedef struct {
|
||||
double tf;
|
||||
double _vMax, _vMin;
|
||||
double pd;
|
||||
} CRuckigPositionFirstOrderStep2;
|
||||
|
||||
void cruckig_pos1_step2_init(CRuckigPositionFirstOrderStep2 *s,
|
||||
double tf, double p0, double pf, double vMax, double vMin);
|
||||
bool cruckig_pos1_step2_get_profile(CRuckigPositionFirstOrderStep2 *s, CRuckigProfile *profile);
|
||||
|
||||
#endif /* CRUCKIG_POSITION_H */
|
||||
41
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_first_step1.c
vendored
Normal file
41
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_first_step1.c
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "position.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
|
||||
void cruckig_pos1_step1_init(CRuckigPositionFirstOrderStep1 *s,
|
||||
double p0, double pf, double vMax, double vMin)
|
||||
{
|
||||
s->_vMax = vMax;
|
||||
s->_vMin = vMin;
|
||||
s->pd = pf - p0;
|
||||
}
|
||||
|
||||
bool cruckig_pos1_step1_get_profile(CRuckigPositionFirstOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block)
|
||||
{
|
||||
CRuckigProfile *p = &block->p_min;
|
||||
cruckig_profile_set_boundary_from_profile(p, input);
|
||||
|
||||
const double vf = (s->pd > 0) ? s->_vMax : s->_vMin;
|
||||
p->t[0] = 0;
|
||||
p->t[1] = 0;
|
||||
p->t[2] = 0;
|
||||
p->t[3] = s->pd / vf;
|
||||
p->t[4] = 0;
|
||||
p->t[5] = 0;
|
||||
p->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_first_order(p, ControlSignsUDDU, ReachedLimitsVEL, vf)) {
|
||||
block->t_min = p->t_sum[6] + p->brake.duration + p->accel.duration;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
37
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_first_step2.c
vendored
Normal file
37
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_first_step2.c
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "position.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
#include "roots.h"
|
||||
|
||||
void cruckig_pos1_step2_init(CRuckigPositionFirstOrderStep2 *s,
|
||||
double tf, double p0, double pf, double vMax, double vMin)
|
||||
{
|
||||
s->tf = tf;
|
||||
s->_vMax = vMax;
|
||||
s->_vMin = vMin;
|
||||
s->pd = pf - p0;
|
||||
}
|
||||
|
||||
bool cruckig_pos1_step2_get_profile(CRuckigPositionFirstOrderStep2 *s, CRuckigProfile *profile)
|
||||
{
|
||||
const double vf = s->pd / s->tf;
|
||||
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = s->tf;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
return cruckig_profile_check_for_first_order_with_timing_full(profile, ControlSignsUDDU, ReachedLimitsNONE,
|
||||
s->tf, vf, s->_vMax, s->_vMin);
|
||||
}
|
||||
179
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_second_step1.c
vendored
Normal file
179
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_second_step1.c
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "position.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
|
||||
void cruckig_pos2_step1_init(CRuckigPositionSecondOrderStep1 *s,
|
||||
double p0, double v0, double pf, double vf,
|
||||
double vMax, double vMin, double aMax, double aMin)
|
||||
{
|
||||
s->v0 = v0;
|
||||
s->vf = vf;
|
||||
s->_vMax = vMax;
|
||||
s->_vMin = vMin;
|
||||
s->_aMax = aMax;
|
||||
s->_aMin = aMin;
|
||||
s->pd = pf - p0;
|
||||
}
|
||||
|
||||
static void time_acc0(CRuckigPositionSecondOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, bool return_after_found)
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
|
||||
profile->t[0] = (-s->v0 + vMax) / aMax;
|
||||
profile->t[1] = (aMin * s->v0 * s->v0 - aMax * s->vf * s->vf) / (2 * aMax * aMin * vMax) + vMax * (aMax - aMin) / (2 * aMax * aMin) + s->pd / vMax;
|
||||
profile->t[2] = (s->vf - vMax) / aMin;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_second_order(profile, ControlSignsUDDU, ReachedLimitsACC0, aMax, aMin, vMax, vMin)) {
|
||||
++(*counter);
|
||||
if (*counter < 4) {
|
||||
cruckig_profile_set_boundary_from_profile(&valid_profiles[*counter], profile);
|
||||
}
|
||||
}
|
||||
|
||||
(void)return_after_found;
|
||||
}
|
||||
|
||||
static void time_none(CRuckigPositionSecondOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, bool return_after_found)
|
||||
{
|
||||
double h1 = (aMax * s->vf * s->vf - aMin * s->v0 * s->v0 - 2 * aMax * aMin * s->pd) / (aMax - aMin);
|
||||
if (h1 >= 0.0) {
|
||||
h1 = sqrt(h1);
|
||||
|
||||
/* Solution 1 */
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
|
||||
profile->t[0] = -(s->v0 + h1) / aMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = (s->vf + h1) / aMin;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_second_order(profile, ControlSignsUDDU, ReachedLimitsNONE, aMax, aMin, vMax, vMin)) {
|
||||
++(*counter);
|
||||
if (*counter < 4) {
|
||||
cruckig_profile_set_boundary_from_profile(&valid_profiles[*counter], profile);
|
||||
}
|
||||
if (return_after_found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Solution 2 */
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
|
||||
profile->t[0] = (-s->v0 + h1) / aMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = (s->vf - h1) / aMin;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_second_order(profile, ControlSignsUDDU, ReachedLimitsNONE, aMax, aMin, vMax, vMin)) {
|
||||
++(*counter);
|
||||
if (*counter < 4) {
|
||||
cruckig_profile_set_boundary_from_profile(&valid_profiles[*counter], profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool time_all_single_step(CRuckigPositionSecondOrderStep1 *s,
|
||||
CRuckigProfile *profile, double vMax, double vMin)
|
||||
{
|
||||
if (fabs(s->vf - s->v0) > DBL_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (fabs(s->v0) > DBL_EPSILON) {
|
||||
profile->t[3] = s->pd / s->v0;
|
||||
if (cruckig_profile_check_for_second_order(profile, ControlSignsUDDU, ReachedLimitsNONE, 0.0, 0.0, vMax, vMin)) {
|
||||
return true;
|
||||
}
|
||||
} else if (fabs(s->pd) < DBL_EPSILON) {
|
||||
if (cruckig_profile_check_for_second_order(profile, ControlSignsUDDU, ReachedLimitsNONE, 0.0, 0.0, vMax, vMin)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cruckig_pos2_step1_get_profile(CRuckigPositionSecondOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block)
|
||||
{
|
||||
/* Zero-limits special case */
|
||||
if (s->_vMax == 0.0 && s->_vMin == 0.0) {
|
||||
CRuckigProfile *p = &block->p_min;
|
||||
cruckig_profile_set_boundary_from_profile(p, input);
|
||||
|
||||
if (time_all_single_step(s, p, s->_vMax, s->_vMin)) {
|
||||
block->t_min = p->t_sum[6] + p->brake.duration + p->accel.duration;
|
||||
if (fabs(s->v0) > DBL_EPSILON) {
|
||||
block->a.valid = true;
|
||||
block->a.left = block->t_min;
|
||||
block->a.right = INFINITY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t valid_profile_counter = 0;
|
||||
cruckig_profile_set_boundary_from_profile(&s->valid_profiles[0], input);
|
||||
|
||||
if (fabs(s->vf) < DBL_EPSILON) {
|
||||
/* There is no blocked interval when vf==0, so return after first found profile */
|
||||
const double vMax = (s->pd >= 0) ? s->_vMax : s->_vMin;
|
||||
const double vMin = (s->pd >= 0) ? s->_vMin : s->_vMax;
|
||||
const double aMax = (s->pd >= 0) ? s->_aMax : s->_aMin;
|
||||
const double aMin = (s->pd >= 0) ? s->_aMin : s->_aMax;
|
||||
|
||||
time_none(s, s->valid_profiles, &valid_profile_counter, vMax, vMin, aMax, aMin, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc0(s, s->valid_profiles, &valid_profile_counter, vMax, vMin, aMax, aMin, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
|
||||
time_none(s, s->valid_profiles, &valid_profile_counter, vMin, vMax, aMin, aMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc0(s, s->valid_profiles, &valid_profile_counter, vMin, vMax, aMin, aMax, true);
|
||||
} else {
|
||||
time_none(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, false);
|
||||
time_none(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, false);
|
||||
time_acc0(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, false);
|
||||
time_acc0(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, false);
|
||||
}
|
||||
|
||||
return_block:
|
||||
return cruckig_block_calculate(block, s->valid_profiles, valid_profile_counter, 4);
|
||||
}
|
||||
146
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_second_step2.c
vendored
Normal file
146
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_second_step2.c
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "position.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
#include "roots.h"
|
||||
|
||||
void cruckig_pos2_step2_init(CRuckigPositionSecondOrderStep2 *s,
|
||||
double tf, double p0, double v0, double pf, double vf,
|
||||
double vMax, double vMin, double aMax, double aMin)
|
||||
{
|
||||
s->v0 = v0;
|
||||
s->tf = tf;
|
||||
s->vf = vf;
|
||||
s->_vMax = vMax;
|
||||
s->_vMin = vMin;
|
||||
s->_aMax = aMax;
|
||||
s->_aMin = aMin;
|
||||
s->pd = pf - p0;
|
||||
s->vd = vf - v0;
|
||||
}
|
||||
|
||||
static bool time_acc0(CRuckigPositionSecondOrderStep2 *s, CRuckigProfile *profile,
|
||||
double vMax, double vMin, double aMax, double aMin)
|
||||
{
|
||||
/* UD Solution 1/2 */
|
||||
{
|
||||
const double h1 = sqrt((2 * aMax * (s->pd - s->tf * s->vf) - 2 * aMin * (s->pd - s->tf * s->v0) + s->vd * s->vd) / (aMax * aMin) + s->tf * s->tf);
|
||||
|
||||
profile->t[0] = (aMax * s->vd - aMax * aMin * (s->tf - h1)) / (aMax * (aMax - aMin));
|
||||
profile->t[1] = h1;
|
||||
profile->t[2] = s->tf - (profile->t[0] + h1);
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_second_order_with_timing(profile, ControlSignsUDDU, ReachedLimitsACC0, s->tf, aMax, aMin, vMax, vMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* UU Solution */
|
||||
{
|
||||
const double h1 = (-s->vd + aMax * s->tf);
|
||||
|
||||
profile->t[0] = -s->vd * s->vd / (2 * aMax * h1) + (s->pd - s->v0 * s->tf) / h1;
|
||||
profile->t[1] = -s->vd / aMax + s->tf;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = s->tf - (profile->t[0] + profile->t[1]);
|
||||
|
||||
if (cruckig_profile_check_for_second_order_with_timing(profile, ControlSignsUDDU, ReachedLimitsACC0, s->tf, aMax, aMin, vMax, vMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* UU Solution - 2 step */
|
||||
{
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = -s->vd / aMax + s->tf;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = s->vd / aMax;
|
||||
|
||||
if (cruckig_profile_check_for_second_order_with_timing(profile, ControlSignsUDDU, ReachedLimitsACC0, s->tf, aMax, aMin, vMax, vMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool time_none(CRuckigPositionSecondOrderStep2 *s, CRuckigProfile *profile,
|
||||
double vMax, double vMin, double aMax, double aMin)
|
||||
{
|
||||
if (fabs(s->v0) < DBL_EPSILON && fabs(s->vf) < DBL_EPSILON && fabs(s->pd) < DBL_EPSILON) {
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = s->tf;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_second_order_with_timing(profile, ControlSignsUDDU, ReachedLimitsNONE, s->tf, aMax, aMin, vMax, vMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* UD Solution 1/2 */
|
||||
{
|
||||
const double h1 = 2 * (s->vf * s->tf - s->pd);
|
||||
|
||||
profile->t[0] = h1 / s->vd;
|
||||
profile->t[1] = s->tf - profile->t[0];
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
const double af = s->vd * s->vd / h1;
|
||||
|
||||
if ((aMin - 1e-12 < af) && (af < aMax + 1e-12) &&
|
||||
cruckig_profile_check_for_second_order_with_timing(profile, ControlSignsUDDU, ReachedLimitsNONE, s->tf, af, -af, vMax, vMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_all(CRuckigPositionSecondOrderStep2 *s, CRuckigProfile *profile,
|
||||
double vMax, double vMin, double aMax, double aMin)
|
||||
{
|
||||
return time_acc0(s, profile, vMax, vMin, aMax, aMin) ||
|
||||
time_none(s, profile, vMax, vMin, aMax, aMin);
|
||||
}
|
||||
|
||||
bool cruckig_pos2_step2_get_profile(CRuckigPositionSecondOrderStep2 *s, CRuckigProfile *profile)
|
||||
{
|
||||
/* Test all cases to get ones that match */
|
||||
if (s->pd > 0) {
|
||||
return check_all(s, profile, s->_vMax, s->_vMin, s->_aMax, s->_aMin) ||
|
||||
check_all(s, profile, s->_vMin, s->_vMax, s->_aMin, s->_aMax);
|
||||
}
|
||||
|
||||
return check_all(s, profile, s->_vMin, s->_vMax, s->_aMin, s->_aMax) ||
|
||||
check_all(s, profile, s->_vMax, s->_vMin, s->_aMax, s->_aMin);
|
||||
}
|
||||
705
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_third_step1.c
vendored
Normal file
705
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_third_step1.c
vendored
Normal file
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "position.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
#include "roots.h"
|
||||
|
||||
void cruckig_pos3_step1_init(CRuckigPositionThirdOrderStep1 *s,
|
||||
double p0, double v0, double a0,
|
||||
double pf, double vf, double af,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax)
|
||||
{
|
||||
s->v0 = v0;
|
||||
s->a0 = a0;
|
||||
s->vf = vf;
|
||||
s->af = af;
|
||||
s->_vMax = vMax;
|
||||
s->_vMin = vMin;
|
||||
s->_aMax = aMax;
|
||||
s->_aMin = aMin;
|
||||
s->_jMax = jMax;
|
||||
|
||||
s->pd = pf - p0;
|
||||
|
||||
s->v0_v0 = v0 * v0;
|
||||
s->vf_vf = vf * vf;
|
||||
|
||||
s->a0_a0 = a0 * a0;
|
||||
s->af_af = af * af;
|
||||
|
||||
s->a0_p3 = a0 * s->a0_a0;
|
||||
s->a0_p4 = s->a0_a0 * s->a0_a0;
|
||||
s->af_p3 = af * s->af_af;
|
||||
s->af_p4 = s->af_af * s->af_af;
|
||||
|
||||
s->jMax_jMax = jMax * jMax;
|
||||
}
|
||||
|
||||
/* Helper: add_profile equivalent - increment counter, copy boundary to next */
|
||||
static inline void add_profile(CRuckigProfile *valid_profiles, size_t *counter, size_t max_profiles)
|
||||
{
|
||||
const size_t prev = *counter;
|
||||
++(*counter);
|
||||
if (*counter < max_profiles) {
|
||||
cruckig_profile_set_boundary_from_profile(&valid_profiles[*counter], &valid_profiles[prev]);
|
||||
}
|
||||
}
|
||||
|
||||
static void time_all_vel(CRuckigPositionThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax,
|
||||
bool return_after_found)
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
const double v0 = s->v0, a0 = s->a0, vf = s->vf, af = s->af;
|
||||
const double v0_v0 = s->v0_v0, vf_vf = s->vf_vf;
|
||||
const double a0_a0 = s->a0_a0, af_af = s->af_af;
|
||||
const double a0_p3 = s->a0_p3, af_p3 = s->af_p3;
|
||||
const double a0_p4 = s->a0_p4, af_p4 = s->af_p4;
|
||||
const double jMax_jMax = s->jMax_jMax;
|
||||
const double pd = s->pd;
|
||||
|
||||
(void)return_after_found;
|
||||
|
||||
/* ACC0_ACC1_VEL */
|
||||
profile->t[0] = (-a0 + aMax) / jMax;
|
||||
profile->t[1] = (a0_a0 / 2 - aMax * aMax - jMax * (v0 - vMax)) / (aMax * jMax);
|
||||
profile->t[2] = aMax / jMax;
|
||||
profile->t[3] = (3 * (a0_p4 * aMin - af_p4 * aMax) + 8 * aMax * aMin * (af_p3 - a0_p3 + 3 * jMax * (a0 * v0 - af * vf)) + 6 * a0_a0 * aMin * (aMax * aMax - 2 * jMax * v0) - 6 * af_af * aMax * (aMin * aMin - 2 * jMax * vf) - 12 * jMax * (aMax * aMin * (aMax * (v0 + vMax) - aMin * (vf + vMax) - 2 * jMax * pd) + (aMin - aMax) * jMax * vMax * vMax + jMax * (aMax * vf_vf - aMin * v0_v0))) / (24 * aMax * aMin * jMax_jMax * vMax);
|
||||
profile->t[4] = -aMin / jMax;
|
||||
profile->t[5] = -(af_af / 2 - aMin * aMin - jMax * (vf - vMax)) / (aMin * jMax);
|
||||
profile->t[6] = profile->t[4] + af / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0_ACC1_VEL, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ACC1_VEL */
|
||||
{
|
||||
const double t_acc0 = sqrt(a0_a0 / (2 * jMax_jMax) + (vMax - v0) / jMax);
|
||||
|
||||
profile->t[0] = t_acc0 - a0 / jMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = t_acc0;
|
||||
profile->t[3] = -(3 * af_p4 - 8 * aMin * (af_p3 - a0_p3) - 24 * aMin * jMax * (a0 * v0 - af * vf) + 6 * af_af * (aMin * aMin - 2 * jMax * vf) - 12 * jMax * (2 * aMin * jMax * pd + aMin * aMin * (vf + vMax) + jMax * (vMax * vMax - vf_vf) + aMin * t_acc0 * (a0_a0 - 2 * jMax * (v0 + vMax)))) / (24 * aMin * jMax_jMax * vMax);
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC1_VEL, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* ACC0_VEL */
|
||||
{
|
||||
const double t_acc1 = sqrt(af_af / (2 * jMax_jMax) + (vMax - vf) / jMax);
|
||||
|
||||
profile->t[0] = (-a0 + aMax) / jMax;
|
||||
profile->t[1] = (a0_a0 / 2 - aMax * aMax - jMax * (v0 - vMax)) / (aMax * jMax);
|
||||
profile->t[2] = aMax / jMax;
|
||||
profile->t[3] = (3 * a0_p4 + 8 * aMax * (af_p3 - a0_p3) + 24 * aMax * jMax * (a0 * v0 - af * vf) + 6 * a0_a0 * (aMax * aMax - 2 * jMax * v0) - 12 * jMax * (-2 * aMax * jMax * pd + aMax * aMax * (v0 + vMax) + jMax * (vMax * vMax - v0_v0) + aMax * t_acc1 * (-af_af + 2 * (vf + vMax) * jMax))) / (24 * aMax * jMax_jMax * vMax);
|
||||
profile->t[4] = t_acc1;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = t_acc1 + af / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0_VEL, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* VEL */
|
||||
{
|
||||
const double t_acc0 = sqrt(a0_a0 / (2 * jMax_jMax) + (vMax - v0) / jMax);
|
||||
const double t_acc1 = sqrt(af_af / (2 * jMax_jMax) + (vMax - vf) / jMax);
|
||||
|
||||
/* Solution 3/4 */
|
||||
profile->t[0] = t_acc0 - a0 / jMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = t_acc0;
|
||||
profile->t[3] = (af_p3 - a0_p3) / (3 * jMax_jMax * vMax) + (a0 * v0 - af * vf + (af_af * t_acc1 + a0_a0 * t_acc0) / 2) / (jMax * vMax) - (v0 / vMax + 1.0) * t_acc0 - (vf / vMax + 1.0) * t_acc1 + pd / vMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsVEL, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void time_acc0_acc1(CRuckigPositionThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax,
|
||||
bool return_after_found)
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
const double a0 = s->a0, af = s->af;
|
||||
const double a0_a0 = s->a0_a0, af_af = s->af_af;
|
||||
const double a0_p3 = s->a0_p3, af_p3 = s->af_p3;
|
||||
const double a0_p4 = s->a0_p4, af_p4 = s->af_p4;
|
||||
const double v0 = s->v0, vf = s->vf;
|
||||
const double v0_v0 = s->v0_v0, vf_vf = s->vf_vf;
|
||||
const double jMax_jMax = s->jMax_jMax;
|
||||
const double pd = s->pd;
|
||||
|
||||
double h1 = (3 * (af_p4 * aMax - a0_p4 * aMin) + aMax * aMin * (8 * (a0_p3 - af_p3) + 3 * aMax * aMin * (aMax - aMin) + 6 * aMin * af_af - 6 * aMax * a0_a0) + 12 * jMax * (aMax * aMin * ((aMax - 2 * a0) * v0 - (aMin - 2 * af) * vf) + aMin * a0_a0 * v0 - aMax * af_af * vf)) / (3 * (aMax - aMin) * jMax_jMax) + 4 * (aMax * vf_vf - aMin * v0_v0 - 2 * aMin * aMax * pd) / (aMax - aMin);
|
||||
|
||||
if (h1 >= 0) {
|
||||
h1 = sqrt(h1) / 2;
|
||||
const double h2 = a0_a0 / (2 * aMax * jMax) + (aMin - 2 * aMax) / (2 * jMax) - v0 / aMax;
|
||||
const double h3 = -af_af / (2 * aMin * jMax) - (aMax - 2 * aMin) / (2 * jMax) + vf / aMin;
|
||||
|
||||
/* UDDU: Solution 2 */
|
||||
if (h2 > h1 / aMax && h3 > -h1 / aMin) {
|
||||
profile->t[0] = (-a0 + aMax) / jMax;
|
||||
profile->t[1] = h2 - h1 / aMax;
|
||||
profile->t[2] = aMax / jMax;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = -aMin / jMax;
|
||||
profile->t[5] = h3 + h1 / aMin;
|
||||
profile->t[6] = profile->t[4] + af / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0_ACC1, true, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
if (return_after_found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* UDDU: Solution 1 */
|
||||
profile = &valid_profiles[*counter];
|
||||
if (h2 > -h1 / aMax && h3 > h1 / aMin) {
|
||||
profile->t[0] = (-a0 + aMax) / jMax;
|
||||
profile->t[1] = h2 + h1 / aMax;
|
||||
profile->t[2] = aMax / jMax;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = -aMin / jMax;
|
||||
profile->t[5] = h3 - h1 / aMin;
|
||||
profile->t[6] = profile->t[4] + af / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0_ACC1, true, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void time_all_none_acc0_acc1(CRuckigPositionThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax,
|
||||
bool return_after_found)
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
const double v0 = s->v0, a0 = s->a0, vf = s->vf, af = s->af;
|
||||
const double v0_v0 = s->v0_v0, vf_vf = s->vf_vf;
|
||||
const double a0_a0 = s->a0_a0, af_af = s->af_af;
|
||||
const double a0_p3 = s->a0_p3, af_p3 = s->af_p3;
|
||||
const double a0_p4 = s->a0_p4, af_p4 = s->af_p4;
|
||||
const double jMax_jMax = s->jMax_jMax;
|
||||
const double pd = s->pd;
|
||||
|
||||
/* NONE UDDU / UDUD Strategy */
|
||||
const double h2_none = (a0_a0 - af_af) / (2 * jMax) + (vf - v0);
|
||||
const double h2_h2 = h2_none * h2_none;
|
||||
const double t_min_none = (a0 - af) / jMax;
|
||||
const double t_max_none = (aMax - aMin) / jMax;
|
||||
|
||||
double polynom_none[4];
|
||||
polynom_none[0] = 0;
|
||||
polynom_none[1] = -2 * (a0_a0 + af_af - 2 * jMax * (v0 + vf)) / jMax_jMax;
|
||||
polynom_none[2] = 4 * (a0_p3 - af_p3 + 3 * jMax * (af * vf - a0 * v0)) / (3 * jMax * jMax_jMax) - 4 * pd / jMax;
|
||||
polynom_none[3] = -h2_h2 / jMax_jMax;
|
||||
|
||||
/* ACC0 */
|
||||
const double h3_acc0 = (a0_a0 - af_af) / (2 * aMax * jMax) + (vf - v0) / aMax;
|
||||
const double t_min_acc0 = (aMax - af) / jMax;
|
||||
const double t_max_acc0 = (aMax - aMin) / jMax;
|
||||
|
||||
const double h0_acc0 = 3 * (af_p4 - a0_p4) + 8 * (a0_p3 - af_p3) * aMax + 24 * aMax * jMax * (af * vf - a0 * v0) - 6 * a0_a0 * (aMax * aMax - 2 * jMax * v0) + 6 * af_af * (aMax * aMax - 2 * jMax * vf) + 12 * jMax * (jMax * (vf_vf - v0_v0 - 2 * aMax * pd) - aMax * aMax * (vf - v0));
|
||||
const double h2_acc0 = -af_af + aMax * aMax + 2 * jMax * vf;
|
||||
|
||||
double polynom_acc0[4];
|
||||
polynom_acc0[0] = -2 * aMax / jMax;
|
||||
polynom_acc0[1] = h2_acc0 / jMax_jMax;
|
||||
polynom_acc0[2] = 0;
|
||||
polynom_acc0[3] = h0_acc0 / (12 * jMax_jMax * jMax_jMax);
|
||||
|
||||
/* ACC1 */
|
||||
const double h3_acc1 = -(a0_a0 + af_af) / (2 * jMax * aMin) + aMin / jMax + (vf - v0) / aMin;
|
||||
const double t_min_acc1 = (aMin - a0) / jMax;
|
||||
const double t_max_acc1 = (aMax - a0) / jMax;
|
||||
|
||||
const double h0_acc1 = (a0_p4 - af_p4) / 4 + 2 * (af_p3 - a0_p3) * aMin / 3 + (a0_a0 - af_af) * aMin * aMin / 2 + jMax * (af_af * vf + a0_a0 * v0 + 2 * aMin * (jMax * pd - a0 * v0 - af * vf) + aMin * aMin * (v0 + vf) + jMax * (v0_v0 - vf_vf));
|
||||
const double h2_acc1 = a0_a0 - a0 * aMin + 2 * jMax * v0;
|
||||
|
||||
double polynom_acc1[4];
|
||||
polynom_acc1[0] = 2 * (2 * a0 - aMin) / jMax;
|
||||
polynom_acc1[1] = (5 * a0_a0 + aMin * (aMin - 6 * a0) + 2 * jMax * v0) / jMax_jMax;
|
||||
polynom_acc1[2] = 2 * (a0 - aMin) * h2_acc1 / (jMax_jMax * jMax);
|
||||
polynom_acc1[3] = h0_acc1 / (jMax_jMax * jMax_jMax);
|
||||
|
||||
CRuckigRootSet roots_none = cruckig_roots_solve_quart_monic(polynom_none[0], polynom_none[1], polynom_none[2], polynom_none[3]);
|
||||
CRuckigRootSet roots_acc0 = cruckig_roots_solve_quart_monic(polynom_acc0[0], polynom_acc0[1], polynom_acc0[2], polynom_acc0[3]);
|
||||
CRuckigRootSet roots_acc1 = cruckig_roots_solve_quart_monic(polynom_acc1[0], polynom_acc1[1], polynom_acc1[2], polynom_acc1[3]);
|
||||
|
||||
cruckig_root_set_sort(&roots_none);
|
||||
cruckig_root_set_sort(&roots_acc0);
|
||||
cruckig_root_set_sort(&roots_acc1);
|
||||
|
||||
for (size_t i = 0; i < roots_none.size; ++i) {
|
||||
double t = roots_none.data[i];
|
||||
if (t < t_min_none || t > t_max_none) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Single Newton-step (regarding pd) */
|
||||
if (t > DBL_EPSILON) {
|
||||
const double h1 = jMax * t * t;
|
||||
const double orig = -h2_h2 / (4 * jMax * t) + h2_none * (af / jMax + t) + (4 * a0_p3 + 2 * af_p3 - 6 * a0_a0 * (af + 2 * jMax * t) + 12 * (af - a0) * jMax * v0 + 3 * jMax_jMax * (-4 * pd + (h1 + 8 * v0) * t)) / (12 * jMax_jMax);
|
||||
const double deriv = h2_none + 2 * v0 - a0_a0 / jMax + h2_h2 / (4 * h1) + (3 * h1) / 4;
|
||||
|
||||
t -= orig / deriv;
|
||||
}
|
||||
|
||||
const double h0 = h2_none / (2 * jMax * t);
|
||||
profile = &valid_profiles[*counter];
|
||||
profile->t[0] = h0 + t / 2 - a0 / jMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = t;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = -h0 + t / 2 + af / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsNONE, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
if (return_after_found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < roots_acc0.size; ++i) {
|
||||
double t = roots_acc0.data[i];
|
||||
if (t < t_min_acc0 || t > t_max_acc0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Single Newton step (regarding pd) */
|
||||
if (t > DBL_EPSILON) {
|
||||
const double h1 = jMax * t;
|
||||
const double orig = h0_acc0 / (12 * jMax_jMax * t) + t * (h2_acc0 + h1 * (h1 - 2 * aMax));
|
||||
const double deriv = 2 * (h2_acc0 + h1 * (2 * h1 - 3 * aMax));
|
||||
|
||||
t -= orig / deriv;
|
||||
}
|
||||
|
||||
profile = &valid_profiles[*counter];
|
||||
profile->t[0] = (-a0 + aMax) / jMax;
|
||||
profile->t[1] = h3_acc0 - 2 * t + jMax / aMax * t * t;
|
||||
profile->t[2] = t;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = (af - aMax) / jMax + t;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
if (return_after_found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < roots_acc1.size; ++i) {
|
||||
double t = roots_acc1.data[i];
|
||||
if (t < t_min_acc1 || t > t_max_acc1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Double Newton step (regarding pd) */
|
||||
if (t > DBL_EPSILON) {
|
||||
const double h5 = a0_p3 + 2 * jMax * a0 * v0;
|
||||
double h1 = jMax * t;
|
||||
double orig = -(h0_acc1 / 2 + h1 * (h5 + a0 * (aMin - 2 * h1) * (aMin - h1) + a0_a0 * (5 * h1 / 2 - 2 * aMin) + aMin * aMin * h1 / 2 + jMax * (h1 / 2 - aMin) * (h1 * t + 2 * v0))) / jMax;
|
||||
double deriv = (aMin - a0 - h1) * (h2_acc1 + h1 * (4 * a0 - aMin + 2 * h1));
|
||||
{
|
||||
double correction = orig / deriv;
|
||||
if (correction > t) correction = t;
|
||||
t -= correction;
|
||||
}
|
||||
|
||||
h1 = jMax * t;
|
||||
orig = -(h0_acc1 / 2 + h1 * (h5 + a0 * (aMin - 2 * h1) * (aMin - h1) + a0_a0 * (5 * h1 / 2 - 2 * aMin) + aMin * aMin * h1 / 2 + jMax * (h1 / 2 - aMin) * (h1 * t + 2 * v0))) / jMax;
|
||||
|
||||
if (fabs(orig) > 1e-9) {
|
||||
deriv = (aMin - a0 - h1) * (h2_acc1 + h1 * (4 * a0 - aMin + 2 * h1));
|
||||
t -= orig / deriv;
|
||||
|
||||
h1 = jMax * t;
|
||||
orig = -(h0_acc1 / 2 + h1 * (h5 + a0 * (aMin - 2 * h1) * (aMin - h1) + a0_a0 * (5 * h1 / 2 - 2 * aMin) + aMin * aMin * h1 / 2 + jMax * (h1 / 2 - aMin) * (h1 * t + 2 * v0))) / jMax;
|
||||
|
||||
if (fabs(orig) > 1e-9) {
|
||||
deriv = (aMin - a0 - h1) * (h2_acc1 + h1 * (4 * a0 - aMin + 2 * h1));
|
||||
t -= orig / deriv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profile = &valid_profiles[*counter];
|
||||
profile->t[0] = t;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = (a0 - aMin) / jMax + t;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = h3_acc1 - (2 * a0 + jMax * t) * t / aMin;
|
||||
profile->t[6] = (af - aMin) / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC1, true, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
if (return_after_found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void time_acc1_vel_two_step(CRuckigPositionThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax)
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
const double v0 = s->v0, a0 = s->a0, vf = s->vf, af = s->af;
|
||||
const double vf_vf = s->vf_vf;
|
||||
const double a0_a0 = s->a0_a0, af_af = s->af_af;
|
||||
const double a0_p3 = s->a0_p3, af_p3 = s->af_p3, af_p4 = s->af_p4;
|
||||
const double jMax_jMax = s->jMax_jMax;
|
||||
const double pd = s->pd;
|
||||
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = a0 / jMax;
|
||||
profile->t[3] = -(3 * af_p4 - 8 * aMin * (af_p3 - a0_p3) - 24 * aMin * jMax * (a0 * v0 - af * vf) + 6 * af_af * (aMin * aMin - 2 * jMax * vf) - 12 * jMax * (2 * aMin * jMax * pd + aMin * aMin * (vf + vMax) + jMax * (vMax * vMax - vf_vf) + aMin * a0 * (a0_a0 - 2 * jMax * (v0 + vMax)) / jMax)) / (24 * aMin * jMax_jMax * vMax);
|
||||
profile->t[4] = -aMin / jMax;
|
||||
profile->t[5] = -(af_af / 2 - aMin * aMin + jMax * (vMax - vf)) / (aMin * jMax);
|
||||
profile->t[6] = profile->t[4] + af / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC1_VEL, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
}
|
||||
}
|
||||
|
||||
static void time_acc0_two_step(CRuckigPositionThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax)
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
const double v0 = s->v0, a0 = s->a0, vf = s->vf, af = s->af;
|
||||
const double v0_v0 = s->v0_v0, vf_vf = s->vf_vf;
|
||||
const double a0_a0 = s->a0_a0, af_af = s->af_af;
|
||||
const double a0_p3 = s->a0_p3, af_p3 = s->af_p3;
|
||||
const double a0_p4 = s->a0_p4, af_p4 = s->af_p4;
|
||||
const double jMax_jMax = s->jMax_jMax;
|
||||
const double pd = s->pd;
|
||||
|
||||
/* Two step */
|
||||
{
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = (af_af - a0_a0 + 2 * jMax * (vf - v0)) / (2 * a0 * jMax);
|
||||
profile->t[2] = (a0 - af) / jMax;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Three step - Removed pf */
|
||||
{
|
||||
profile = &valid_profiles[*counter];
|
||||
profile->t[0] = (-a0 + aMax) / jMax;
|
||||
profile->t[1] = (a0_a0 + af_af - 2 * aMax * aMax + 2 * jMax * (vf - v0)) / (2 * aMax * jMax);
|
||||
profile->t[2] = (-af + aMax) / jMax;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Three step - Removed aMax */
|
||||
{
|
||||
profile = &valid_profiles[*counter];
|
||||
const double h0 = 3 * (af_af - a0_a0 + 2 * jMax * (v0 + vf));
|
||||
const double h2 = a0_p3 + 2 * af_p3 + 6 * jMax_jMax * pd + 6 * (af - a0) * jMax * vf - 3 * a0 * af_af;
|
||||
const double h1_sq = 2 * (2 * h2 * h2 + h0 * (a0_p4 - 6 * a0_a0 * (af_af + 2 * jMax * vf) + 8 * a0 * (af_p3 + 3 * jMax_jMax * pd + 3 * af * jMax * vf) - 3 * (af_p4 + 4 * af_af * jMax * vf + 4 * jMax_jMax * (vf_vf - v0_v0))));
|
||||
const double h1 = sqrt(h1_sq) * fabs(jMax) / jMax;
|
||||
profile->t[0] = (4 * af_p3 + 2 * a0_p3 - 6 * a0 * af_af + 12 * jMax_jMax * pd + 12 * (af - a0) * jMax * vf + h1) / (2 * jMax * h0);
|
||||
profile->t[1] = -h1 / (jMax * h0);
|
||||
profile->t[2] = (-4 * a0_p3 - 2 * af_p3 + 6 * a0_a0 * af + 12 * jMax_jMax * pd - 12 * (af - a0) * jMax * v0 + h1) / (2 * jMax * h0);
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Three step - t=(aMax - aMin)/jMax */
|
||||
{
|
||||
profile = &valid_profiles[*counter];
|
||||
const double t = (aMax - aMin) / jMax;
|
||||
|
||||
profile->t[0] = (-a0 + aMax) / jMax;
|
||||
profile->t[1] = (a0_a0 - af_af) / (2 * aMax * jMax) + (vf - v0 + jMax * t * t) / aMax - 2 * t;
|
||||
profile->t[2] = t;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = (af - aMin) / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsACC0, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void time_vel_two_step(CRuckigPositionThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax)
|
||||
{
|
||||
CRuckigProfile *profile;
|
||||
const double v0 = s->v0, a0 = s->a0, vf = s->vf, af = s->af;
|
||||
const double af_af = s->af_af;
|
||||
const double a0_p3 = s->a0_p3, af_p3 = s->af_p3;
|
||||
const double jMax_jMax = s->jMax_jMax;
|
||||
const double pd = s->pd;
|
||||
|
||||
const double h1 = sqrt(af_af / (2 * jMax_jMax) + (vMax - vf) / jMax);
|
||||
|
||||
/* Four step - Solution 3/4 */
|
||||
{
|
||||
profile = &valid_profiles[*counter];
|
||||
profile->t[0] = -a0 / jMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = (af_p3 - a0_p3) / (3 * jMax_jMax * vMax) + (a0 * v0 - af * vf + (af_af * h1) / 2) / (jMax * vMax) - (vf / vMax + 1.0) * h1 + pd / vMax;
|
||||
profile->t[4] = h1;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = h1 + af / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsVEL, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Four step */
|
||||
{
|
||||
profile = &valid_profiles[*counter];
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = a0 / jMax;
|
||||
profile->t[3] = (af_p3 - a0_p3) / (3 * jMax_jMax * vMax) + (a0 * v0 - af * vf + (af_af * h1 + a0_p3 / jMax) / 2) / (jMax * vMax) - (v0 / vMax + 1.0) * a0 / jMax - (vf / vMax + 1.0) * h1 + pd / vMax;
|
||||
profile->t[4] = h1;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = h1 + af / jMax;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsVEL, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void time_none_two_step(CRuckigPositionThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double vMax, double vMin, double aMax, double aMin, double jMax)
|
||||
{
|
||||
CRuckigProfile *profile;
|
||||
const double v0 = s->v0, a0 = s->a0, vf = s->vf, af = s->af;
|
||||
const double a0_a0 = s->a0_a0, af_af = s->af_af;
|
||||
|
||||
/* Two step */
|
||||
{
|
||||
profile = &valid_profiles[*counter];
|
||||
const double h0 = sqrt((a0_a0 + af_af) / 2 + jMax * (vf - v0)) * fabs(jMax) / jMax;
|
||||
profile->t[0] = (h0 - a0) / jMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = (h0 - af) / jMax;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsNONE, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Single step */
|
||||
{
|
||||
profile = &valid_profiles[*counter];
|
||||
profile->t[0] = (af - a0) / jMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsNONE, false, jMax, vMax, vMin, aMax, aMin)) {
|
||||
add_profile(valid_profiles, counter, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool time_all_single_step(CRuckigPositionThirdOrderStep1 *s,
|
||||
CRuckigProfile *profile, double vMax, double vMin, double aMax, double aMin)
|
||||
{
|
||||
const double v0 = s->v0, a0 = s->a0, af = s->af;
|
||||
const double v0_v0 = s->v0_v0;
|
||||
const double pd = s->pd;
|
||||
|
||||
if (fabs(af - a0) > DBL_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (fabs(a0) > DBL_EPSILON) {
|
||||
const double q = sqrt(2 * a0 * pd + v0_v0);
|
||||
|
||||
/* Solution 1 */
|
||||
profile->t[3] = (-v0 + q) / a0;
|
||||
if (profile->t[3] >= 0.0 && cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsNONE, false, 0.0, vMax, vMin, aMax, aMin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Solution 2 */
|
||||
profile->t[3] = -(v0 + q) / a0;
|
||||
if (profile->t[3] >= 0.0 && cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsNONE, false, 0.0, vMax, vMin, aMax, aMin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (fabs(v0) > DBL_EPSILON) {
|
||||
profile->t[3] = pd / v0;
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsNONE, false, 0.0, vMax, vMin, aMax, aMin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (fabs(pd) < DBL_EPSILON) {
|
||||
if (cruckig_profile_check(profile, ControlSignsUDDU, ReachedLimitsNONE, false, 0.0, vMax, vMin, aMax, aMin)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CRUCKIG_HOT
|
||||
bool cruckig_pos3_step1_get_profile(CRuckigPositionThirdOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block)
|
||||
{
|
||||
/* Zero-limits special case */
|
||||
if (s->_jMax == 0.0 || s->_aMax == 0.0 || s->_aMin == 0.0) {
|
||||
CRuckigProfile *p = &block->p_min;
|
||||
cruckig_profile_set_boundary_from_profile(p, input);
|
||||
|
||||
if (time_all_single_step(s, p, s->_vMax, s->_vMin, s->_aMax, s->_aMin)) {
|
||||
block->t_min = p->t_sum[6] + p->brake.duration + p->accel.duration;
|
||||
if (fabs(s->v0) > DBL_EPSILON || fabs(s->a0) > DBL_EPSILON) {
|
||||
block->a.valid = true;
|
||||
block->a.left = block->t_min;
|
||||
block->a.right = INFINITY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t valid_profile_counter = 0;
|
||||
cruckig_profile_set_boundary_from_profile(&s->valid_profiles[0], input);
|
||||
|
||||
if (fabs(s->vf) < DBL_EPSILON && fabs(s->af) < DBL_EPSILON) {
|
||||
const double vMax = (s->pd >= 0) ? s->_vMax : s->_vMin;
|
||||
const double vMin = (s->pd >= 0) ? s->_vMin : s->_vMax;
|
||||
const double aMax = (s->pd >= 0) ? s->_aMax : s->_aMin;
|
||||
const double aMin = (s->pd >= 0) ? s->_aMin : s->_aMax;
|
||||
const double jMax = (s->pd >= 0) ? s->_jMax : -s->_jMax;
|
||||
|
||||
if (fabs(s->v0) < DBL_EPSILON && fabs(s->a0) < DBL_EPSILON && fabs(s->pd) < DBL_EPSILON) {
|
||||
time_all_none_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, vMax, vMin, aMax, aMin, jMax, true);
|
||||
|
||||
} else {
|
||||
/* There is no blocked interval when vf==0 && af==0, so return after first found profile */
|
||||
time_all_vel(s, s->valid_profiles, &valid_profile_counter, vMax, vMin, aMax, aMin, jMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_all_none_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, vMax, vMin, aMax, aMin, jMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, vMax, vMin, aMax, aMin, jMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
|
||||
time_all_vel(s, s->valid_profiles, &valid_profile_counter, vMin, vMax, aMin, aMax, -jMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_all_none_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, vMin, vMax, aMin, aMax, -jMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, vMin, vMax, aMin, aMax, -jMax, true);
|
||||
}
|
||||
|
||||
} else {
|
||||
time_all_none_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, s->_jMax, false);
|
||||
time_all_none_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, -s->_jMax, false);
|
||||
time_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, s->_jMax, false);
|
||||
time_acc0_acc1(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, -s->_jMax, false);
|
||||
time_all_vel(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, s->_jMax, false);
|
||||
time_all_vel(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, -s->_jMax, false);
|
||||
}
|
||||
|
||||
if (valid_profile_counter == 0) {
|
||||
time_none_two_step(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, s->_jMax);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_none_two_step(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, -s->_jMax);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc0_two_step(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, s->_jMax);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc0_two_step(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, -s->_jMax);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_vel_two_step(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, s->_jMax);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_vel_two_step(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, -s->_jMax);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc1_vel_two_step(s, s->valid_profiles, &valid_profile_counter, s->_vMax, s->_vMin, s->_aMax, s->_aMin, s->_jMax);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc1_vel_two_step(s, s->valid_profiles, &valid_profile_counter, s->_vMin, s->_vMax, s->_aMin, s->_aMax, -s->_jMax);
|
||||
}
|
||||
|
||||
return_block:
|
||||
return cruckig_block_calculate(block, s->valid_profiles, valid_profile_counter, 6);
|
||||
}
|
||||
1370
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_third_step2.c
vendored
Normal file
1370
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/position_third_step2.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
539
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/profile.c
vendored
Normal file
539
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/profile.c
vendored
Normal file
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "profile.h"
|
||||
#include "roots.h"
|
||||
#include "utils.h"
|
||||
|
||||
void cruckig_profile_init(CRuckigProfile *p) {
|
||||
memset(p->t, 0, sizeof(p->t));
|
||||
memset(p->t_sum, 0, sizeof(p->t_sum));
|
||||
memset(p->j, 0, sizeof(p->j));
|
||||
memset(p->a, 0, sizeof(p->a));
|
||||
memset(p->v, 0, sizeof(p->v));
|
||||
memset(p->p, 0, sizeof(p->p));
|
||||
|
||||
cruckig_brake_init(&p->brake);
|
||||
cruckig_brake_init(&p->accel);
|
||||
|
||||
p->pf = 0.0;
|
||||
p->vf = 0.0;
|
||||
p->af = 0.0;
|
||||
|
||||
p->limits = ReachedLimitsNONE;
|
||||
p->direction = DirectionUP;
|
||||
p->control_signs = ControlSignsUDDU;
|
||||
}
|
||||
|
||||
void cruckig_profile_set_boundary(CRuckigProfile *p, double p0, double v0, double a0,
|
||||
double pf, double vf, double af) {
|
||||
p->a[0] = a0;
|
||||
p->v[0] = v0;
|
||||
p->p[0] = p0;
|
||||
p->af = af;
|
||||
p->vf = vf;
|
||||
p->pf = pf;
|
||||
}
|
||||
|
||||
void cruckig_profile_set_boundary_from_profile(CRuckigProfile *p, const CRuckigProfile *src) {
|
||||
p->a[0] = src->a[0];
|
||||
p->v[0] = src->v[0];
|
||||
p->p[0] = src->p[0];
|
||||
p->af = src->af;
|
||||
p->vf = src->vf;
|
||||
p->pf = src->pf;
|
||||
p->brake = src->brake;
|
||||
p->accel = src->accel;
|
||||
}
|
||||
|
||||
void cruckig_profile_set_boundary_for_velocity(CRuckigProfile *p, double p0, double v0, double a0,
|
||||
double vf, double af) {
|
||||
p->a[0] = a0;
|
||||
p->v[0] = v0;
|
||||
p->p[0] = p0;
|
||||
p->af = af;
|
||||
p->vf = vf;
|
||||
}
|
||||
|
||||
/* Third-order position check */
|
||||
CRUCKIG_HOT
|
||||
bool cruckig_profile_check(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
bool set_limits, double jf, double vMax, double vMin, double aMax, double aMin) {
|
||||
if (CRUCKIG_UNLIKELY(p->t[0] < 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p->t_sum[0] = p->t[0];
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
if (CRUCKIG_UNLIKELY(p->t[i + 1] < 0)) {
|
||||
return false;
|
||||
}
|
||||
p->t_sum[i + 1] = p->t_sum[i] + p->t[i + 1];
|
||||
}
|
||||
|
||||
if (lim == ReachedLimitsACC0_ACC1_VEL || lim == ReachedLimitsACC0_VEL || lim == ReachedLimitsACC1_VEL || lim == ReachedLimitsVEL) {
|
||||
if (CRUCKIG_UNLIKELY(p->t[3] < DBL_EPSILON)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (lim == ReachedLimitsACC0 || lim == ReachedLimitsACC0_ACC1) {
|
||||
if (CRUCKIG_UNLIKELY(p->t[1] < DBL_EPSILON)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (lim == ReachedLimitsACC1 || lim == ReachedLimitsACC0_ACC1) {
|
||||
if (CRUCKIG_UNLIKELY(p->t[5] < DBL_EPSILON)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (CRUCKIG_UNLIKELY(p->t_sum[6] > PROFILE_T_MAX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cs == ControlSignsUDDU) {
|
||||
p->j[0] = (p->t[0] > 0 ? jf : 0);
|
||||
p->j[1] = 0;
|
||||
p->j[2] = (p->t[2] > 0 ? -jf : 0);
|
||||
p->j[3] = 0;
|
||||
p->j[4] = (p->t[4] > 0 ? -jf : 0);
|
||||
p->j[5] = 0;
|
||||
p->j[6] = (p->t[6] > 0 ? jf : 0);
|
||||
} else {
|
||||
p->j[0] = (p->t[0] > 0 ? jf : 0);
|
||||
p->j[1] = 0;
|
||||
p->j[2] = (p->t[2] > 0 ? -jf : 0);
|
||||
p->j[3] = 0;
|
||||
p->j[4] = (p->t[4] > 0 ? jf : 0);
|
||||
p->j[5] = 0;
|
||||
p->j[6] = (p->t[6] > 0 ? -jf : 0);
|
||||
}
|
||||
|
||||
p->direction = (vMax > 0) ? DirectionUP : DirectionDOWN;
|
||||
const double vUppLim = (p->direction == DirectionUP ? vMax : vMin) + PROFILE_V_EPS;
|
||||
const double vLowLim = (p->direction == DirectionUP ? vMin : vMax) - PROFILE_V_EPS;
|
||||
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
p->a[i + 1] = p->a[i] + p->t[i] * p->j[i];
|
||||
p->v[i + 1] = p->v[i] + p->t[i] * (p->a[i] + p->t[i] * p->j[i] / 2);
|
||||
p->p[i + 1] = p->p[i] + p->t[i] * (p->v[i] + p->t[i] * (p->a[i] / 2 + p->t[i] * p->j[i] / 6));
|
||||
|
||||
if (lim == ReachedLimitsACC0_ACC1_VEL || lim == ReachedLimitsACC0_ACC1 || lim == ReachedLimitsACC0_VEL || lim == ReachedLimitsACC1_VEL || lim == ReachedLimitsVEL) {
|
||||
if (i == 2) {
|
||||
p->a[3] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (set_limits) {
|
||||
if (lim == ReachedLimitsACC1) {
|
||||
if (i == 2) {
|
||||
p->a[3] = aMin;
|
||||
}
|
||||
}
|
||||
|
||||
if (lim == ReachedLimitsACC0_ACC1) {
|
||||
if (i == 0) {
|
||||
p->a[1] = aMax;
|
||||
}
|
||||
if (i == 4) {
|
||||
p->a[5] = aMin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 1 && p->a[i + 1] * p->a[i] < -DBL_EPSILON) {
|
||||
const double v_a_zero = p->v[i] - (p->a[i] * p->a[i]) / (2 * p->j[i]);
|
||||
if (v_a_zero > vUppLim || v_a_zero < vLowLim) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p->control_signs = cs;
|
||||
p->limits = lim;
|
||||
|
||||
const double aUppLim = (p->direction == DirectionUP ? aMax : aMin) + PROFILE_A_EPS;
|
||||
const double aLowLim = (p->direction == DirectionUP ? aMin : aMax) - PROFILE_A_EPS;
|
||||
|
||||
return fabs(p->p[7] - p->pf) < PROFILE_P_PREC && fabs(p->v[7] - p->vf) < PROFILE_V_PREC && fabs(p->a[7] - p->af) < PROFILE_A_PREC
|
||||
&& p->a[1] >= aLowLim && p->a[3] >= aLowLim && p->a[5] >= aLowLim
|
||||
&& p->a[1] <= aUppLim && p->a[3] <= aUppLim && p->a[5] <= aUppLim
|
||||
&& p->v[3] <= vUppLim && p->v[4] <= vUppLim && p->v[5] <= vUppLim && p->v[6] <= vUppLim
|
||||
&& p->v[3] >= vLowLim && p->v[4] >= vLowLim && p->v[5] >= vLowLim && p->v[6] >= vLowLim;
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double jf, double vMax, double vMin, double aMax, double aMin) {
|
||||
(void)tf;
|
||||
/* Time doesn't need to be checked as every profile has a: tf - ... equation */
|
||||
return cruckig_profile_check(p, cs, lim, false, jf, vMax, vMin, aMax, aMin);
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double jf, double vMax, double vMin, double aMax, double aMin, double jMax) {
|
||||
return (fabs(jf) < fabs(jMax) + PROFILE_J_EPS) && cruckig_profile_check_with_timing(p, cs, lim, tf, jf, vMax, vMin, aMax, aMin);
|
||||
}
|
||||
|
||||
/* Third-order velocity check */
|
||||
CRUCKIG_HOT
|
||||
bool cruckig_profile_check_for_velocity(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double jf, double aMax, double aMin) {
|
||||
if (CRUCKIG_UNLIKELY(p->t[0] < 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p->t_sum[0] = p->t[0];
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
if (p->t[i + 1] < 0) {
|
||||
return false;
|
||||
}
|
||||
p->t_sum[i + 1] = p->t_sum[i] + p->t[i + 1];
|
||||
}
|
||||
|
||||
if (lim == ReachedLimitsACC0) {
|
||||
if (p->t[1] < DBL_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->t_sum[6] > PROFILE_T_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cs == ControlSignsUDDU) {
|
||||
p->j[0] = (p->t[0] > 0 ? jf : 0);
|
||||
p->j[1] = 0;
|
||||
p->j[2] = (p->t[2] > 0 ? -jf : 0);
|
||||
p->j[3] = 0;
|
||||
p->j[4] = (p->t[4] > 0 ? -jf : 0);
|
||||
p->j[5] = 0;
|
||||
p->j[6] = (p->t[6] > 0 ? jf : 0);
|
||||
} else {
|
||||
p->j[0] = (p->t[0] > 0 ? jf : 0);
|
||||
p->j[1] = 0;
|
||||
p->j[2] = (p->t[2] > 0 ? -jf : 0);
|
||||
p->j[3] = 0;
|
||||
p->j[4] = (p->t[4] > 0 ? jf : 0);
|
||||
p->j[5] = 0;
|
||||
p->j[6] = (p->t[6] > 0 ? -jf : 0);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
p->a[i + 1] = p->a[i] + p->t[i] * p->j[i];
|
||||
p->v[i + 1] = p->v[i] + p->t[i] * (p->a[i] + p->t[i] * p->j[i] / 2);
|
||||
p->p[i + 1] = p->p[i] + p->t[i] * (p->v[i] + p->t[i] * (p->a[i] / 2 + p->t[i] * p->j[i] / 6));
|
||||
}
|
||||
|
||||
p->control_signs = cs;
|
||||
p->limits = lim;
|
||||
|
||||
p->direction = (aMax > 0) ? DirectionUP : DirectionDOWN;
|
||||
const double aUppLim = (p->direction == DirectionUP ? aMax : aMin) + PROFILE_A_EPS;
|
||||
const double aLowLim = (p->direction == DirectionUP ? aMin : aMax) - PROFILE_A_EPS;
|
||||
|
||||
return fabs(p->v[7] - p->vf) < PROFILE_V_PREC && fabs(p->a[7] - p->af) < PROFILE_A_PREC
|
||||
&& p->a[1] >= aLowLim && p->a[3] >= aLowLim && p->a[5] >= aLowLim
|
||||
&& p->a[1] <= aUppLim && p->a[3] <= aUppLim && p->a[5] <= aUppLim;
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_for_velocity_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double jf, double aMax, double aMin) {
|
||||
(void)tf;
|
||||
return cruckig_profile_check_for_velocity(p, cs, lim, jf, aMax, aMin);
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_for_velocity_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double jf, double aMax, double aMin, double jMax) {
|
||||
return (fabs(jf) < fabs(jMax) + PROFILE_J_EPS) && cruckig_profile_check_for_velocity_with_timing(p, cs, lim, tf, jf, aMax, aMin);
|
||||
}
|
||||
|
||||
/* Second-order position check */
|
||||
bool cruckig_profile_check_for_second_order(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double aUp, double aDown, double vMax, double vMin) {
|
||||
if (p->t[0] < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p->t_sum[0] = p->t[0];
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
if (p->t[i + 1] < 0) {
|
||||
return false;
|
||||
}
|
||||
p->t_sum[i + 1] = p->t_sum[i] + p->t[i + 1];
|
||||
}
|
||||
|
||||
if (p->t_sum[6] > PROFILE_T_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p->j[0] = 0; p->j[1] = 0; p->j[2] = 0; p->j[3] = 0;
|
||||
p->j[4] = 0; p->j[5] = 0; p->j[6] = 0;
|
||||
|
||||
if (cs == ControlSignsUDDU) {
|
||||
p->a[0] = (p->t[0] > 0 ? aUp : 0);
|
||||
p->a[1] = 0;
|
||||
p->a[2] = (p->t[2] > 0 ? aDown : 0);
|
||||
p->a[3] = 0;
|
||||
p->a[4] = (p->t[4] > 0 ? aDown : 0);
|
||||
p->a[5] = 0;
|
||||
p->a[6] = (p->t[6] > 0 ? aUp : 0);
|
||||
p->a[7] = p->af;
|
||||
} else {
|
||||
p->a[0] = (p->t[0] > 0 ? aUp : 0);
|
||||
p->a[1] = 0;
|
||||
p->a[2] = (p->t[2] > 0 ? aDown : 0);
|
||||
p->a[3] = 0;
|
||||
p->a[4] = (p->t[4] > 0 ? aUp : 0);
|
||||
p->a[5] = 0;
|
||||
p->a[6] = (p->t[6] > 0 ? aDown : 0);
|
||||
p->a[7] = p->af;
|
||||
}
|
||||
|
||||
p->direction = (vMax > 0) ? DirectionUP : DirectionDOWN;
|
||||
const double vUppLim = (p->direction == DirectionUP ? vMax : vMin) + PROFILE_V_EPS;
|
||||
const double vLowLim = (p->direction == DirectionUP ? vMin : vMax) - PROFILE_V_EPS;
|
||||
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
p->v[i + 1] = p->v[i] + p->t[i] * p->a[i];
|
||||
p->p[i + 1] = p->p[i] + p->t[i] * (p->v[i] + p->t[i] * p->a[i] / 2);
|
||||
}
|
||||
|
||||
p->control_signs = cs;
|
||||
p->limits = lim;
|
||||
|
||||
return fabs(p->p[7] - p->pf) < PROFILE_P_PREC && fabs(p->v[7] - p->vf) < PROFILE_V_PREC
|
||||
&& p->v[2] <= vUppLim && p->v[3] <= vUppLim && p->v[4] <= vUppLim && p->v[5] <= vUppLim && p->v[6] <= vUppLim
|
||||
&& p->v[2] >= vLowLim && p->v[3] >= vLowLim && p->v[4] >= vLowLim && p->v[5] >= vLowLim && p->v[6] >= vLowLim;
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_for_second_order_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double aUp, double aDown, double vMax, double vMin) {
|
||||
(void)tf;
|
||||
return cruckig_profile_check_for_second_order(p, cs, lim, aUp, aDown, vMax, vMin);
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_for_second_order_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double aUp, double aDown, double vMax, double vMin,
|
||||
double aMax, double aMin) {
|
||||
return (aMin - PROFILE_A_EPS < aUp) && (aUp < aMax + PROFILE_A_EPS) && (aMin - PROFILE_A_EPS < aDown) && (aDown < aMax + PROFILE_A_EPS)
|
||||
&& cruckig_profile_check_for_second_order_with_timing(p, cs, lim, tf, aUp, aDown, vMax, vMin);
|
||||
}
|
||||
|
||||
/* Second-order velocity check */
|
||||
bool cruckig_profile_check_for_second_order_velocity(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double aUp) {
|
||||
/* ReachedLimits::ACC0 */
|
||||
if (p->t[1] < 0.0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p->t_sum[0] = 0;
|
||||
p->t_sum[1] = p->t[1];
|
||||
p->t_sum[2] = p->t[1];
|
||||
p->t_sum[3] = p->t[1];
|
||||
p->t_sum[4] = p->t[1];
|
||||
p->t_sum[5] = p->t[1];
|
||||
p->t_sum[6] = p->t[1];
|
||||
|
||||
if (p->t_sum[6] > PROFILE_T_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p->j[0] = 0; p->j[1] = 0; p->j[2] = 0; p->j[3] = 0;
|
||||
p->j[4] = 0; p->j[5] = 0; p->j[6] = 0;
|
||||
|
||||
p->a[0] = 0;
|
||||
p->a[1] = (p->t[1] > 0) ? aUp : 0;
|
||||
p->a[2] = 0; p->a[3] = 0; p->a[4] = 0; p->a[5] = 0; p->a[6] = 0;
|
||||
p->a[7] = p->af;
|
||||
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
p->v[i + 1] = p->v[i] + p->t[i] * p->a[i];
|
||||
p->p[i + 1] = p->p[i] + p->t[i] * (p->v[i] + p->t[i] * p->a[i] / 2);
|
||||
}
|
||||
|
||||
p->control_signs = cs;
|
||||
p->limits = lim;
|
||||
|
||||
p->direction = (aUp > 0) ? DirectionUP : DirectionDOWN;
|
||||
|
||||
return fabs(p->v[7] - p->vf) < PROFILE_V_PREC;
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_for_second_order_velocity_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double aUp) {
|
||||
(void)tf;
|
||||
return cruckig_profile_check_for_second_order_velocity(p, cs, lim, aUp);
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_for_second_order_velocity_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double aUp, double aMax, double aMin) {
|
||||
return (aMin - PROFILE_A_EPS < aUp) && (aUp < aMax + PROFILE_A_EPS)
|
||||
&& cruckig_profile_check_for_second_order_velocity_with_timing(p, cs, lim, tf, aUp);
|
||||
}
|
||||
|
||||
/* First-order position check */
|
||||
bool cruckig_profile_check_for_first_order(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double vUp) {
|
||||
/* ReachedLimits::VEL */
|
||||
if (p->t[3] < 0.0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p->t_sum[0] = 0; p->t_sum[1] = 0; p->t_sum[2] = 0;
|
||||
p->t_sum[3] = p->t[3];
|
||||
p->t_sum[4] = p->t[3]; p->t_sum[5] = p->t[3]; p->t_sum[6] = p->t[3];
|
||||
|
||||
if (p->t_sum[6] > PROFILE_T_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p->j[0] = 0; p->j[1] = 0; p->j[2] = 0; p->j[3] = 0;
|
||||
p->j[4] = 0; p->j[5] = 0; p->j[6] = 0;
|
||||
|
||||
p->a[0] = 0; p->a[1] = 0; p->a[2] = 0; p->a[3] = 0;
|
||||
p->a[4] = 0; p->a[5] = 0; p->a[6] = 0; p->a[7] = p->af;
|
||||
|
||||
p->v[0] = 0; p->v[1] = 0; p->v[2] = 0;
|
||||
p->v[3] = (p->t[3] > 0 ? vUp : 0);
|
||||
p->v[4] = 0; p->v[5] = 0; p->v[6] = 0; p->v[7] = p->vf;
|
||||
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
p->p[i + 1] = p->p[i] + p->t[i] * (p->v[i] + p->t[i] * p->a[i] / 2);
|
||||
}
|
||||
|
||||
p->control_signs = cs;
|
||||
p->limits = lim;
|
||||
|
||||
p->direction = (vUp > 0) ? DirectionUP : DirectionDOWN;
|
||||
|
||||
return fabs(p->p[7] - p->pf) < PROFILE_P_PREC;
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_for_first_order_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double vUp) {
|
||||
(void)tf;
|
||||
return cruckig_profile_check_for_first_order(p, cs, lim, vUp);
|
||||
}
|
||||
|
||||
bool cruckig_profile_check_for_first_order_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double vUp, double vMax, double vMin) {
|
||||
return (vMin - PROFILE_V_EPS < vUp) && (vUp < vMax + PROFILE_V_EPS)
|
||||
&& cruckig_profile_check_for_first_order_with_timing(p, cs, lim, tf, vUp);
|
||||
}
|
||||
|
||||
/* Position extrema helpers */
|
||||
static void check_position_extremum(double t_ext, double t_sum_val, double t_seg, double pos, double vel, double acc, double jrk, CRuckigBound *ext) {
|
||||
if (0 < t_ext && t_ext < t_seg) {
|
||||
double p_ext, v_ext, a_ext;
|
||||
cruckig_integrate(t_ext, pos, vel, acc, jrk, &p_ext, &v_ext, &a_ext);
|
||||
(void)v_ext;
|
||||
if (a_ext > 0 && p_ext < ext->min) {
|
||||
ext->min = p_ext;
|
||||
ext->t_min = t_sum_val + t_ext;
|
||||
} else if (a_ext < 0 && p_ext > ext->max) {
|
||||
ext->max = p_ext;
|
||||
ext->t_max = t_sum_val + t_ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void check_step_for_position_extremum(double t_sum_val, double t_seg, double pos, double vel, double acc, double jrk, CRuckigBound *ext) {
|
||||
if (pos < ext->min) {
|
||||
ext->min = pos;
|
||||
ext->t_min = t_sum_val;
|
||||
}
|
||||
if (pos > ext->max) {
|
||||
ext->max = pos;
|
||||
ext->t_max = t_sum_val;
|
||||
}
|
||||
|
||||
if (jrk != 0) {
|
||||
const double D = acc * acc - 2 * jrk * vel;
|
||||
if (fabs(D) < DBL_EPSILON) {
|
||||
check_position_extremum(-acc / jrk, t_sum_val, t_seg, pos, vel, acc, jrk, ext);
|
||||
} else if (D > 0.0) {
|
||||
const double D_sqrt = sqrt(D);
|
||||
check_position_extremum((-acc - D_sqrt) / jrk, t_sum_val, t_seg, pos, vel, acc, jrk, ext);
|
||||
check_position_extremum((-acc + D_sqrt) / jrk, t_sum_val, t_seg, pos, vel, acc, jrk, ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRuckigBound cruckig_profile_get_position_extrema(const CRuckigProfile *p) {
|
||||
CRuckigBound extrema;
|
||||
extrema.min = INFINITY;
|
||||
extrema.max = -INFINITY;
|
||||
extrema.t_min = 0.0;
|
||||
extrema.t_max = 0.0;
|
||||
|
||||
if (p->brake.duration > 0.0) {
|
||||
if (p->brake.t[0] > 0.0) {
|
||||
check_step_for_position_extremum(0.0, p->brake.t[0], p->brake.p[0], p->brake.v[0], p->brake.a[0], p->brake.j[0], &extrema);
|
||||
|
||||
if (p->brake.t[1] > 0.0) {
|
||||
check_step_for_position_extremum(p->brake.t[0], p->brake.t[1], p->brake.p[1], p->brake.v[1], p->brake.a[1], p->brake.j[1], &extrema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double t_current_sum = 0.0;
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
if (i > 0) {
|
||||
t_current_sum = p->t_sum[i - 1];
|
||||
}
|
||||
check_step_for_position_extremum(t_current_sum + p->brake.duration, p->t[i], p->p[i], p->v[i], p->a[i], p->j[i], &extrema);
|
||||
}
|
||||
|
||||
if (p->pf < extrema.min) {
|
||||
extrema.min = p->pf;
|
||||
extrema.t_min = p->t_sum[6] + p->brake.duration;
|
||||
}
|
||||
if (p->pf > extrema.max) {
|
||||
extrema.max = p->pf;
|
||||
extrema.t_max = p->t_sum[6] + p->brake.duration;
|
||||
}
|
||||
|
||||
return extrema;
|
||||
}
|
||||
|
||||
bool cruckig_profile_get_first_state_at_position(const CRuckigProfile *p, double pt, double *time, double time_after) {
|
||||
double t_cum = 0.0;
|
||||
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
if (p->t[i] == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fabs(p->p[i] - pt) < DBL_EPSILON && t_cum >= time_after) {
|
||||
*time = t_cum;
|
||||
return true;
|
||||
}
|
||||
|
||||
CRuckigRootSet cubic_roots = cruckig_roots_solve_cubic(p->j[i] / 6, p->a[i] / 2, p->v[i], p->p[i] - pt);
|
||||
cruckig_root_set_sort(&cubic_roots);
|
||||
for (size_t r = 0; r < cubic_roots.size; ++r) {
|
||||
double _t = cubic_roots.data[r];
|
||||
if (0 < _t && time_after - t_cum <= _t && _t <= p->t[i]) {
|
||||
*time = _t + t_cum;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
t_cum += p->t[i];
|
||||
}
|
||||
|
||||
if ((p->t[6] > 0.0 || p->t_sum[6] == 0.0) && fabs(p->pf - pt) < 1e-9 && p->t_sum[6] >= time_after) {
|
||||
*time = p->t_sum[6];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
126
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/profile.h
vendored
Normal file
126
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/profile.h
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_PROFILE_H
|
||||
#define CRUCKIG_PROFILE_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
#include "brake.h"
|
||||
|
||||
/* Constants */
|
||||
#define PROFILE_V_EPS 1e-12
|
||||
#define PROFILE_A_EPS 1e-12
|
||||
#define PROFILE_J_EPS 1e-12
|
||||
#define PROFILE_P_PREC 1e-8
|
||||
#define PROFILE_V_PREC 1e-8
|
||||
#define PROFILE_A_PREC 1e-10
|
||||
#define PROFILE_T_PREC 1e-12
|
||||
#define PROFILE_T_MAX 1e12
|
||||
|
||||
typedef enum {
|
||||
ReachedLimitsACC0_ACC1_VEL = 0,
|
||||
ReachedLimitsVEL,
|
||||
ReachedLimitsACC0,
|
||||
ReachedLimitsACC1,
|
||||
ReachedLimitsACC0_ACC1,
|
||||
ReachedLimitsACC0_VEL,
|
||||
ReachedLimitsACC1_VEL,
|
||||
ReachedLimitsNONE
|
||||
} CRuckigReachedLimits;
|
||||
|
||||
typedef enum {
|
||||
DirectionUP = 0,
|
||||
DirectionDOWN
|
||||
} CRuckigDirection;
|
||||
|
||||
typedef enum {
|
||||
ControlSignsUDDU = 0,
|
||||
ControlSignsUDUD
|
||||
} CRuckigControlSigns;
|
||||
|
||||
/* Position extrema info */
|
||||
typedef struct {
|
||||
double min, max;
|
||||
double t_min, t_max;
|
||||
} CRuckigBound;
|
||||
|
||||
/* Single-DOF kinematic profile */
|
||||
typedef struct {
|
||||
double t[7];
|
||||
double t_sum[7];
|
||||
double j[7];
|
||||
double a[8];
|
||||
double v[8];
|
||||
double p[8];
|
||||
|
||||
CRuckigBrakeProfile brake;
|
||||
CRuckigBrakeProfile accel;
|
||||
|
||||
double pf, vf, af;
|
||||
|
||||
CRuckigReachedLimits limits;
|
||||
CRuckigDirection direction;
|
||||
CRuckigControlSigns control_signs;
|
||||
} CRuckigProfile;
|
||||
|
||||
void cruckig_profile_init(CRuckigProfile *p);
|
||||
|
||||
/* Set boundary conditions */
|
||||
void cruckig_profile_set_boundary(CRuckigProfile *p, double p0, double v0, double a0,
|
||||
double pf, double vf, double af);
|
||||
void cruckig_profile_set_boundary_from_profile(CRuckigProfile *p, const CRuckigProfile *src);
|
||||
void cruckig_profile_set_boundary_for_velocity(CRuckigProfile *p, double p0, double v0, double a0,
|
||||
double vf, double af);
|
||||
|
||||
/* Third-order position check */
|
||||
bool cruckig_profile_check(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
bool set_limits, double jf, double vMax, double vMin, double aMax, double aMin);
|
||||
bool cruckig_profile_check_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double jf, double vMax, double vMin, double aMax, double aMin);
|
||||
bool cruckig_profile_check_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double jf, double vMax, double vMin, double aMax, double aMin, double jMax);
|
||||
|
||||
/* Third-order velocity check */
|
||||
bool cruckig_profile_check_for_velocity(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double jf, double aMax, double aMin);
|
||||
bool cruckig_profile_check_for_velocity_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double jf, double aMax, double aMin);
|
||||
bool cruckig_profile_check_for_velocity_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double jf, double aMax, double aMin, double jMax);
|
||||
|
||||
/* Second-order position check */
|
||||
bool cruckig_profile_check_for_second_order(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double aUp, double aDown, double vMax, double vMin);
|
||||
bool cruckig_profile_check_for_second_order_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double aUp, double aDown, double vMax, double vMin);
|
||||
bool cruckig_profile_check_for_second_order_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double aUp, double aDown, double vMax, double vMin,
|
||||
double aMax, double aMin);
|
||||
|
||||
/* Second-order velocity check */
|
||||
bool cruckig_profile_check_for_second_order_velocity(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double aUp);
|
||||
bool cruckig_profile_check_for_second_order_velocity_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double aUp);
|
||||
bool cruckig_profile_check_for_second_order_velocity_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double aUp, double aMax, double aMin);
|
||||
|
||||
/* First-order position check */
|
||||
bool cruckig_profile_check_for_first_order(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double vUp);
|
||||
bool cruckig_profile_check_for_first_order_with_timing(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double vUp);
|
||||
bool cruckig_profile_check_for_first_order_with_timing_full(CRuckigProfile *p, CRuckigControlSigns cs, CRuckigReachedLimits lim,
|
||||
double tf, double vUp, double vMax, double vMin);
|
||||
|
||||
/* Position extrema */
|
||||
CRuckigBound cruckig_profile_get_position_extrema(const CRuckigProfile *p);
|
||||
|
||||
/* First time at position */
|
||||
bool cruckig_profile_get_first_state_at_position(const CRuckigProfile *p, double pt, double *time, double time_after);
|
||||
|
||||
#endif /* CRUCKIG_PROFILE_H */
|
||||
40
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/result.h
vendored
Normal file
40
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/result.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_RESULT_H
|
||||
#define CRUCKIG_RESULT_H
|
||||
|
||||
typedef enum {
|
||||
CRuckigWorking = 0,
|
||||
CRuckigFinished = 1,
|
||||
CRuckigError = -1,
|
||||
CRuckigErrorInvalidInput = -100,
|
||||
CRuckigErrorTrajectoryDuration = -101,
|
||||
CRuckigErrorPositionalLimits = -102,
|
||||
CRuckigErrorZeroLimits = -104,
|
||||
CRuckigErrorExecutionTimeCalculation = -110,
|
||||
CRuckigErrorSynchronizationCalculation = -111
|
||||
} CRuckigResult;
|
||||
|
||||
typedef enum {
|
||||
CRuckigPosition = 0,
|
||||
CRuckigVelocity = 1
|
||||
} CRuckigControlInterface;
|
||||
|
||||
typedef enum {
|
||||
CRuckigSyncTime = 0,
|
||||
CRuckigSyncTimeIfNecessary = 1,
|
||||
CRuckigSyncPhase = 2,
|
||||
CRuckigSyncNone = 3
|
||||
} CRuckigSynchronization;
|
||||
|
||||
typedef enum {
|
||||
CRuckigContinuous = 0,
|
||||
CRuckigDiscrete = 1
|
||||
} CRuckigDurationDiscretization;
|
||||
|
||||
#endif /* CRUCKIG_RESULT_H */
|
||||
408
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/roots.c
vendored
Normal file
408
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/roots.c
vendored
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "roots.h"
|
||||
|
||||
/*
|
||||
* cruckig_cbrt() - Cube root, used for cubic/quartic polynomial solving.
|
||||
* Optimized implementation from musl libc / FreeBSD libmsun.
|
||||
* Polynomial approximation to 23 bits + one Newton step to 53 bits.
|
||||
* Error < 0.667 ulps.
|
||||
*
|
||||
* Copyright (c) 1993 Sun Microsystems, Inc. All rights reserved.
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Copyright (c) 2005-2020 Rich Felker, et al. (musl libc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*/
|
||||
double cruckig_cbrt(double x) {
|
||||
static const unsigned B1 = 715094163;
|
||||
static const unsigned B2 = 696219795;
|
||||
static const double P0 = 1.87595182427177009643;
|
||||
static const double P1 = -1.88497979543377169875;
|
||||
static const double P2 = 1.621429720105354466140;
|
||||
static const double P3 = -0.758397934778766047437;
|
||||
static const double P4 = 0.145996192886612446982;
|
||||
union { double f; unsigned long long i; } u = {x};
|
||||
double r, s, t, w;
|
||||
unsigned hx = u.i >> 32 & 0x7fffffff;
|
||||
|
||||
if (hx >= 0x7ff00000)
|
||||
return x + x;
|
||||
|
||||
if (hx < 0x00100000) {
|
||||
u.f = x * 0x1p54;
|
||||
hx = u.i >> 32 & 0x7fffffff;
|
||||
if (hx == 0) return x;
|
||||
hx = hx / 3 + B2;
|
||||
} else {
|
||||
hx = hx / 3 + B1;
|
||||
}
|
||||
u.i &= 1ULL << 63;
|
||||
u.i |= (unsigned long long)hx << 32;
|
||||
t = u.f;
|
||||
|
||||
r = (t * t) * (t / x);
|
||||
t = t * ((P0 + r * (P1 + r * P2)) + ((r * r) * r) * (P3 + r * P4));
|
||||
|
||||
u.f = t;
|
||||
u.i = (u.i + 0x80000000) & 0xffffffffc0000000ULL;
|
||||
t = u.f;
|
||||
|
||||
s = t * t;
|
||||
r = x / s;
|
||||
w = t + t;
|
||||
r = (r - t) / (w + r);
|
||||
t = t + t * r;
|
||||
return t;
|
||||
}
|
||||
|
||||
void cruckig_root_set_sort(CRuckigRootSet *s) {
|
||||
/* Insertion sort for small arrays (max 4 elements) */
|
||||
for (size_t i = 1; i < s->size; ++i) {
|
||||
double key = s->data[i];
|
||||
size_t j = i;
|
||||
while (j > 0 && s->data[j - 1] > key) {
|
||||
s->data[j] = s->data[j - 1];
|
||||
--j;
|
||||
}
|
||||
s->data[j] = key;
|
||||
}
|
||||
}
|
||||
|
||||
CRUCKIG_HOT
|
||||
CRuckigRootSet cruckig_roots_solve_cubic(double a, double b, double c, double d) {
|
||||
CRuckigRootSet roots;
|
||||
cruckig_root_set_init(&roots);
|
||||
|
||||
if (fabs(d) < DBL_EPSILON) {
|
||||
/* First solution is x = 0 */
|
||||
cruckig_root_set_insert(&roots, 0.0);
|
||||
|
||||
/* Converting to a quadratic equation */
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = 0.0;
|
||||
}
|
||||
|
||||
if (fabs(a) < DBL_EPSILON) {
|
||||
if (fabs(b) < DBL_EPSILON) {
|
||||
/* Linear equation */
|
||||
if (fabs(c) > DBL_EPSILON) {
|
||||
cruckig_root_set_insert(&roots, -d / c);
|
||||
}
|
||||
} else {
|
||||
/* Quadratic equation */
|
||||
const double discriminant = c * c - 4 * b * d;
|
||||
if (discriminant >= 0) {
|
||||
const double inv2b = 1.0 / (2 * b);
|
||||
const double y = sqrt(discriminant);
|
||||
cruckig_root_set_insert(&roots, (-c + y) * inv2b);
|
||||
cruckig_root_set_insert(&roots, (-c - y) * inv2b);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Cubic equation */
|
||||
const double inva = 1.0 / a;
|
||||
const double invaa = inva * inva;
|
||||
const double bb = b * b;
|
||||
const double bover3a = b * inva / 3;
|
||||
const double p = (a * c - bb / 3) * invaa;
|
||||
const double halfq = (2 * bb * b - 9 * a * b * c + 27 * a * a * d) / 54 * invaa * inva;
|
||||
const double yy = p * p * p / 27 + halfq * halfq;
|
||||
|
||||
const double cos120 = -0.50;
|
||||
const double sin120 = 0.866025403784438646764;
|
||||
|
||||
if (yy > DBL_EPSILON) {
|
||||
/* Sqrt is positive: one real solution */
|
||||
const double y = sqrt(yy);
|
||||
const double uuu = -halfq + y;
|
||||
const double vvv = -halfq - y;
|
||||
const double www = fabs(uuu) > fabs(vvv) ? uuu : vvv;
|
||||
const double w = cruckig_cbrt(www);
|
||||
cruckig_root_set_insert(&roots, w - p / (3 * w) - bover3a);
|
||||
} else if (yy < -DBL_EPSILON) {
|
||||
/* Sqrt is negative: three real solutions */
|
||||
const double x = -halfq;
|
||||
const double y = sqrt(-yy);
|
||||
double theta;
|
||||
double r;
|
||||
|
||||
/* Convert to polar form */
|
||||
if (fabs(x) > DBL_EPSILON) {
|
||||
theta = (x > 0.0) ? atan(y / x) : (atan(y / x) + M_PI);
|
||||
r = sqrt(x * x - yy);
|
||||
} else {
|
||||
/* Vertical line */
|
||||
theta = M_PI / 2;
|
||||
r = y;
|
||||
}
|
||||
/* Calculate cube root */
|
||||
theta /= 3;
|
||||
r = 2 * cruckig_cbrt(r);
|
||||
/* Convert to complex coordinate */
|
||||
const double ux = cos(theta) * r;
|
||||
const double uyi = sin(theta) * r;
|
||||
|
||||
cruckig_root_set_insert(&roots, ux - bover3a);
|
||||
cruckig_root_set_insert(&roots, ux * cos120 - uyi * sin120 - bover3a);
|
||||
cruckig_root_set_insert(&roots, ux * cos120 + uyi * sin120 - bover3a);
|
||||
} else {
|
||||
/* Sqrt is zero: two real solutions */
|
||||
const double www = -halfq;
|
||||
const double w = 2 * cruckig_cbrt(www);
|
||||
|
||||
cruckig_root_set_insert(&roots, w - bover3a);
|
||||
cruckig_root_set_insert(&roots, w * cos120 - bover3a);
|
||||
}
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
int cruckig_roots_solve_resolvent(double x[3], double a, double b, double c) {
|
||||
const double cos120 = -0.50;
|
||||
const double sin120 = 0.866025403784438646764;
|
||||
|
||||
a /= 3;
|
||||
const double a2 = a * a;
|
||||
double q = a2 - b / 3;
|
||||
const double r = (a * (2 * a2 - b) + c) / 2;
|
||||
const double r2 = r * r;
|
||||
const double q3 = q * q * q;
|
||||
|
||||
if (r2 < q3) {
|
||||
const double qsqrt = sqrt(q);
|
||||
double t_val = r / (q * qsqrt);
|
||||
if (t_val < -1.0) t_val = -1.0;
|
||||
if (t_val > 1.0) t_val = 1.0;
|
||||
q = -2 * qsqrt;
|
||||
|
||||
const double theta = acos(t_val) / 3;
|
||||
const double ux = cos(theta) * q;
|
||||
const double uyi = sin(theta) * q;
|
||||
x[0] = ux - a;
|
||||
x[1] = ux * cos120 - uyi * sin120 - a;
|
||||
x[2] = ux * cos120 + uyi * sin120 - a;
|
||||
return 3;
|
||||
} else {
|
||||
double A = -cruckig_cbrt(fabs(r) + sqrt(r2 - q3));
|
||||
if (r < 0.0) {
|
||||
A = -A;
|
||||
}
|
||||
const double B = (0.0 == A ? 0.0 : q / A);
|
||||
|
||||
x[0] = (A + B) - a;
|
||||
x[1] = -(A + B) / 2 - a;
|
||||
x[2] = sqrt(3.0) * (A - B) / 2;
|
||||
if (fabs(x[2]) < DBL_EPSILON) {
|
||||
x[2] = x[1];
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
CRUCKIG_HOT
|
||||
CRuckigRootSet cruckig_roots_solve_quart_monic(double a, double b, double c, double d) {
|
||||
CRuckigRootSet roots;
|
||||
cruckig_root_set_init(&roots);
|
||||
|
||||
if (fabs(d) < DBL_EPSILON) {
|
||||
if (fabs(c) < DBL_EPSILON) {
|
||||
cruckig_root_set_insert(&roots, 0.0);
|
||||
|
||||
const double D = a * a - 4 * b;
|
||||
if (fabs(D) < DBL_EPSILON) {
|
||||
cruckig_root_set_insert(&roots, -a / 2);
|
||||
} else if (D > 0.0) {
|
||||
const double sqrtD = sqrt(D);
|
||||
cruckig_root_set_insert(&roots, (-a - sqrtD) / 2);
|
||||
cruckig_root_set_insert(&roots, (-a + sqrtD) / 2);
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
if (fabs(a) < DBL_EPSILON && fabs(b) < DBL_EPSILON) {
|
||||
cruckig_root_set_insert(&roots, 0.0);
|
||||
cruckig_root_set_insert(&roots, -cruckig_cbrt(c));
|
||||
return roots;
|
||||
}
|
||||
}
|
||||
|
||||
const double a3 = -b;
|
||||
const double b3 = a * c - 4 * d;
|
||||
const double c3 = -a * a * d - c * c + 4 * b * d;
|
||||
|
||||
double x3[3];
|
||||
const int number_zeroes = cruckig_roots_solve_resolvent(x3, a3, b3, c3);
|
||||
|
||||
double y = x3[0];
|
||||
/* Choosing Y with maximal absolute value */
|
||||
if (number_zeroes != 1) {
|
||||
if (fabs(x3[1]) > fabs(y)) {
|
||||
y = x3[1];
|
||||
}
|
||||
if (fabs(x3[2]) > fabs(y)) {
|
||||
y = x3[2];
|
||||
}
|
||||
}
|
||||
|
||||
double q1, q2, p1, p2;
|
||||
double D;
|
||||
|
||||
D = y * y - 4 * d;
|
||||
if (fabs(D) < DBL_EPSILON) {
|
||||
q1 = q2 = y / 2;
|
||||
D = a * a - 4 * (b - y);
|
||||
if (fabs(D) < DBL_EPSILON) {
|
||||
p1 = p2 = a / 2;
|
||||
} else {
|
||||
const double sqrtD = sqrt(D);
|
||||
p1 = (a + sqrtD) / 2;
|
||||
p2 = (a - sqrtD) / 2;
|
||||
}
|
||||
} else {
|
||||
const double sqrtD = sqrt(D);
|
||||
q1 = (y + sqrtD) / 2;
|
||||
q2 = (y - sqrtD) / 2;
|
||||
p1 = (a * q1 - c) / (q1 - q2);
|
||||
p2 = (c - a * q2) / (q1 - q2);
|
||||
}
|
||||
|
||||
{
|
||||
const double eps = 16 * DBL_EPSILON;
|
||||
|
||||
D = p1 * p1 - 4 * q1;
|
||||
if (fabs(D) < eps) {
|
||||
cruckig_root_set_insert(&roots, -p1 / 2);
|
||||
} else if (D > 0.0) {
|
||||
const double sqrtD = sqrt(D);
|
||||
cruckig_root_set_insert(&roots, (-p1 - sqrtD) / 2);
|
||||
cruckig_root_set_insert(&roots, (-p1 + sqrtD) / 2);
|
||||
}
|
||||
|
||||
D = p2 * p2 - 4 * q2;
|
||||
if (fabs(D) < eps) {
|
||||
cruckig_root_set_insert(&roots, -p2 / 2);
|
||||
} else if (D > 0.0) {
|
||||
const double sqrtD = sqrt(D);
|
||||
cruckig_root_set_insert(&roots, (-p2 - sqrtD) / 2);
|
||||
cruckig_root_set_insert(&roots, (-p2 + sqrtD) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
double cruckig_roots_poly_eval(const double *p, size_t n, double x) {
|
||||
if (n == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double retVal = 0.0;
|
||||
|
||||
if (fabs(x) < DBL_EPSILON) {
|
||||
retVal = p[n - 1];
|
||||
} else if (x == 1.0) {
|
||||
for (int i = (int)n - 1; i >= 0; i--) {
|
||||
retVal += p[i];
|
||||
}
|
||||
} else {
|
||||
double xn = 1.0;
|
||||
for (int i = (int)n - 1; i >= 0; i--) {
|
||||
retVal += p[i] * xn;
|
||||
xn *= x;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void cruckig_roots_poly_derivative(const double *coeffs, size_t n, double *deriv) {
|
||||
for (size_t i = 0; i < n - 1; ++i) {
|
||||
deriv[i] = (double)(n - 1 - i) * coeffs[i];
|
||||
}
|
||||
}
|
||||
|
||||
double cruckig_roots_shrink_interval(const double *p, size_t n, double l, double h) {
|
||||
const size_t maxIts = 128;
|
||||
const double tolerance = 1e-14;
|
||||
|
||||
const double fl = cruckig_roots_poly_eval(p, n, l);
|
||||
const double fh = cruckig_roots_poly_eval(p, n, h);
|
||||
if (fl == 0.0) {
|
||||
return l;
|
||||
}
|
||||
if (fh == 0.0) {
|
||||
return h;
|
||||
}
|
||||
if (fl > 0.0) {
|
||||
/* swap l and h */
|
||||
double tmp = l;
|
||||
l = h;
|
||||
h = tmp;
|
||||
}
|
||||
|
||||
double rts = (l + h) / 2;
|
||||
double dxold = fabs(h - l);
|
||||
double dx = dxold;
|
||||
|
||||
/* Compute derivative coefficients (n-1 elements) */
|
||||
double deriv[16]; /* max polynomial degree supported */
|
||||
cruckig_roots_poly_derivative(p, n, deriv);
|
||||
size_t dn = n - 1;
|
||||
|
||||
double f = cruckig_roots_poly_eval(p, n, rts);
|
||||
double df = cruckig_roots_poly_eval(deriv, dn, rts);
|
||||
double temp;
|
||||
|
||||
for (size_t j = 0; j < maxIts; j++) {
|
||||
if ((((rts - h) * df - f) * ((rts - l) * df - f) > 0.0) || (fabs(2 * f) > fabs(dxold * df))) {
|
||||
dxold = dx;
|
||||
dx = (h - l) / 2;
|
||||
rts = l + dx;
|
||||
if (l == rts) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
dxold = dx;
|
||||
dx = f / df;
|
||||
temp = rts;
|
||||
rts -= dx;
|
||||
if (temp == rts) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fabs(dx) < tolerance) {
|
||||
break;
|
||||
}
|
||||
|
||||
f = cruckig_roots_poly_eval(p, n, rts);
|
||||
df = cruckig_roots_poly_eval(deriv, dn, rts);
|
||||
if (f < 0.0) {
|
||||
l = rts;
|
||||
} else {
|
||||
h = rts;
|
||||
}
|
||||
}
|
||||
|
||||
return rts;
|
||||
}
|
||||
57
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/roots.h
vendored
Normal file
57
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/roots.h
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_ROOTS_H
|
||||
#define CRUCKIG_ROOTS_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
|
||||
/* A set of positive double roots, stored on the stack */
|
||||
typedef struct {
|
||||
double data[4];
|
||||
size_t size;
|
||||
} CRuckigRootSet;
|
||||
|
||||
CRUCKIG_FORCE_INLINE void cruckig_root_set_init(CRuckigRootSet *s) {
|
||||
s->size = 0;
|
||||
}
|
||||
|
||||
CRUCKIG_FORCE_INLINE void cruckig_root_set_insert(CRuckigRootSet *s, double value) {
|
||||
if (value >= 0.0) {
|
||||
s->data[s->size] = value;
|
||||
s->size++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cube root, portable replacement for cbrt() (not available in kernel).
|
||||
* Optimized implementation from musl libc / FreeBSD libmsun.
|
||||
*/
|
||||
double cruckig_cbrt(double x);
|
||||
|
||||
/* Sort the root set (simple insertion sort for small N) */
|
||||
void cruckig_root_set_sort(CRuckigRootSet *s);
|
||||
|
||||
/* Solve a*x^3 + b*x^2 + c*x + d = 0, returning positive roots */
|
||||
CRuckigRootSet cruckig_roots_solve_cubic(double a, double b, double c, double d);
|
||||
|
||||
/* Solve resolvent equation, returns number of zeros */
|
||||
int cruckig_roots_solve_resolvent(double x[3], double a, double b, double c);
|
||||
|
||||
/* Solve monic quartic x^4 + a*x^3 + b*x^2 + c*x + d = 0 */
|
||||
CRuckigRootSet cruckig_roots_solve_quart_monic(double a, double b, double c, double d);
|
||||
|
||||
/* Evaluate polynomial of order N at x. Coefficients in descending order: p[0]*x^(N-1) + ... + p[N-1] */
|
||||
double cruckig_roots_poly_eval(const double *p, size_t n, double x);
|
||||
|
||||
/* Calculate derivative coefficients */
|
||||
void cruckig_roots_poly_derivative(const double *coeffs, size_t n, double *deriv);
|
||||
|
||||
/* Safe Newton method: find root in [l, h] where p(l)*p(h) < 0 */
|
||||
double cruckig_roots_shrink_interval(const double *p, size_t n, double l, double h);
|
||||
|
||||
#endif /* CRUCKIG_ROOTS_H */
|
||||
315
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/trajectory.c
vendored
Normal file
315
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/trajectory.c
vendored
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#include "trajectory.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
CRuckigTrajectory* cruckig_trajectory_create(size_t dofs) {
|
||||
CRuckigTrajectory *traj = (CRuckigTrajectory*)cruckig_calloc(1, sizeof(CRuckigTrajectory));
|
||||
if (!traj) return NULL;
|
||||
|
||||
traj->degrees_of_freedom = dofs;
|
||||
traj->num_sections = 1;
|
||||
traj->section_capacity = 1;
|
||||
traj->duration = 0.0;
|
||||
|
||||
traj->profiles = (CRuckigProfile*)cruckig_calloc(dofs, sizeof(CRuckigProfile));
|
||||
traj->cumulative_times = (double*)cruckig_calloc(1, sizeof(double));
|
||||
traj->independent_min_durations = (double*)cruckig_calloc(dofs, sizeof(double));
|
||||
traj->position_extrema = (CRuckigBound*)cruckig_calloc(dofs, sizeof(CRuckigBound));
|
||||
|
||||
if (!traj->profiles || !traj->cumulative_times ||
|
||||
!traj->independent_min_durations || !traj->position_extrema) {
|
||||
cruckig_trajectory_destroy(traj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
cruckig_profile_init(&traj->profiles[dof]);
|
||||
}
|
||||
|
||||
return traj;
|
||||
}
|
||||
|
||||
void cruckig_trajectory_destroy(CRuckigTrajectory *traj) {
|
||||
if (!traj) return;
|
||||
cruckig_free(traj->profiles);
|
||||
cruckig_free(traj->cumulative_times);
|
||||
cruckig_free(traj->independent_min_durations);
|
||||
cruckig_free(traj->position_extrema);
|
||||
cruckig_free(traj);
|
||||
}
|
||||
|
||||
bool cruckig_trajectory_resize(CRuckigTrajectory *traj, size_t num_sections) {
|
||||
if (!traj || num_sections == 0) return false;
|
||||
|
||||
const size_t dofs = traj->degrees_of_freedom;
|
||||
|
||||
if (num_sections > traj->section_capacity) {
|
||||
CRuckigProfile *new_profiles = (CRuckigProfile*)cruckig_realloc(
|
||||
traj->profiles, num_sections * dofs * sizeof(CRuckigProfile));
|
||||
double *new_times = (double*)cruckig_realloc(
|
||||
traj->cumulative_times, num_sections * sizeof(double));
|
||||
|
||||
if (!new_profiles || !new_times) {
|
||||
/* Restore on failure */
|
||||
if (new_profiles) traj->profiles = new_profiles;
|
||||
if (new_times) traj->cumulative_times = new_times;
|
||||
return false;
|
||||
}
|
||||
|
||||
traj->profiles = new_profiles;
|
||||
traj->cumulative_times = new_times;
|
||||
traj->section_capacity = num_sections;
|
||||
|
||||
/* Initialize new profiles */
|
||||
for (size_t s = traj->num_sections; s < num_sections; ++s) {
|
||||
for (size_t d = 0; d < dofs; ++d) {
|
||||
cruckig_profile_init(&traj->profiles[s * dofs + d]);
|
||||
}
|
||||
traj->cumulative_times[s] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
traj->num_sections = num_sections;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* state_to_integrate_from: Determine the integration base state at a given time.
|
||||
* Supports multi-section trajectories via binary search on cumulative_times.
|
||||
*/
|
||||
static void state_to_integrate_from(const CRuckigTrajectory *traj, double time,
|
||||
size_t *new_section,
|
||||
double *t_out, double *p_out, double *v_out,
|
||||
double *a_out, double *j_out)
|
||||
{
|
||||
const size_t dofs = traj->degrees_of_freedom;
|
||||
const size_t nsec = traj->num_sections;
|
||||
|
||||
if (time >= traj->duration) {
|
||||
/* Past the end of trajectory */
|
||||
*new_section = nsec;
|
||||
size_t last = nsec - 1;
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
const CRuckigProfile *prof = &traj->profiles[last * dofs + dof];
|
||||
double t_pre = prof->brake.duration;
|
||||
double t_diff = time - (traj->duration - (t_pre + prof->t_sum[6]) + t_pre + prof->t_sum[6]);
|
||||
/* Simplify: time past the end of last section's profile */
|
||||
double section_start = (last > 0) ? traj->cumulative_times[last - 1] : 0.0;
|
||||
t_diff = time - section_start - t_pre - prof->t_sum[6];
|
||||
t_out[dof] = t_diff;
|
||||
p_out[dof] = prof->p[7];
|
||||
v_out[dof] = prof->v[7];
|
||||
a_out[dof] = prof->a[7];
|
||||
j_out[dof] = 0.0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Binary search to find current section */
|
||||
size_t section = 0;
|
||||
if (nsec > 1) {
|
||||
size_t lo = 0, hi = nsec;
|
||||
while (lo < hi) {
|
||||
size_t mid = lo + (hi - lo) / 2;
|
||||
if (traj->cumulative_times[mid] <= time) {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
section = lo;
|
||||
if (section >= nsec) section = nsec - 1;
|
||||
}
|
||||
|
||||
*new_section = section;
|
||||
|
||||
/* Time offset within this section */
|
||||
double section_start = (section > 0) ? traj->cumulative_times[section - 1] : 0.0;
|
||||
double t_diff = time - section_start;
|
||||
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
const CRuckigProfile *prof = &traj->profiles[section * dofs + dof];
|
||||
double t_diff_dof = t_diff;
|
||||
|
||||
/* Brake pre-trajectory (only in first section, or in each section for waypoints) */
|
||||
if (prof->brake.duration > 0.0) {
|
||||
if (t_diff_dof < prof->brake.duration) {
|
||||
size_t index = (t_diff_dof < prof->brake.t[0]) ? 0 : 1;
|
||||
if (index > 0) {
|
||||
t_diff_dof -= prof->brake.t[index - 1];
|
||||
}
|
||||
t_out[dof] = t_diff_dof;
|
||||
p_out[dof] = prof->brake.p[index];
|
||||
v_out[dof] = prof->brake.v[index];
|
||||
a_out[dof] = prof->brake.a[index];
|
||||
j_out[dof] = prof->brake.j[index];
|
||||
continue;
|
||||
} else {
|
||||
t_diff_dof -= prof->brake.duration;
|
||||
}
|
||||
}
|
||||
|
||||
/* Non-time synchronization: past the end of this DOF's profile */
|
||||
if (t_diff_dof >= prof->t_sum[6]) {
|
||||
t_out[dof] = t_diff_dof - prof->t_sum[6];
|
||||
p_out[dof] = prof->p[7];
|
||||
v_out[dof] = prof->v[7];
|
||||
a_out[dof] = prof->a[7];
|
||||
j_out[dof] = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Binary search in t_sum[0..6] */
|
||||
size_t index_dof = 0;
|
||||
{
|
||||
size_t lo = 0, hi = 7;
|
||||
while (lo < hi) {
|
||||
size_t mid = lo + (hi - lo) / 2;
|
||||
if (prof->t_sum[mid] <= t_diff_dof) {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
index_dof = lo;
|
||||
}
|
||||
|
||||
if (index_dof > 0) {
|
||||
t_diff_dof -= prof->t_sum[index_dof - 1];
|
||||
}
|
||||
|
||||
t_out[dof] = t_diff_dof;
|
||||
p_out[dof] = prof->p[index_dof];
|
||||
v_out[dof] = prof->v[index_dof];
|
||||
a_out[dof] = prof->a[index_dof];
|
||||
j_out[dof] = prof->j[index_dof];
|
||||
}
|
||||
}
|
||||
|
||||
CRUCKIG_HOT
|
||||
void cruckig_trajectory_at_time(const CRuckigTrajectory *traj, double time,
|
||||
double * CRUCKIG_RESTRICT new_position,
|
||||
double * CRUCKIG_RESTRICT new_velocity,
|
||||
double * CRUCKIG_RESTRICT new_acceleration,
|
||||
double * CRUCKIG_RESTRICT new_jerk,
|
||||
size_t *new_section)
|
||||
{
|
||||
const size_t dofs = traj->degrees_of_freedom;
|
||||
|
||||
/* Implementation limit: max 16 DOF (stack-allocated work arrays) */
|
||||
double t_buf[16], p_buf[16], v_buf[16], a_buf[16], j_buf[16];
|
||||
const size_t ndofs = (dofs > 16) ? 16 : dofs;
|
||||
|
||||
state_to_integrate_from(traj, time, new_section, t_buf, p_buf, v_buf, a_buf, j_buf);
|
||||
|
||||
for (size_t dof = 0; dof < ndofs; ++dof) {
|
||||
double p_out, v_out, a_out;
|
||||
cruckig_integrate(t_buf[dof], p_buf[dof], v_buf[dof], a_buf[dof], j_buf[dof],
|
||||
&p_out, &v_out, &a_out);
|
||||
new_position[dof] = p_out;
|
||||
new_velocity[dof] = v_out;
|
||||
new_acceleration[dof] = a_out;
|
||||
if (new_jerk) {
|
||||
new_jerk[dof] = j_buf[dof];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cruckig_trajectory_at_time_simple(const CRuckigTrajectory *traj, double time,
|
||||
double *new_position, double *new_velocity,
|
||||
double *new_acceleration)
|
||||
{
|
||||
size_t new_section;
|
||||
cruckig_trajectory_at_time(traj, time, new_position, new_velocity,
|
||||
new_acceleration, NULL, &new_section);
|
||||
}
|
||||
|
||||
double cruckig_trajectory_get_duration(const CRuckigTrajectory *traj) {
|
||||
return traj->duration;
|
||||
}
|
||||
|
||||
size_t cruckig_trajectory_get_intermediate_durations(const CRuckigTrajectory *traj,
|
||||
double *out_durations)
|
||||
{
|
||||
for (size_t s = 0; s < traj->num_sections; ++s) {
|
||||
out_durations[s] = traj->cumulative_times[s];
|
||||
}
|
||||
return traj->num_sections;
|
||||
}
|
||||
|
||||
void cruckig_trajectory_get_position_extrema(CRuckigTrajectory *traj) {
|
||||
const size_t dofs = traj->degrees_of_freedom;
|
||||
for (size_t dof = 0; dof < dofs; ++dof) {
|
||||
/* Initialize from first section */
|
||||
CRuckigBound bound = cruckig_profile_get_position_extrema(&traj->profiles[dof]);
|
||||
|
||||
/* Merge across all sections */
|
||||
for (size_t s = 1; s < traj->num_sections; ++s) {
|
||||
double section_start = traj->cumulative_times[s - 1];
|
||||
CRuckigBound sb = cruckig_profile_get_position_extrema(
|
||||
&traj->profiles[s * dofs + dof]);
|
||||
if (sb.min < bound.min) {
|
||||
bound.min = sb.min;
|
||||
bound.t_min = sb.t_min + section_start;
|
||||
}
|
||||
if (sb.max > bound.max) {
|
||||
bound.max = sb.max;
|
||||
bound.t_max = sb.t_max + section_start;
|
||||
}
|
||||
}
|
||||
|
||||
traj->position_extrema[dof] = bound;
|
||||
}
|
||||
}
|
||||
|
||||
bool cruckig_trajectory_get_first_time_at_position(const CRuckigTrajectory *traj,
|
||||
size_t dof, double position,
|
||||
double *time, double time_after)
|
||||
{
|
||||
if (dof >= traj->degrees_of_freedom) return false;
|
||||
|
||||
const size_t dofs = traj->degrees_of_freedom;
|
||||
|
||||
/* Search through all sections */
|
||||
for (size_t s = 0; s < traj->num_sections; ++s) {
|
||||
double section_start = (s > 0) ? traj->cumulative_times[s - 1] : 0.0;
|
||||
double adjusted_time_after = time_after - section_start;
|
||||
if (adjusted_time_after < 0.0) adjusted_time_after = 0.0;
|
||||
|
||||
if (cruckig_profile_get_first_state_at_position(
|
||||
&traj->profiles[s * dofs + dof], position, time, adjusted_time_after)) {
|
||||
*time += section_start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void cruckig_trajectory_get_independent_min_durations(const CRuckigTrajectory *traj,
|
||||
double *out_durations)
|
||||
{
|
||||
for (size_t dof = 0; dof < traj->degrees_of_freedom; ++dof) {
|
||||
out_durations[dof] = traj->independent_min_durations[dof];
|
||||
}
|
||||
}
|
||||
|
||||
const CRuckigProfile* cruckig_trajectory_get_profile(const CRuckigTrajectory *traj, size_t dof)
|
||||
{
|
||||
if (dof >= traj->degrees_of_freedom) return NULL;
|
||||
return &traj->profiles[dof];
|
||||
}
|
||||
|
||||
const CRuckigProfile* cruckig_trajectory_get_section_profile(const CRuckigTrajectory *traj,
|
||||
size_t section, size_t dof)
|
||||
{
|
||||
if (section >= traj->num_sections || dof >= traj->degrees_of_freedom) return NULL;
|
||||
return &traj->profiles[section * traj->degrees_of_freedom + dof];
|
||||
}
|
||||
71
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/trajectory.h
vendored
Normal file
71
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/trajectory.h
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_TRAJECTORY_H
|
||||
#define CRUCKIG_TRAJECTORY_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
#include "profile.h"
|
||||
|
||||
typedef struct {
|
||||
size_t degrees_of_freedom;
|
||||
|
||||
/* Multi-section support: profiles[section * dofs + dof] */
|
||||
CRuckigProfile *profiles; /* Array of num_sections * dofs profiles */
|
||||
size_t num_sections; /* Number of sections (1 for state-to-state) */
|
||||
size_t section_capacity; /* Allocated capacity for sections */
|
||||
double duration;
|
||||
double *cumulative_times; /* Array of num_sections cumulative durations */
|
||||
|
||||
double *independent_min_durations; /* Array of dofs */
|
||||
CRuckigBound *position_extrema; /* Array of dofs */
|
||||
} CRuckigTrajectory;
|
||||
|
||||
/* Create trajectory for single-section (backward compatible) */
|
||||
CRuckigTrajectory* cruckig_trajectory_create(size_t dofs);
|
||||
void cruckig_trajectory_destroy(CRuckigTrajectory *traj);
|
||||
|
||||
/* Resize trajectory for multi-section (num_sections = max_waypoints + 1) */
|
||||
bool cruckig_trajectory_resize(CRuckigTrajectory *traj, size_t num_sections);
|
||||
|
||||
/* Query trajectory state at time */
|
||||
void cruckig_trajectory_at_time(const CRuckigTrajectory *traj, double time,
|
||||
double *new_position, double *new_velocity,
|
||||
double *new_acceleration, double *new_jerk,
|
||||
size_t *new_section);
|
||||
|
||||
/* Simplified version without jerk/section */
|
||||
void cruckig_trajectory_at_time_simple(const CRuckigTrajectory *traj, double time,
|
||||
double *new_position, double *new_velocity,
|
||||
double *new_acceleration);
|
||||
|
||||
double cruckig_trajectory_get_duration(const CRuckigTrajectory *traj);
|
||||
|
||||
/* Get intermediate durations (cumulative times array). Returns num_sections. */
|
||||
size_t cruckig_trajectory_get_intermediate_durations(const CRuckigTrajectory *traj,
|
||||
double *out_durations);
|
||||
|
||||
/* Get position extrema for all DOFs */
|
||||
void cruckig_trajectory_get_position_extrema(CRuckigTrajectory *traj);
|
||||
|
||||
/* Get first time at position for a DOF. Returns true if found. */
|
||||
bool cruckig_trajectory_get_first_time_at_position(const CRuckigTrajectory *traj,
|
||||
size_t dof, double position,
|
||||
double *time, double time_after);
|
||||
|
||||
/* Get independent minimum durations (one per DOF). Caller provides array of dofs. */
|
||||
void cruckig_trajectory_get_independent_min_durations(const CRuckigTrajectory *traj,
|
||||
double *out_durations);
|
||||
|
||||
/* Get the underlying profile for a specific DOF in a section (read-only). */
|
||||
const CRuckigProfile* cruckig_trajectory_get_profile(const CRuckigTrajectory *traj, size_t dof);
|
||||
|
||||
/* Get profile for specific section and DOF. */
|
||||
const CRuckigProfile* cruckig_trajectory_get_section_profile(const CRuckigTrajectory *traj,
|
||||
size_t section, size_t dof);
|
||||
|
||||
#endif /* CRUCKIG_TRAJECTORY_H */
|
||||
26
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/utils.h
vendored
Normal file
26
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/utils.h
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_UTILS_H
|
||||
#define CRUCKIG_UTILS_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
|
||||
CRUCKIG_FORCE_INLINE void cruckig_integrate(double t, double p0, double v0, double a0, double j,
|
||||
double * CRUCKIG_RESTRICT p_out,
|
||||
double * CRUCKIG_RESTRICT v_out,
|
||||
double * CRUCKIG_RESTRICT a_out) {
|
||||
*p_out = p0 + t * (v0 + t * (a0 / 2.0 + t * j / 6.0));
|
||||
*v_out = v0 + t * (a0 + t * j / 2.0);
|
||||
*a_out = a0 + t * j;
|
||||
}
|
||||
|
||||
CRUCKIG_FORCE_INLINE double cruckig_pow2(double v) {
|
||||
return v * v;
|
||||
}
|
||||
|
||||
#endif /* CRUCKIG_UTILS_H */
|
||||
63
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity.h
vendored
Normal file
63
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity.h
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#ifndef CRUCKIG_VELOCITY_H
|
||||
#define CRUCKIG_VELOCITY_H
|
||||
|
||||
#include "cruckig_internal.h"
|
||||
#include "profile.h"
|
||||
#include "block.h"
|
||||
|
||||
/* ---- Third Order Step 1 ---- */
|
||||
typedef struct {
|
||||
double a0, af;
|
||||
double _aMax, _aMin, _jMax;
|
||||
double vd;
|
||||
CRuckigProfile valid_profiles[3];
|
||||
} CRuckigVelocityThirdOrderStep1;
|
||||
|
||||
void cruckig_vel3_step1_init(CRuckigVelocityThirdOrderStep1 *s,
|
||||
double v0, double a0, double vf, double af,
|
||||
double aMax, double aMin, double jMax);
|
||||
bool cruckig_vel3_step1_get_profile(CRuckigVelocityThirdOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block);
|
||||
|
||||
/* ---- Third Order Step 2 ---- */
|
||||
typedef struct {
|
||||
double a0, tf, af;
|
||||
double _aMax, _aMin, _jMax;
|
||||
double vd, ad;
|
||||
} CRuckigVelocityThirdOrderStep2;
|
||||
|
||||
void cruckig_vel3_step2_init(CRuckigVelocityThirdOrderStep2 *s,
|
||||
double tf, double v0, double a0, double vf, double af,
|
||||
double aMax, double aMin, double jMax);
|
||||
bool cruckig_vel3_step2_get_profile(CRuckigVelocityThirdOrderStep2 *s, CRuckigProfile *profile);
|
||||
|
||||
/* ---- Second Order Step 1 ---- */
|
||||
typedef struct {
|
||||
double _aMax, _aMin;
|
||||
double vd;
|
||||
} CRuckigVelocitySecondOrderStep1;
|
||||
|
||||
void cruckig_vel2_step1_init(CRuckigVelocitySecondOrderStep1 *s,
|
||||
double v0, double vf, double aMax, double aMin);
|
||||
bool cruckig_vel2_step1_get_profile(CRuckigVelocitySecondOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block);
|
||||
|
||||
/* ---- Second Order Step 2 ---- */
|
||||
typedef struct {
|
||||
double tf;
|
||||
double _aMax, _aMin;
|
||||
double vd;
|
||||
} CRuckigVelocitySecondOrderStep2;
|
||||
|
||||
void cruckig_vel2_step2_init(CRuckigVelocitySecondOrderStep2 *s,
|
||||
double tf, double v0, double vf, double aMax, double aMin);
|
||||
bool cruckig_vel2_step2_get_profile(CRuckigVelocitySecondOrderStep2 *s, CRuckigProfile *profile);
|
||||
|
||||
#endif /* CRUCKIG_VELOCITY_H */
|
||||
40
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity_second_step1.c
vendored
Normal file
40
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity_second_step1.c
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#include "velocity.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
|
||||
void cruckig_vel2_step1_init(CRuckigVelocitySecondOrderStep1 *s,
|
||||
double v0, double vf, double aMax, double aMin)
|
||||
{
|
||||
s->_aMax = aMax;
|
||||
s->_aMin = aMin;
|
||||
s->vd = vf - v0;
|
||||
}
|
||||
|
||||
bool cruckig_vel2_step1_get_profile(CRuckigVelocitySecondOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block)
|
||||
{
|
||||
CRuckigProfile *p = &block->p_min;
|
||||
cruckig_profile_set_boundary_from_profile(p, input);
|
||||
|
||||
const double af = (s->vd > 0) ? s->_aMax : s->_aMin;
|
||||
p->t[0] = 0;
|
||||
p->t[1] = s->vd / af;
|
||||
p->t[2] = 0;
|
||||
p->t[3] = 0;
|
||||
p->t[4] = 0;
|
||||
p->t[5] = 0;
|
||||
p->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_second_order_velocity(p, ControlSignsUDDU, ReachedLimitsACC0, af)) {
|
||||
block->t_min = p->t_sum[6] + p->brake.duration + p->accel.duration;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
39
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity_second_step2.c
vendored
Normal file
39
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity_second_step2.c
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
#include "velocity.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
|
||||
void cruckig_vel2_step2_init(CRuckigVelocitySecondOrderStep2 *s,
|
||||
double tf, double v0, double vf, double aMax, double aMin)
|
||||
{
|
||||
s->tf = tf;
|
||||
s->_aMax = aMax;
|
||||
s->_aMin = aMin;
|
||||
s->vd = vf - v0;
|
||||
}
|
||||
|
||||
bool cruckig_vel2_step2_get_profile(CRuckigVelocitySecondOrderStep2 *s, CRuckigProfile *profile)
|
||||
{
|
||||
const double af = s->vd / s->tf;
|
||||
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = s->tf;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_second_order_velocity_with_timing_full(profile, ControlSignsUDDU, ReachedLimitsNONE, s->tf, af, s->_aMax, s->_aMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
187
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity_third_step1.c
vendored
Normal file
187
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity_third_step1.c
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "velocity.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
|
||||
/* ---- Internal helper functions ---- */
|
||||
|
||||
static void time_acc0(const CRuckigVelocityThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double aMax, double aMin, double jMax, bool return_after_found)
|
||||
{
|
||||
(void)return_after_found;
|
||||
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
|
||||
profile->t[0] = (-s->a0 + aMax) / jMax;
|
||||
profile->t[1] = (s->a0 * s->a0 + s->af * s->af) / (2 * aMax * jMax) - aMax / jMax + s->vd / aMax;
|
||||
profile->t[2] = (-s->af + aMax) / jMax;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_velocity(profile, ControlSignsUDDU, ReachedLimitsACC0, jMax, aMax, aMin)) {
|
||||
(*counter)++;
|
||||
if (*counter < 3) {
|
||||
cruckig_profile_set_boundary_from_profile(&valid_profiles[*counter], profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void time_none(const CRuckigVelocityThirdOrderStep1 *s,
|
||||
CRuckigProfile *valid_profiles, size_t *counter,
|
||||
double aMax, double aMin, double jMax, bool return_after_found)
|
||||
{
|
||||
double h1 = (s->a0 * s->a0 + s->af * s->af) / 2 + jMax * s->vd;
|
||||
if (h1 >= 0.0) {
|
||||
h1 = sqrt(h1);
|
||||
|
||||
/* Solution 1 */
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
|
||||
profile->t[0] = -(s->a0 + h1) / jMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = -(s->af + h1) / jMax;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_velocity(profile, ControlSignsUDDU, ReachedLimitsNONE, jMax, aMax, aMin)) {
|
||||
(*counter)++;
|
||||
if (*counter < 3) {
|
||||
cruckig_profile_set_boundary_from_profile(&valid_profiles[*counter], profile);
|
||||
}
|
||||
if (return_after_found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Solution 2 */
|
||||
{
|
||||
CRuckigProfile *profile = &valid_profiles[*counter];
|
||||
|
||||
profile->t[0] = (-s->a0 + h1) / jMax;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = (-s->af + h1) / jMax;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_velocity(profile, ControlSignsUDDU, ReachedLimitsNONE, jMax, aMax, aMin)) {
|
||||
(*counter)++;
|
||||
if (*counter < 3) {
|
||||
cruckig_profile_set_boundary_from_profile(&valid_profiles[*counter], profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool time_all_single_step(const CRuckigVelocityThirdOrderStep1 *s,
|
||||
CRuckigProfile *profile,
|
||||
double aMax, double aMin, double jMax)
|
||||
{
|
||||
(void)jMax;
|
||||
|
||||
if (fabs(s->af - s->a0) > DBL_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = 0;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (fabs(s->a0) > DBL_EPSILON) {
|
||||
profile->t[3] = s->vd / s->a0;
|
||||
if (cruckig_profile_check_for_velocity(profile, ControlSignsUDDU, ReachedLimitsNONE, 0.0, aMax, aMin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (fabs(s->vd) < DBL_EPSILON) {
|
||||
if (cruckig_profile_check_for_velocity(profile, ControlSignsUDDU, ReachedLimitsNONE, 0.0, aMax, aMin)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* ---- Public interface ---- */
|
||||
|
||||
void cruckig_vel3_step1_init(CRuckigVelocityThirdOrderStep1 *s,
|
||||
double v0, double a0, double vf, double af,
|
||||
double aMax, double aMin, double jMax)
|
||||
{
|
||||
s->a0 = a0;
|
||||
s->af = af;
|
||||
s->_aMax = aMax;
|
||||
s->_aMin = aMin;
|
||||
s->_jMax = jMax;
|
||||
s->vd = vf - v0;
|
||||
}
|
||||
|
||||
bool cruckig_vel3_step1_get_profile(CRuckigVelocityThirdOrderStep1 *s,
|
||||
const CRuckigProfile *input, CRuckigBlock *block)
|
||||
{
|
||||
/* Zero-limits special case */
|
||||
if (s->_jMax == 0.0) {
|
||||
CRuckigProfile *p = &block->p_min;
|
||||
cruckig_profile_set_boundary_from_profile(p, input);
|
||||
|
||||
if (time_all_single_step(s, p, s->_aMax, s->_aMin, s->_jMax)) {
|
||||
block->t_min = p->t_sum[6] + p->brake.duration + p->accel.duration;
|
||||
if (fabs(s->a0) > DBL_EPSILON) {
|
||||
block->a.valid = true;
|
||||
block->a.left = block->t_min;
|
||||
block->a.right = INFINITY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t valid_profile_counter = 0;
|
||||
cruckig_profile_set_boundary_from_profile(&s->valid_profiles[0], input);
|
||||
|
||||
if (fabs(s->af) < DBL_EPSILON) {
|
||||
/* There is no blocked interval when af==0, so return after first found profile */
|
||||
const double aMax = (s->vd >= 0) ? s->_aMax : s->_aMin;
|
||||
const double aMin = (s->vd >= 0) ? s->_aMin : s->_aMax;
|
||||
const double jMax = (s->vd >= 0) ? s->_jMax : -s->_jMax;
|
||||
|
||||
time_none(s, s->valid_profiles, &valid_profile_counter, aMax, aMin, jMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc0(s, s->valid_profiles, &valid_profile_counter, aMax, aMin, jMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
|
||||
time_none(s, s->valid_profiles, &valid_profile_counter, aMin, aMax, -jMax, true);
|
||||
if (valid_profile_counter > 0) { goto return_block; }
|
||||
time_acc0(s, s->valid_profiles, &valid_profile_counter, aMin, aMax, -jMax, true);
|
||||
|
||||
} else {
|
||||
time_none(s, s->valid_profiles, &valid_profile_counter, s->_aMax, s->_aMin, s->_jMax, false);
|
||||
time_none(s, s->valid_profiles, &valid_profile_counter, s->_aMin, s->_aMax, -s->_jMax, false);
|
||||
time_acc0(s, s->valid_profiles, &valid_profile_counter, s->_aMax, s->_aMin, s->_jMax, false);
|
||||
time_acc0(s, s->valid_profiles, &valid_profile_counter, s->_aMin, s->_aMax, -s->_jMax, false);
|
||||
}
|
||||
|
||||
return_block:
|
||||
return cruckig_block_calculate(block, s->valid_profiles, valid_profile_counter, 3);
|
||||
}
|
||||
146
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity_third_step2.c
vendored
Normal file
146
wasm-port/vendor/linuxcnc/src/emc/tp/cruckig/velocity_third_step2.c
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* cruckig - Pure C99 port of the Ruckig trajectory generation library
|
||||
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
|
||||
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
|
||||
*
|
||||
* License: MIT, see the LICENSE file in this directory.
|
||||
*/
|
||||
|
||||
#include "velocity.h"
|
||||
#include "block.h"
|
||||
#include "profile.h"
|
||||
|
||||
/* ---- Internal helper functions ---- */
|
||||
|
||||
static bool time_acc0(CRuckigVelocityThirdOrderStep2 *s, CRuckigProfile *profile,
|
||||
double aMax, double aMin, double jMax)
|
||||
{
|
||||
/* UD Solution 1/2 */
|
||||
{
|
||||
const double h1 = sqrt((-s->ad * s->ad + 2 * jMax * ((s->a0 + s->af) * s->tf - 2 * s->vd)) / (jMax * jMax) + s->tf * s->tf);
|
||||
|
||||
profile->t[0] = s->ad / (2 * jMax) + (s->tf - h1) / 2;
|
||||
profile->t[1] = h1;
|
||||
profile->t[2] = s->tf - (profile->t[0] + h1);
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_velocity_with_timing(profile, ControlSignsUDDU, ReachedLimitsACC0, s->tf, jMax, aMax, aMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* UU Solution */
|
||||
{
|
||||
const double h1 = (-s->ad + jMax * s->tf);
|
||||
|
||||
profile->t[0] = -s->ad * s->ad / (2 * jMax * h1) + (s->vd - s->a0 * s->tf) / h1;
|
||||
profile->t[1] = -s->ad / jMax + s->tf;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = s->tf - (profile->t[0] + profile->t[1]);
|
||||
|
||||
if (cruckig_profile_check_for_velocity_with_timing(profile, ControlSignsUDDU, ReachedLimitsACC0, s->tf, jMax, aMax, aMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* UU Solution - 2 step */
|
||||
{
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = -s->ad / jMax + s->tf;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = s->ad / jMax;
|
||||
|
||||
if (cruckig_profile_check_for_velocity_with_timing(profile, ControlSignsUDDU, ReachedLimitsACC0, s->tf, jMax, aMax, aMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool time_none(CRuckigVelocityThirdOrderStep2 *s, CRuckigProfile *profile,
|
||||
double aMax, double aMin, double jMax)
|
||||
{
|
||||
if (fabs(s->a0) < DBL_EPSILON && fabs(s->af) < DBL_EPSILON && fabs(s->vd) < DBL_EPSILON) {
|
||||
profile->t[0] = 0;
|
||||
profile->t[1] = s->tf;
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
if (cruckig_profile_check_for_velocity_with_timing(profile, ControlSignsUDDU, ReachedLimitsNONE, s->tf, jMax, aMax, aMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* UD Solution 1/2 */
|
||||
{
|
||||
const double h1 = 2 * (s->af * s->tf - s->vd);
|
||||
|
||||
profile->t[0] = h1 / s->ad;
|
||||
profile->t[1] = s->tf - profile->t[0];
|
||||
profile->t[2] = 0;
|
||||
profile->t[3] = 0;
|
||||
profile->t[4] = 0;
|
||||
profile->t[5] = 0;
|
||||
profile->t[6] = 0;
|
||||
|
||||
const double jf = s->ad * s->ad / h1;
|
||||
|
||||
if (fabs(jf) < fabs(jMax) + 1e-12 && cruckig_profile_check_for_velocity_with_timing(profile, ControlSignsUDDU, ReachedLimitsNONE, s->tf, jf, aMax, aMin)) {
|
||||
profile->pf = profile->p[7];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_all(CRuckigVelocityThirdOrderStep2 *s, CRuckigProfile *profile,
|
||||
double aMax, double aMin, double jMax)
|
||||
{
|
||||
return time_acc0(s, profile, aMax, aMin, jMax) || time_none(s, profile, aMax, aMin, jMax);
|
||||
}
|
||||
|
||||
|
||||
/* ---- Public interface ---- */
|
||||
|
||||
void cruckig_vel3_step2_init(CRuckigVelocityThirdOrderStep2 *s,
|
||||
double tf, double v0, double a0, double vf, double af,
|
||||
double aMax, double aMin, double jMax)
|
||||
{
|
||||
s->a0 = a0;
|
||||
s->tf = tf;
|
||||
s->af = af;
|
||||
s->_aMax = aMax;
|
||||
s->_aMin = aMin;
|
||||
s->_jMax = jMax;
|
||||
s->vd = vf - v0;
|
||||
s->ad = af - a0;
|
||||
}
|
||||
|
||||
bool cruckig_vel3_step2_get_profile(CRuckigVelocityThirdOrderStep2 *s, CRuckigProfile *profile)
|
||||
{
|
||||
/* Test all cases to get ones that match */
|
||||
/* However we should guess which one is correct and try them first... */
|
||||
if (s->vd > 0) {
|
||||
return check_all(s, profile, s->_aMax, s->_aMin, s->_jMax) || check_all(s, profile, s->_aMin, s->_aMax, -s->_jMax);
|
||||
}
|
||||
|
||||
return check_all(s, profile, s->_aMin, s->_aMax, -s->_jMax) || check_all(s, profile, s->_aMax, s->_aMin, s->_jMax);
|
||||
}
|
||||
680
wasm-port/vendor/linuxcnc/src/emc/tp/ruckig_wrapper.c
vendored
Normal file
680
wasm-port/vendor/linuxcnc/src/emc/tp/ruckig_wrapper.c
vendored
Normal file
@@ -0,0 +1,680 @@
|
||||
/********************************************************************
|
||||
* Description: ruckig_wrapper.c
|
||||
* Cruckig (pure C) trajectory planning library wrapper implementation
|
||||
*
|
||||
* This file provides a C wrapper around the Cruckig C library
|
||||
* for S-curve trajectory planning in LinuxCNC.
|
||||
* Replaces the C++ Ruckig implementation to enable RTAI kernel builds.
|
||||
*
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
* Original Author: 杨阳 (mika-net@outlook.com)
|
||||
* Cruckig port: LinuxCNC contributors
|
||||
*
|
||||
* Copyright (c) 2024-2026 All rights reserved.
|
||||
********************************************************************/
|
||||
|
||||
#include "ruckig_wrapper.h"
|
||||
#include <rtapi.h>
|
||||
#include <rtapi_math.h>
|
||||
#include <rtapi_slab.h>
|
||||
|
||||
/* LinuxCNC precision constants (consistent with tp_types.h) */
|
||||
#ifndef TP_POS_EPSILON
|
||||
#define TP_POS_EPSILON 1e-12
|
||||
#endif
|
||||
#ifndef TP_VEL_EPSILON
|
||||
#define TP_VEL_EPSILON 1e-8
|
||||
#endif
|
||||
|
||||
/* Cruckig C headers */
|
||||
#include "cruckig/cruckig.h"
|
||||
|
||||
/* Internal implementation struct */
|
||||
struct RuckigPlannerImpl {
|
||||
CRuckig *otg; /* cruckig planner instance */
|
||||
CRuckigInputParameter *input; /* input parameters */
|
||||
CRuckigTrajectory *trajectory; /* trajectory result */
|
||||
double cycle_time; /* cycle time */
|
||||
int planned; /* whether planning has been done */
|
||||
double start_time; /* trajectory start time */
|
||||
double target_pos; /* target position (used for precision correction) */
|
||||
double target_vel; /* target velocity (used for precision correction) */
|
||||
double target_acc; /* target acceleration (used for precision correction) */
|
||||
int use_position_control; /* 1=position control, 0=velocity control */
|
||||
double last_actual_acc; /* previous actual acceleration (for jerk calculation) */
|
||||
int is_first_cycle; /* first cycle after replanning */
|
||||
int enable_logging; /* 1=enabled, 0=disabled */
|
||||
};
|
||||
|
||||
/* Helper macro: conditionally output log based on planner's logging setting */
|
||||
#define RUCKIG_LOG_IF_ENABLED(planner, level, fmt, ...) \
|
||||
do { \
|
||||
if (planner) { \
|
||||
struct RuckigPlannerImpl *_impl = (struct RuckigPlannerImpl *)planner; \
|
||||
if (_impl->enable_logging) { \
|
||||
rtapi_print_msg(level, fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} else { \
|
||||
rtapi_print_msg(level, fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
RuckigPlanner ruckig_create(double cycle_time) {
|
||||
if (cycle_time <= 0.0) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_create: invalid cycle_time %f\n", cycle_time);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)rtapi_kmalloc(sizeof(struct RuckigPlannerImpl), RTAPI_GFP_KERNEL);
|
||||
if (!impl) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_create: memory allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
impl->otg = cruckig_create(1, cycle_time);
|
||||
impl->input = cruckig_input_create(1);
|
||||
impl->trajectory = cruckig_trajectory_create(1);
|
||||
|
||||
if (!impl->otg || !impl->input || !impl->trajectory) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_create: cruckig allocation failed\n");
|
||||
if (impl->otg) cruckig_destroy(impl->otg);
|
||||
if (impl->input) cruckig_input_destroy(impl->input);
|
||||
if (impl->trajectory) cruckig_trajectory_destroy(impl->trajectory);
|
||||
rtapi_kfree(impl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
impl->cycle_time = cycle_time;
|
||||
impl->planned = 0;
|
||||
impl->start_time = 0.0;
|
||||
impl->target_pos = 0.0;
|
||||
impl->target_vel = 0.0;
|
||||
impl->target_acc = 0.0;
|
||||
impl->use_position_control = 0;
|
||||
impl->last_actual_acc = 0.0;
|
||||
impl->is_first_cycle = 0;
|
||||
impl->enable_logging = 1;
|
||||
|
||||
return (RuckigPlanner)impl;
|
||||
}
|
||||
|
||||
void ruckig_destroy(RuckigPlanner planner) {
|
||||
if (planner) {
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
if (impl->otg) cruckig_destroy(impl->otg);
|
||||
if (impl->input) cruckig_input_destroy(impl->input);
|
||||
if (impl->trajectory) cruckig_trajectory_destroy(impl->trajectory);
|
||||
rtapi_kfree(impl);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper: copy trajectory state for backup/restore on planning failure.
|
||||
* We cannot just memcpy the CRuckigTrajectory because it contains owned pointers.
|
||||
* Instead we save/restore the profile data and scalar fields. */
|
||||
struct TrajectoryBackup {
|
||||
CRuckigProfile profile; /* single-DOF single-section profile copy */
|
||||
double duration;
|
||||
double cumulative_time;
|
||||
double independent_min_duration;
|
||||
CRuckigBound position_extremum;
|
||||
};
|
||||
|
||||
static void backup_trajectory(const CRuckigTrajectory *traj, struct TrajectoryBackup *bk) {
|
||||
bk->duration = traj->duration;
|
||||
if (traj->profiles)
|
||||
bk->profile = traj->profiles[0]; /* 1 DOF, 1 section */
|
||||
if (traj->cumulative_times)
|
||||
bk->cumulative_time = traj->cumulative_times[0];
|
||||
if (traj->independent_min_durations)
|
||||
bk->independent_min_duration = traj->independent_min_durations[0];
|
||||
if (traj->position_extrema)
|
||||
bk->position_extremum = traj->position_extrema[0];
|
||||
}
|
||||
|
||||
static void restore_trajectory(CRuckigTrajectory *traj, const struct TrajectoryBackup *bk) {
|
||||
traj->duration = bk->duration;
|
||||
if (traj->profiles)
|
||||
traj->profiles[0] = bk->profile;
|
||||
if (traj->cumulative_times)
|
||||
traj->cumulative_times[0] = bk->cumulative_time;
|
||||
if (traj->independent_min_durations)
|
||||
traj->independent_min_durations[0] = bk->independent_min_duration;
|
||||
if (traj->position_extrema)
|
||||
traj->position_extrema[0] = bk->position_extremum;
|
||||
}
|
||||
|
||||
/* Helper: handle cruckig result codes, return 0 on success, -1 or -2 on failure.
|
||||
* On failure with a previous plan, restores the backup. */
|
||||
static int handle_result(CRuckigResult result, RuckigPlanner planner,
|
||||
const char *func_name,
|
||||
int had_previous_plan,
|
||||
const struct TrajectoryBackup *bk,
|
||||
double bk_target_pos, double bk_target_vel,
|
||||
int bk_use_position_control, double bk_last_actual_acc) {
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
if (result == CRuckigWorking || result == CRuckigFinished) {
|
||||
if (result == CRuckigFinished) {
|
||||
double duration = cruckig_trajectory_get_duration(impl->trajectory);
|
||||
if (duration < 0.001) {
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_INFO,
|
||||
"%s: already at target (duration=%f)\n", func_name, duration);
|
||||
} else {
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_INFO,
|
||||
"%s: trajectory finished (duration=%f)\n", func_name, duration);
|
||||
}
|
||||
}
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
/* Planning failed: restore previous trajectory if it exists */
|
||||
if (had_previous_plan) {
|
||||
restore_trajectory(impl->trajectory, bk);
|
||||
impl->target_pos = bk_target_pos;
|
||||
impl->target_vel = bk_target_vel;
|
||||
impl->use_position_control = bk_use_position_control;
|
||||
impl->last_actual_acc = bk_last_actual_acc;
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_INFO,
|
||||
"%s: planning failed, restored previous trajectory\n", func_name);
|
||||
}
|
||||
|
||||
/* Log error */
|
||||
switch (result) {
|
||||
case CRuckigErrorInvalidInput:
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"%s: invalid input parameters\n", func_name);
|
||||
break;
|
||||
case CRuckigErrorTrajectoryDuration:
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"%s: trajectory duration exceeds numerical limits\n", func_name);
|
||||
break;
|
||||
case CRuckigErrorPositionalLimits:
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"%s: positional limits exceeded\n", func_name);
|
||||
break;
|
||||
case CRuckigErrorZeroLimits:
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"%s: zero limits conflict\n", func_name);
|
||||
break;
|
||||
case CRuckigErrorExecutionTimeCalculation:
|
||||
return -2;
|
||||
case CRuckigErrorSynchronizationCalculation:
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"%s: synchronization calculation error\n", func_name);
|
||||
break;
|
||||
case CRuckigError:
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"%s: general error\n", func_name);
|
||||
break;
|
||||
default:
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"%s: unknown error result %d\n", func_name, (int)result);
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ruckig_plan_position(RuckigPlanner planner,
|
||||
double current_pos,
|
||||
double current_vel,
|
||||
double current_acc,
|
||||
double target_pos,
|
||||
double target_vel,
|
||||
double target_acc,
|
||||
double min_vel,
|
||||
double max_vel,
|
||||
double max_acc,
|
||||
double max_jerk) {
|
||||
if (!planner) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
/* Parameter validation */
|
||||
if (max_vel <= 0.0 || max_acc <= 0.0 || max_jerk <= 0.0) {
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"ruckig_plan_position: invalid limits (v=%f, a=%f, j=%f)\n",
|
||||
max_vel, max_acc, max_jerk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set input parameters (position control mode) */
|
||||
impl->input->control_interface = CRuckigPosition;
|
||||
impl->input->synchronization = CRuckigSyncTime;
|
||||
|
||||
impl->input->current_position[0] = current_pos;
|
||||
impl->input->current_velocity[0] = current_vel;
|
||||
impl->input->current_acceleration[0] = current_acc;
|
||||
impl->input->target_position[0] = target_pos;
|
||||
impl->input->target_velocity[0] = target_vel;
|
||||
impl->input->target_acceleration[0] = target_acc;
|
||||
impl->input->max_velocity[0] = max_vel;
|
||||
impl->input->max_acceleration[0] = max_acc;
|
||||
impl->input->max_jerk[0] = max_jerk;
|
||||
|
||||
/* Set min_velocity: cruckig uses NULL for default (-max), or a pointer for explicit */
|
||||
if (impl->input->min_velocity == NULL) {
|
||||
impl->input->min_velocity = (double *)rtapi_kmalloc(sizeof(double), RTAPI_GFP_KERNEL);
|
||||
if (!impl->input->min_velocity) return -1;
|
||||
}
|
||||
impl->input->min_velocity[0] = min_vel;
|
||||
|
||||
/* Backup trajectory on failure */
|
||||
int had_previous_plan = impl->planned;
|
||||
struct TrajectoryBackup bk;
|
||||
double bk_target_pos = 0.0, bk_target_vel = 0.0, bk_last_actual_acc = 0.0;
|
||||
int bk_use_position_control = 0;
|
||||
|
||||
if (had_previous_plan) {
|
||||
backup_trajectory(impl->trajectory, &bk);
|
||||
bk_target_pos = impl->target_pos;
|
||||
bk_target_vel = impl->target_vel;
|
||||
bk_use_position_control = impl->use_position_control;
|
||||
bk_last_actual_acc = impl->last_actual_acc;
|
||||
}
|
||||
|
||||
/* Execute planning */
|
||||
CRuckigResult result = cruckig_calculate(impl->otg, impl->input, impl->trajectory);
|
||||
|
||||
int rc = handle_result(result, planner, "ruckig_plan_position",
|
||||
had_previous_plan, &bk,
|
||||
bk_target_pos, bk_target_vel,
|
||||
bk_use_position_control, bk_last_actual_acc);
|
||||
if (rc != 0) return rc;
|
||||
|
||||
/* Update state on success */
|
||||
int was_planned = impl->planned;
|
||||
if (!was_planned) {
|
||||
impl->last_actual_acc = current_acc;
|
||||
}
|
||||
|
||||
impl->planned = 1;
|
||||
impl->start_time = 0.0;
|
||||
impl->target_pos = target_pos;
|
||||
impl->target_vel = target_vel;
|
||||
impl->target_acc = target_acc;
|
||||
impl->use_position_control = 1;
|
||||
impl->is_first_cycle = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ruckig_plan_velocity(RuckigPlanner planner,
|
||||
double current_vel,
|
||||
double current_acc,
|
||||
double target_vel,
|
||||
double target_acc,
|
||||
double min_vel,
|
||||
double max_acc,
|
||||
double max_jerk) {
|
||||
if (!planner) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
/* Parameter validation */
|
||||
if (max_acc <= 0.0 || max_jerk <= 0.0) {
|
||||
RUCKIG_LOG_IF_ENABLED(planner, RTAPI_MSG_ERR,
|
||||
"ruckig_plan_velocity: invalid limits (a=%f, j=%f)\n",
|
||||
max_acc, max_jerk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set input parameters (velocity control mode) */
|
||||
impl->input->control_interface = CRuckigVelocity;
|
||||
impl->input->synchronization = CRuckigSyncNone;
|
||||
|
||||
impl->input->current_position[0] = 0.0;
|
||||
impl->input->current_velocity[0] = current_vel;
|
||||
impl->input->current_acceleration[0] = current_acc;
|
||||
impl->input->target_position[0] = 0.0;
|
||||
impl->input->target_velocity[0] = target_vel;
|
||||
impl->input->target_acceleration[0] = target_acc;
|
||||
impl->input->max_velocity[0] = INFINITY;
|
||||
impl->input->max_acceleration[0] = max_acc;
|
||||
impl->input->max_jerk[0] = max_jerk;
|
||||
|
||||
/* Set min_velocity */
|
||||
if (impl->input->min_velocity == NULL) {
|
||||
impl->input->min_velocity = (double *)rtapi_kmalloc(sizeof(double), RTAPI_GFP_KERNEL);
|
||||
if (!impl->input->min_velocity) return -1;
|
||||
}
|
||||
impl->input->min_velocity[0] = min_vel;
|
||||
|
||||
/* Backup trajectory on failure */
|
||||
int had_previous_plan = impl->planned;
|
||||
struct TrajectoryBackup bk;
|
||||
double bk_target_pos = 0.0, bk_target_vel = 0.0, bk_last_actual_acc = 0.0;
|
||||
int bk_use_position_control = 0;
|
||||
|
||||
if (had_previous_plan) {
|
||||
backup_trajectory(impl->trajectory, &bk);
|
||||
bk_target_pos = impl->target_pos;
|
||||
bk_target_vel = impl->target_vel;
|
||||
bk_use_position_control = impl->use_position_control;
|
||||
bk_last_actual_acc = impl->last_actual_acc;
|
||||
}
|
||||
|
||||
/* Execute planning */
|
||||
CRuckigResult result = cruckig_calculate(impl->otg, impl->input, impl->trajectory);
|
||||
|
||||
int rc = handle_result(result, planner, "ruckig_plan_velocity",
|
||||
had_previous_plan, &bk,
|
||||
bk_target_pos, bk_target_vel,
|
||||
bk_use_position_control, bk_last_actual_acc);
|
||||
if (rc != 0) return rc;
|
||||
|
||||
/* Update state on success */
|
||||
int was_planned = impl->planned;
|
||||
if (!was_planned) {
|
||||
impl->last_actual_acc = current_acc;
|
||||
}
|
||||
|
||||
impl->planned = 1;
|
||||
impl->start_time = 0.0;
|
||||
impl->target_pos = 0.0;
|
||||
impl->target_vel = target_vel;
|
||||
impl->target_acc = target_acc;
|
||||
impl->use_position_control = 0;
|
||||
impl->is_first_cycle = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ruckig_at_time(RuckigPlanner planner,
|
||||
double time,
|
||||
double *pos,
|
||||
double *vel,
|
||||
double *acc,
|
||||
double *jerk) {
|
||||
if (!planner || !pos || !vel || !acc || !jerk) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
if (!impl->planned) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_at_time: trajectory not planned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
double duration = cruckig_trajectory_get_duration(impl->trajectory);
|
||||
|
||||
/* Clamp time */
|
||||
double query_time = time;
|
||||
if (time < 0.0) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_at_time: time %f is negative\n", time);
|
||||
return -1;
|
||||
}
|
||||
if (time > duration) {
|
||||
query_time = duration;
|
||||
}
|
||||
|
||||
/* Get state at specified time */
|
||||
double new_pos, new_vel, new_acc, new_jerk_unused;
|
||||
size_t new_section;
|
||||
cruckig_trajectory_at_time(impl->trajectory, query_time,
|
||||
&new_pos, &new_vel, &new_acc, &new_jerk_unused,
|
||||
&new_section);
|
||||
|
||||
*pos = new_pos;
|
||||
*vel = new_vel;
|
||||
*acc = new_acc;
|
||||
|
||||
/* Precision correction: ensure position and velocity exactly match target values */
|
||||
if (impl->use_position_control) {
|
||||
const double TIME_THRESHOLD = fmax(duration * 0.1, impl->cycle_time * 10.0);
|
||||
const double POS_ERROR_THRESHOLD = 1e-6;
|
||||
|
||||
if (time >= duration - TIME_THRESHOLD || time >= duration) {
|
||||
double pos_error = fabs(*pos - impl->target_pos);
|
||||
if (pos_error < POS_ERROR_THRESHOLD) {
|
||||
*pos = impl->target_pos;
|
||||
}
|
||||
|
||||
if (time >= duration) {
|
||||
*vel = impl->target_vel;
|
||||
}
|
||||
/* During trajectory: let S-curve complete naturally */
|
||||
}
|
||||
} else {
|
||||
/* Velocity control mode: only correct at trajectory end */
|
||||
if (time >= duration) {
|
||||
*vel = impl->target_vel;
|
||||
*acc = impl->target_acc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate jerk */
|
||||
if (time > duration) {
|
||||
if (impl->use_position_control) {
|
||||
double pos_error = fabs(*pos - impl->target_pos);
|
||||
double vel_error = fabs(*vel - impl->target_vel);
|
||||
double acc_threshold = 1e-6;
|
||||
int acc_near_zero = (fabs(*acc) < acc_threshold);
|
||||
if (pos_error < TP_POS_EPSILON * 100.0 && vel_error < TP_VEL_EPSILON * 10.0 && acc_near_zero) {
|
||||
*jerk = 0.0;
|
||||
*acc = 0.0;
|
||||
}
|
||||
} else {
|
||||
double vel_error = fabs(*vel - impl->target_vel);
|
||||
double acc_threshold = 1e-6;
|
||||
int acc_near_zero = (fabs(*acc) < acc_threshold);
|
||||
if (vel_error < TP_VEL_EPSILON * 10.0 && acc_near_zero) {
|
||||
*jerk = 0.0;
|
||||
*acc = 0.0;
|
||||
}
|
||||
}
|
||||
} else if (query_time > impl->cycle_time) {
|
||||
/* Compute jerk from acceleration difference */
|
||||
double prev_pos, prev_vel, prev_acc_val, prev_jerk_unused;
|
||||
size_t prev_section;
|
||||
double prev_time = query_time - impl->cycle_time;
|
||||
if (prev_time < 0.0) prev_time = 0.0;
|
||||
cruckig_trajectory_at_time(impl->trajectory, prev_time,
|
||||
&prev_pos, &prev_vel, &prev_acc_val, &prev_jerk_unused,
|
||||
&prev_section);
|
||||
*jerk = (new_acc - prev_acc_val) / impl->cycle_time;
|
||||
} else {
|
||||
/* First cycle after replanning */
|
||||
if (impl->is_first_cycle) {
|
||||
double base_acc = impl->last_actual_acc;
|
||||
*jerk = (new_acc - base_acc) / impl->cycle_time;
|
||||
impl->is_first_cycle = 0;
|
||||
} else {
|
||||
/* Use initial acceleration from planning time */
|
||||
*jerk = (query_time > 0.0) ?
|
||||
(new_acc - impl->input->current_acceleration[0]) / query_time : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save current acceleration for jerk calculation in next cycle */
|
||||
impl->last_actual_acc = *acc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ruckig_next_cycle(RuckigPlanner planner,
|
||||
double current_time,
|
||||
double cycle_time,
|
||||
double *pos,
|
||||
double *vel,
|
||||
double *acc,
|
||||
double *jerk) {
|
||||
if (!planner) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
double next_time = current_time + cycle_time;
|
||||
return ruckig_at_time(planner, next_time, pos, vel, acc, jerk);
|
||||
}
|
||||
|
||||
double ruckig_get_duration(RuckigPlanner planner) {
|
||||
if (!planner) {
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
if (!impl->planned) {
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
return cruckig_trajectory_get_duration(impl->trajectory);
|
||||
}
|
||||
|
||||
int ruckig_is_finished(RuckigPlanner planner, double current_time) {
|
||||
if (!planner) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
if (!impl->planned) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
double duration = ruckig_get_duration(planner);
|
||||
if (duration < 0.0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (current_time >= duration) ? 1 : 0;
|
||||
}
|
||||
|
||||
void ruckig_reset(RuckigPlanner planner) {
|
||||
if (!planner) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
/* Reset all state fields */
|
||||
impl->planned = 0;
|
||||
impl->start_time = 0.0;
|
||||
impl->target_pos = 0.0;
|
||||
impl->target_vel = 0.0;
|
||||
impl->target_acc = 0.0;
|
||||
impl->use_position_control = 0;
|
||||
impl->last_actual_acc = 0.0;
|
||||
impl->is_first_cycle = 0;
|
||||
/* Note: do not reset enable_logging, preserve user setting */
|
||||
|
||||
/* Reset cruckig objects */
|
||||
cruckig_reset(impl->otg);
|
||||
}
|
||||
|
||||
void ruckig_set_logging(RuckigPlanner planner, int enable) {
|
||||
if (!planner) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
impl->enable_logging = (enable != 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
int ruckig_get_decelerate_phases(RuckigPlanner planner, double *t1, double *t2) {
|
||||
if (!planner) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
if (!impl->planned) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_get_decelerate_phases: trajectory not planned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get Profile (1 DOF, section 0) */
|
||||
const CRuckigProfile *profile = cruckig_trajectory_get_profile(impl->trajectory, 0);
|
||||
if (!profile) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_get_decelerate_phases: no profile available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Deceleration phases: t[4]=T1 (jerk), t[5]=T2 (constant accel) */
|
||||
if (t1 != NULL) {
|
||||
*t1 = (profile->t[4] > 0.0) ? profile->t[4] : 0.0;
|
||||
}
|
||||
if (t2 != NULL) {
|
||||
*t2 = (profile->t[5] > 0.0) ? profile->t[5] : 0.0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ruckig_get_peak_velocity(RuckigPlanner planner, double *peak_vel) {
|
||||
if (!planner || !peak_vel) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
if (!impl->planned) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_get_peak_velocity: trajectory not planned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const CRuckigProfile *profile = cruckig_trajectory_get_profile(impl->trajectory, 0);
|
||||
if (!profile) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_get_peak_velocity: no profile available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Peak velocity is the maximum of v[0] through v[7] */
|
||||
double max_v = 0.0;
|
||||
size_t i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (profile->v[i] > max_v) {
|
||||
max_v = profile->v[i];
|
||||
}
|
||||
}
|
||||
|
||||
*peak_vel = max_v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ruckig_get_start_velocity(RuckigPlanner planner, double *start_vel) {
|
||||
if (!planner || !start_vel) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
if (!impl->planned) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_get_start_velocity: trajectory not planned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const CRuckigProfile *profile = cruckig_trajectory_get_profile(impl->trajectory, 0);
|
||||
if (!profile) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_get_start_velocity: no profile available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*start_vel = profile->v[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ruckig_get_time_at_position(RuckigPlanner planner, double position, double time_after, double *time) {
|
||||
if (!planner || time == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RuckigPlannerImpl *impl = (struct RuckigPlannerImpl *)planner;
|
||||
|
||||
if (!impl->planned) {
|
||||
rtapi_print_msg(RTAPI_MSG_ERR, "ruckig_get_time_at_position: trajectory not planned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
double result_time;
|
||||
if (cruckig_trajectory_get_first_time_at_position(impl->trajectory, 0, position,
|
||||
&result_time, time_after)) {
|
||||
*time = result_time;
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
221
wasm-port/vendor/linuxcnc/src/emc/tp/ruckig_wrapper.h
vendored
Normal file
221
wasm-port/vendor/linuxcnc/src/emc/tp/ruckig_wrapper.h
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
/********************************************************************
|
||||
* Description: ruckig_wrapper.h
|
||||
* Ruckig trajectory planning library wrapper for LinuxCNC
|
||||
*
|
||||
* This wrapper provides a C interface to Ruckig C++ library
|
||||
* for S-curve trajectory planning.
|
||||
*
|
||||
* License: GPL Version 2
|
||||
* System: Linux
|
||||
*
|
||||
* Copyright (c) 2024 All rights reserved.
|
||||
********************************************************************/
|
||||
#ifndef RUCKIG_WRAPPER_H
|
||||
#define RUCKIG_WRAPPER_H
|
||||
|
||||
#include <rtapi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Ruckig trajectory planner handle (opaque pointer)
|
||||
*/
|
||||
typedef void* RuckigPlanner;
|
||||
|
||||
/**
|
||||
* Create a Ruckig trajectory planner.
|
||||
*
|
||||
* @param cycle_time cycle time in seconds
|
||||
* @return planner handle, or NULL on failure
|
||||
*/
|
||||
RuckigPlanner ruckig_create(double cycle_time);
|
||||
|
||||
/**
|
||||
* Destroy a Ruckig trajectory planner.
|
||||
*
|
||||
* @param planner planner handle
|
||||
*/
|
||||
void ruckig_destroy(RuckigPlanner planner);
|
||||
|
||||
/**
|
||||
* Plan an S-curve trajectory in position control mode.
|
||||
*
|
||||
* Given the initial and target states, plan a complete S-curve trajectory.
|
||||
*
|
||||
* @param planner planner handle
|
||||
* @param current_pos current position
|
||||
* @param current_vel current velocity
|
||||
* @param current_acc current acceleration
|
||||
* @param target_pos target position
|
||||
* @param target_vel target velocity (usually 0)
|
||||
* @param target_acc target acceleration (usually 0)
|
||||
* @param min_vel minimum velocity limit (set to 0 for unidirectional motion)
|
||||
* @param max_vel maximum velocity limit
|
||||
* @param max_acc maximum acceleration limit
|
||||
* @param max_jerk maximum jerk limit
|
||||
* @return 0 on success, -1 on failure (insufficient distance or invalid params)
|
||||
*/
|
||||
int ruckig_plan_position(RuckigPlanner planner,
|
||||
double current_pos,
|
||||
double current_vel,
|
||||
double current_acc,
|
||||
double target_pos,
|
||||
double target_vel,
|
||||
double target_acc,
|
||||
double min_vel,
|
||||
double max_vel,
|
||||
double max_acc,
|
||||
double max_jerk);
|
||||
|
||||
/**
|
||||
* Plan an S-curve trajectory in velocity control mode (for stop/pause).
|
||||
*
|
||||
* Uses velocity control mode, ignoring target position.
|
||||
* Suitable for stop or pause scenarios where deceleration may span segments.
|
||||
*
|
||||
* @param planner planner handle
|
||||
* @param current_vel current velocity
|
||||
* @param current_acc current acceleration
|
||||
* @param target_vel target velocity (0 for stop)
|
||||
* @param target_acc target acceleration (usually 0)
|
||||
* @param min_vel minimum velocity limit (set to 0 for unidirectional motion)
|
||||
* @param max_acc maximum acceleration limit
|
||||
* @param max_jerk maximum jerk limit
|
||||
* @return 0 on success, -1 on failure (invalid params)
|
||||
*/
|
||||
int ruckig_plan_velocity(RuckigPlanner planner,
|
||||
double current_vel,
|
||||
double current_acc,
|
||||
double target_vel,
|
||||
double target_acc,
|
||||
double min_vel,
|
||||
double max_acc,
|
||||
double max_jerk);
|
||||
|
||||
/**
|
||||
* Get the motion state at a specified time.
|
||||
*
|
||||
* @param planner planner handle
|
||||
* @param time time in seconds (from trajectory start)
|
||||
* @param pos [out] position
|
||||
* @param vel [out] velocity
|
||||
* @param acc [out] acceleration
|
||||
* @param jerk [out] jerk
|
||||
* @return 0 on success, -1 on failure (time out of range)
|
||||
*/
|
||||
int ruckig_at_time(RuckigPlanner planner,
|
||||
double time,
|
||||
double *pos,
|
||||
double *vel,
|
||||
double *acc,
|
||||
double *jerk);
|
||||
|
||||
/**
|
||||
* Get the motion state at the next cycle.
|
||||
*
|
||||
* Computes the state at (current_time + cycle_time).
|
||||
*
|
||||
* @param planner planner handle
|
||||
* @param current_time current time in seconds (from trajectory start)
|
||||
* @param cycle_time cycle time in seconds
|
||||
* @param pos [out] position
|
||||
* @param vel [out] velocity
|
||||
* @param acc [out] acceleration
|
||||
* @param jerk [out] jerk
|
||||
* @return 0 on success, -1 on failure (time out of range or not planned)
|
||||
*/
|
||||
int ruckig_next_cycle(RuckigPlanner planner,
|
||||
double current_time,
|
||||
double cycle_time,
|
||||
double *pos,
|
||||
double *vel,
|
||||
double *acc,
|
||||
double *jerk);
|
||||
|
||||
/**
|
||||
* Get total trajectory duration.
|
||||
*
|
||||
* @param planner planner handle
|
||||
* @return total time in seconds, or -1.0 on failure
|
||||
*/
|
||||
double ruckig_get_duration(RuckigPlanner planner);
|
||||
|
||||
/**
|
||||
* Check if the trajectory has completed.
|
||||
*
|
||||
* @param planner planner handle
|
||||
* @param current_time current time in seconds
|
||||
* @return 1 if finished, 0 if not, -1 on error
|
||||
*/
|
||||
int ruckig_is_finished(RuckigPlanner planner, double current_time);
|
||||
|
||||
/**
|
||||
* Reset the planner state.
|
||||
*
|
||||
* Clears previous planning results, preparing for new planning.
|
||||
*
|
||||
* @param planner planner handle
|
||||
*/
|
||||
void ruckig_reset(RuckigPlanner planner);
|
||||
|
||||
/**
|
||||
* Enable or disable log output.
|
||||
*
|
||||
* Controls whether the planner outputs error and warning messages.
|
||||
* For velocity planning scenarios (e.g. sp_scurve.c), logging can be
|
||||
* disabled to avoid unnecessary warnings.
|
||||
*
|
||||
* @param planner planner handle
|
||||
* @param enable 1=enable logging, 0=disable logging
|
||||
*/
|
||||
void ruckig_set_logging(RuckigPlanner planner, int enable);
|
||||
|
||||
/**
|
||||
* Get the deceleration phase durations (T1 and T2) from the Ruckig profile.
|
||||
*
|
||||
* T1: time for acceleration to change from 0 to -amax (jerk phase)
|
||||
* T2: time at constant -amax acceleration (constant accel phase)
|
||||
*
|
||||
* @param planner planner handle (must have completed planning)
|
||||
* @param t1 [out] T1 time (jerk phase), NULL if not needed
|
||||
* @param t2 [out] T2 time (constant accel phase), NULL if not needed
|
||||
* @return 0 on success, -1 on failure (not planned or cannot retrieve)
|
||||
*/
|
||||
int ruckig_get_decelerate_phases(RuckigPlanner planner, double *t1, double *t2);
|
||||
|
||||
/**
|
||||
* Get the peak velocity of the trajectory.
|
||||
*
|
||||
* @param planner planner handle (must have completed planning)
|
||||
* @param peak_vel [out] peak velocity
|
||||
* @return 0 on success, -1 on failure (not planned or cannot retrieve)
|
||||
*/
|
||||
int ruckig_get_peak_velocity(RuckigPlanner planner, double *peak_vel);
|
||||
|
||||
/**
|
||||
* Get the start velocity of the trajectory.
|
||||
*
|
||||
* @param planner planner handle (must have completed planning)
|
||||
* @param start_vel [out] start velocity
|
||||
* @return 0 on success, -1 on failure (not planned or cannot retrieve)
|
||||
*/
|
||||
int ruckig_get_start_velocity(RuckigPlanner planner, double *start_vel);
|
||||
|
||||
/**
|
||||
* Get the time at which the trajectory first reaches a given position.
|
||||
*
|
||||
* @param planner planner handle (must have completed planning)
|
||||
* @param position target position
|
||||
* @param time_after start query time (optional, default 0.0)
|
||||
* @param time [out] time at which position is reached
|
||||
* @return 0 on success, -1 on failure (not planned, position unreachable)
|
||||
*/
|
||||
int ruckig_get_time_at_position(RuckigPlanner planner, double position, double time_after, double *time);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RUCKIG_WRAPPER_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user