Made the Compansation Dampening as optional
[clinton/Smoothieware.git] / src / modules / tools / zprobe / CartGridStrategy.cpp
CommitLineData
6c972e51
JL
1/*
2 This code is derived from (and mostly copied from) Johann Rocholls code at https://github.com/jcrocholl/Marlin/blob/deltabot/Marlin/Marlin_main.cpp
3 license is the same as his code.
4
5 Summary
6 -------
7 Probes grid_size points in X and Y (total probes grid_size * grid_size) and stores the relative offsets from the 0,0 Z height
8 When enabled every move will calculate the Z offset based on interpolating the height offset within the grids nearest 4 points.
9
10 Configuration
11 -------------
12 The strategy must be enabled in the config as well as zprobe.
13
c77455c3 14 leveling-strategy.rectangular-grid.enable true
6c972e51
JL
15
16 The size of the grid can be set with...
5d7cbe0c
VB
17 leveling-strategy.rectangular-grid.size 7
18 or
258f176c
VB
19 leveling-strategy.rectangular-grid.grid_x_size 7
20 leveling-strategy.rectangular-grid.grid_y_size 7
5d7cbe0c
VB
21 this is the X and Y size of the grid, it must be an odd number, the default is 7 which is 49 probe points
22
23 If both "size" and "grid_x_size" and "grid_x_size defined "grid_x_size" and "grid_x_size" will be used.
24 If "grid_x_size" and "grid_x_size" omitted then "size" will be used.
25 If "size" omitted default value will be used.
6c972e51 26
01cacfc9 27 I and J params used for grid size. If both omitted values from config will be used. If only one provided (I or J) then it will be used for both x_size and y-size.
6c972e51 28
5d3233cd 29 The width and length of the rectangle that is probed is set with...
c77455c3 30
5d3233cd
JM
31 leveling-strategy.rectangular-grid.x_size 100
32 leveling-strategy.rectangular-grid.y_size 90
c77455c3 33
6c972e51
JL
34 Optionally probe offsets from the nozzle or tool head can be defined with...
35
c77455c3 36 leveling-strategy.rectangular-grid.probe_offsets 0,0,0 # probe offsetrs x,y,z
6c972e51
JL
37
38 they may also be set with M565 X0 Y0 Z0
39
40 If the saved grid is to be loaded on boot then this must be set in the config...
41
c77455c3 42 leveling-strategy.rectangular-grid.save true
6c972e51
JL
43
44 Then when M500 is issued it will save M375 which will cause the grid to be loaded on boot. The default is to not autoload the grid on boot
45
46 Optionally an initial_height can be set that tell the intial probe where to stop the fast decent before it probes, this should be around 5-10mm above the bed
c77455c3 47 leveling-strategy.rectangular-grid.initial_height 10
6c972e51 48
e99c6e24 49 If two corners rectangular mode activated using "leveling-strategy.rectangular-grid.only_by_two_corners true" then G29/31/32 will not work without providing XYAB parameters
825e3420 50 XY - start point, AB rectangle size from starting point
01cacfc9 51 "Two corners"" not absolutely correct name for this mode, because it use only one corner and rectangle size.
825e3420 52
01cacfc9 53 Display mode of current grid can be changed to human redable mode (table with coordinates) by using
2f05c122 54 leveling-strategy.rectangular-grid.human_readable true
6c972e51
JL
55
56 Usage
57 -----
c77455c3 58 G29 test probes a rectangle which defaults to the width and height, can be overidden with Xnnn and Ynnn
6c972e51
JL
59
60 G31 probes the grid and turns the compensation on, this will remain in effect until reset or M561/M370
c77455c3 61 optional parameters {{Xn}} {{Yn}} sets the size for this rectangular probe, which gets saved with M375
6c972e51
JL
62
63 M370 clears the grid and turns off compensation
c77455c3
JM
64 M374 Save grid to /sd/cartesian.grid
65 M374.1 delete /sd/cartesian.grid
66 M375 Load the grid from /sd/cartesian.grid and enable compensation
6c972e51
JL
67 M375.1 display the current grid
68 M561 clears the grid and turns off compensation
69 M565 defines the probe offsets from the nozzle or tool head
70
71
72 M500 saves the probe points
73 M503 displays the current settings
74*/
75
76#include "CartGridStrategy.h"
77
78#include "Kernel.h"
79#include "Config.h"
80#include "Robot.h"
81#include "StreamOutputPool.h"
82#include "Gcode.h"
83#include "checksumm.h"
84#include "ConfigValue.h"
85#include "PublicDataRequest.h"
86#include "PublicData.h"
87#include "Conveyor.h"
88#include "ZProbe.h"
89#include "nuts_bolts.h"
90#include "utils.h"
91#include "platform_memory.h"
92
93#include <string>
94#include <algorithm>
95#include <cstdlib>
96#include <cmath>
97#include <fastmath.h>
98
608a0f26 99#define grid_size_checksum CHECKSUM("size")
258f176c
VB
100#define grid_x_size_checksum CHECKSUM("grid_x_size")
101#define grid_y_size_checksum CHECKSUM("grid_y_size")
6c972e51
JL
102#define tolerance_checksum CHECKSUM("tolerance")
103#define save_checksum CHECKSUM("save")
104#define probe_offsets_checksum CHECKSUM("probe_offsets")
105#define initial_height_checksum CHECKSUM("initial_height")
5d3233cd
JM
106#define x_size_checksum CHECKSUM("x_size")
107#define y_size_checksum CHECKSUM("y_size")
6c972e51 108#define do_home_checksum CHECKSUM("do_home")
825e3420 109#define only_by_two_corners_checksum CHECKSUM("only_by_two_corners")
2f05c122 110#define human_readable_checksum CHECKSUM("human_readable")
92143a7e
AI
111#define height_limit_checksum CHECKSUM("height_limit")
112#define dampening_start_checksum CHECKSUM("dampening_start")
6c972e51
JL
113
114#define GRIDFILE "/sd/cartesian.grid"
bf2abd76 115#define GRIDFILE_NM "/sd/cartesian_nm.grid"
6c972e51
JL
116
117CartGridStrategy::CartGridStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe)
118{
119 grid = nullptr;
120}
121
122CartGridStrategy::~CartGridStrategy()
123{
124 if(grid != nullptr) AHB0.dealloc(grid);
125}
126
127bool CartGridStrategy::handleConfig()
128{
608a0f26
VB
129
130 uint8_t grid_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_size_checksum)->by_default(7)->as_number();
0ba48b60
VB
131 this->current_grid_x_size = this->configured_grid_x_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_x_size_checksum)->by_default(grid_size)->as_number();
132 this->current_grid_y_size = this->configured_grid_y_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_y_size_checksum)->by_default(grid_size)->as_number();
6c972e51
JL
133 tolerance = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, tolerance_checksum)->by_default(0.03F)->as_number();
134 save = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, save_checksum)->by_default(false)->as_bool();
135 do_home = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, do_home_checksum)->by_default(true)->as_bool();
825e3420 136 only_by_two_corners = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, only_by_two_corners_checksum)->by_default(false)->as_bool();
2f05c122 137 human_readable = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, human_readable_checksum)->by_default(false)->as_bool();
92143a7e 138
04f8c534
IA
139 this->height_limit = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, height_limit_checksum)->by_default(NAN)->as_number();
140 this->dampening_start = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, dampening_start_checksum)->by_default(NAN)->as_number();
141
142 if(!isnan(this->height_limit) && !isnan(this->dampening_start))
143 this->damping_interval = height_limit - dampening_start;
144 else
145 this->damping_interval = NAN;
92143a7e 146
0ba48b60
VB
147 this->x_start = 0.0F;
148 this->y_start = 0.0F;
149 this->x_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, x_size_checksum)->by_default(0.0F)->as_number();
150 this->y_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, y_size_checksum)->by_default(0.0F)->as_number();
151 if (this->x_size == 0.0F || this->y_size == 0.0F) {
6c972e51
JL
152 THEKERNEL->streams->printf("Error: Invalid config, x_size and y_size must be defined\n");
153 return false;
154 }
155
156 // the initial height above the bed we stop the intial move down after home to find the bed
157 // this should be a height that is enough that the probe will not hit the bed and is an offset from max_z (can be set to 0 if max_z takes into account the probe offset)
158 this->initial_height = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, initial_height_checksum)->by_default(10)->as_number();
159
160 // Probe offsets xxx,yyy,zzz
161 {
162 std::string po = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string();
163 std::vector<float> v = parse_number_list(po.c_str());
164 if(v.size() >= 3) {
165 this->probe_offsets = std::make_tuple(v[0], v[1], v[2]);
166 }
167 }
168
169 // allocate in AHB0
258f176c 170 grid = (float *)AHB0.alloc(configured_grid_x_size * configured_grid_y_size * sizeof(float));
6c972e51 171
fc6c9aac
JM
172 if(grid == nullptr) {
173 THEKERNEL->streams->printf("Error: Not enough memory\n");
174 return false;
175 }
176
6c972e51
JL
177 reset_bed_level();
178
179 return true;
180}
181
182void CartGridStrategy::save_grid(StreamOutput *stream)
183{
825e3420
VB
184 if(only_by_two_corners){
185 stream->printf("error:Unable to save grid in only_by_two_corners mode\n");
186 return;
187 }
188
6c972e51
JL
189 if(isnan(grid[0])) {
190 stream->printf("error:No grid to save\n");
191 return;
192 }
193
258f176c
VB
194 if((current_grid_x_size != configured_grid_x_size) || (current_grid_y_size != configured_grid_y_size)) {
195 stream->printf("error:Unable to save grid with size different from configured\n");
196 return;
197 }
198
bf2abd76 199 FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "w"):fopen(GRIDFILE_NM, "w");
6c972e51
JL
200 if(fp == NULL) {
201 stream->printf("error:Failed to open grid file %s\n", GRIDFILE);
202 return;
203 }
44b5bc10
VB
204 uint8_t tmp_configured_grid_size = configured_grid_x_size;
205 if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) {
258f176c
VB
206 stream->printf("error:Failed to write grid x size\n");
207 fclose(fp);
208 return;
209 }
210
44b5bc10 211 tmp_configured_grid_size = configured_grid_y_size;
bf2abd76 212 if(configured_grid_y_size != configured_grid_x_size){
44b5bc10 213 if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) {
bf2abd76
VB
214 stream->printf("error:Failed to write grid y size\n");
215 fclose(fp);
216 return;
217 }
6c972e51
JL
218 }
219
220 if(fwrite(&x_size, sizeof(float), 1, fp) != 1) {
221 stream->printf("error:Failed to write x_size\n");
222 fclose(fp);
223 return;
224 }
225
226 if(fwrite(&y_size, sizeof(float), 1, fp) != 1) {
227 stream->printf("error:Failed to write y_size\n");
228 fclose(fp);
229 return;
230 }
231
258f176c
VB
232 for (int y = 0; y < configured_grid_y_size; y++) {
233 for (int x = 0; x < configured_grid_x_size; x++) {
234 if(fwrite(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) {
6c972e51
JL
235 stream->printf("error:Failed to write grid\n");
236 fclose(fp);
237 return;
238 }
239 }
240 }
241 stream->printf("grid saved to %s\n", GRIDFILE);
242 fclose(fp);
243}
244
245bool CartGridStrategy::load_grid(StreamOutput *stream)
246{
825e3420
VB
247 if(only_by_two_corners){
248 stream->printf("error:Unable to load grid in only_by_two_corners mode\n");
249 return false;
250 }
01cacfc9 251
bf2abd76 252 FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "r"):fopen(GRIDFILE_NM, "r");
6c972e51
JL
253 if(fp == NULL) {
254 stream->printf("error:Failed to open grid %s\n", GRIDFILE);
255 return false;
256 }
257
258f176c 258 uint8_t load_grid_x_size, load_grid_y_size;
6c972e51
JL
259 float x, y;
260
258f176c
VB
261 if(fread(&load_grid_x_size, sizeof(uint8_t), 1, fp) != 1) {
262 stream->printf("error:Failed to read grid size\n");
263 fclose(fp);
264 return false;
265 }
266
267 if(load_grid_x_size != configured_grid_x_size) {
268 stream->printf("error:grid size x is different read %d - config %d\n", load_grid_x_size, configured_grid_x_size);
269 fclose(fp);
270 return false;
271 }
272
bf2abd76 273 load_grid_y_size = load_grid_x_size;
6c972e51 274
bf2abd76
VB
275 if(configured_grid_x_size != configured_grid_y_size){
276 if(fread(&load_grid_y_size, sizeof(uint8_t), 1, fp) != 1) {
277 stream->printf("error:Failed to read grid size\n");
278 fclose(fp);
279 return false;
280 }
281
282 if(load_grid_y_size != configured_grid_y_size) {
283 stream->printf("error:grid size y is different read %d - config %d\n", load_grid_y_size, configured_grid_x_size);
284 fclose(fp);
285 return false;
286 }
6c972e51
JL
287 }
288
289 if(fread(&x, sizeof(float), 1, fp) != 1) {
290 stream->printf("error:Failed to read grid x size\n");
291 fclose(fp);
292 return false;
293 }
294
295 if(fread(&y, sizeof(float), 1, fp) != 1) {
296 stream->printf("error:Failed to read grid y size\n");
297 fclose(fp);
298 return false;
299 }
300
301 if(x != x_size || y != y_size) {
302 stream->printf("error:bed dimensions changed read (%f, %f) - config (%f,%f)\n", x, y, x_size, y_size);
303 fclose(fp);
304 return false;
305 }
306
258f176c
VB
307 for (int y = 0; y < configured_grid_y_size; y++) {
308 for (int x = 0; x < configured_grid_x_size; x++) {
309 if(fread(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) {
6c972e51
JL
310 stream->printf("error:Failed to read grid\n");
311 fclose(fp);
312 return false;
313 }
314 }
315 }
258f176c 316 stream->printf("grid loaded, grid: (%f, %f), size: %d x %d\n", x_size, y_size, load_grid_x_size, load_grid_y_size);
6c972e51
JL
317 fclose(fp);
318 return true;
319}
320
0ba48b60 321bool CartGridStrategy::probe_grid(int n, int m, float _x_start, float _y_start, float _x_size, float _y_size, StreamOutput *stream)
6c972e51 322{
608a0f26 323 if((n < 5)||(m < 5)) {
6c972e51
JL
324 stream->printf("Need at least a 5x5 grid to probe\n");
325 return true;
326 }
327
01cacfc9
JM
328
329 if(!findBed()) return false;
6c972e51 330
0ba48b60
VB
331 float x_step = _x_size / n;
332 float y_step = _y_size / m;
5dcdfcd7 333 for (int c = 0; c < m; ++c) {
0ba48b60 334 float y = _y_start + y_step * c;
6c972e51 335 for (int r = 0; r < n; ++r) {
0ba48b60 336 float x = _x_start + x_step * r;
6c972e51 337 float z = 0.0F;
c77455c3
JM
338 float mm;
339 if(!zprobe->doProbeAt(mm, x, y)) return false;
340 z = zprobe->getProbeHeight() - mm;
953d8b14 341 stream->printf("%1.4f ", z);
6c972e51
JL
342 }
343 stream->printf("\n");
344 }
345 return true;
346}
347
348bool CartGridStrategy::handleGcode(Gcode *gcode)
349{
350 if(gcode->has_g) {
351 if (gcode->g == 29) { // do a probe to test flatness
352 // first wait for an empty queue i.e. no moves left
353 THEKERNEL->conveyor->wait_for_idle();
354
09080bb3
VB
355 int n = gcode->has_letter('I') ? gcode->get_value('I') : configured_grid_x_size;
356 int m = gcode->has_letter('J') ? gcode->get_value('J') : configured_grid_y_size;
01cacfc9 357
0ba48b60
VB
358 float _x_size = this->x_size, _y_size = this->x_size;
359 float _x_start = this->x_start, _y_start = this->y_start;
825e3420
VB
360
361 if(only_by_two_corners){
27e1ee3d 362 if(gcode->has_letter('X') && gcode->has_letter('Y') && gcode->has_letter('A') && gcode->has_letter('B')){
0ba48b60
VB
363 _x_start = gcode->get_value('X'); // override default probe start point
364 _y_start = gcode->get_value('Y'); // override default probe start point
365 _x_size = gcode->get_value('A'); // override default probe width
366 _y_size = gcode->get_value('B'); // override default probe length
825e3420
VB
367 } else {
368 gcode->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n");
369 return true;
370 }
371 } else {
0ba48b60
VB
372 if(gcode->has_letter('X')) _x_size = gcode->get_value('X'); // override default probe width
373 if(gcode->has_letter('Y')) _y_size = gcode->get_value('Y'); // override default probe length
825e3420
VB
374 }
375
0ba48b60 376 probe_grid(n, m, _x_start, _y_start, _x_size, _y_size, gcode->stream);
c77455c3 377
6c972e51
JL
378 return true;
379
737e15be 380 } else if( gcode->g == 31 || gcode->g == 32) { // do a grid probe
6c972e51
JL
381 // first wait for an empty queue i.e. no moves left
382 THEKERNEL->conveyor->wait_for_idle();
383
384 if(!doProbe(gcode)) {
385 gcode->stream->printf("Probe failed to complete, check the initial probe height and/or initial_height settings\n");
386 } else {
387 gcode->stream->printf("Probe completed\n");
388 }
389 return true;
390 }
391
392 } else if(gcode->has_m) {
393 if(gcode->m == 370 || gcode->m == 561) { // M370: Clear bed, M561: Set Identity Transform
394 // delete the compensationTransform in robot
395 setAdjustFunction(false);
396 reset_bed_level();
397 gcode->stream->printf("grid cleared and disabled\n");
398 return true;
399
400 } else if(gcode->m == 374) { // M374: Save grid, M374.1: delete saved grid
401 if(gcode->subcode == 1) {
402 remove(GRIDFILE);
403 gcode->stream->printf("%s deleted\n", GRIDFILE);
404 } else {
405 save_grid(gcode->stream);
406 }
407
408 return true;
409
410 } else if(gcode->m == 375) { // M375: load grid, M375.1 display grid
411 if(gcode->subcode == 1) {
412 print_bed_level(gcode->stream);
413 } else {
414 if(load_grid(gcode->stream)) setAdjustFunction(true);
415 }
416 return true;
417
418 } else if(gcode->m == 565) { // M565: Set Z probe offsets
419 float x = 0, y = 0, z = 0;
420 if(gcode->has_letter('X')) x = gcode->get_value('X');
421 if(gcode->has_letter('Y')) y = gcode->get_value('Y');
422 if(gcode->has_letter('Z')) z = gcode->get_value('Z');
423 probe_offsets = std::make_tuple(x, y, z);
424 return true;
425
426 } else if(gcode->m == 500 || gcode->m == 503) { // M500 save, M503 display
427 float x, y, z;
428 std::tie(x, y, z) = probe_offsets;
429 gcode->stream->printf(";Probe offsets:\nM565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z);
430 if(save) {
431 if(!isnan(grid[0])) gcode->stream->printf(";Load saved grid\nM375\n");
432 else if(gcode->m == 503) gcode->stream->printf(";WARNING No grid to save\n");
433 }
434 return true;
435 }
436 }
437
438 return false;
439}
440
6c972e51
JL
441#define X_PROBE_OFFSET_FROM_EXTRUDER std::get<0>(probe_offsets)
442#define Y_PROBE_OFFSET_FROM_EXTRUDER std::get<1>(probe_offsets)
443#define Z_PROBE_OFFSET_FROM_EXTRUDER std::get<2>(probe_offsets)
444
445void CartGridStrategy::setAdjustFunction(bool on)
446{
447 if(on) {
448 // set the compensationTransform in robot
449 using std::placeholders::_1;
450 using std::placeholders::_2;
451 THEROBOT->compensationTransform = std::bind(&CartGridStrategy::doCompensation, this, _1, _2); // [this](float *target, bool inverse) { doCompensation(target, inverse); };
452 } else {
453 // clear it
454 THEROBOT->compensationTransform = nullptr;
455 }
456}
457
01cacfc9 458bool CartGridStrategy::findBed()
6c972e51
JL
459{
460 if (do_home) zprobe->home();
01cacfc9
JM
461 float z = initial_height;
462 zprobe->coordinated_move(NAN, NAN, z, zprobe->getFastFeedrate()); // move Z only to initial_height
036275de 463 zprobe->coordinated_move(x_start - X_PROBE_OFFSET_FROM_EXTRUDER, y_start - Y_PROBE_OFFSET_FROM_EXTRUDER, NAN, zprobe->getFastFeedrate()); // move at initial_height to x_start, y_start
6c972e51
JL
464
465 // find bed at 0,0 run at slow rate so as to not hit bed hard
466 float mm;
01cacfc9 467 if(!zprobe->run_probe_return(mm, zprobe->getSlowFeedrate())) return false;
6c972e51 468
01cacfc9 469 // leave head probe_height above bed
6c972e51
JL
470 float dz = zprobe->getProbeHeight() - mm;
471 zprobe->coordinated_move(NAN, NAN, dz, zprobe->getFastFeedrate(), true); // relative move
472
01cacfc9 473 return true;
6c972e51
JL
474}
475
476bool CartGridStrategy::doProbe(Gcode *gc)
477{
4138ce97 478 gc->stream->printf("Rectangular Grid Probe...\n");
825e3420
VB
479
480 if(only_by_two_corners){
ea856391 481 if(gc->has_letter('X') && gc->has_letter('Y') && gc->has_letter('A') && gc->has_letter('B')){
0ba48b60
VB
482 this->x_start = gc->get_value('X'); // override default probe start point, will get saved
483 this->y_start = gc->get_value('Y'); // override default probe start point, will get saved
484 this->x_size = gc->get_value('A'); // override default probe width, will get saved
485 this->y_size = gc->get_value('B'); // override default probe length, will get saved
825e3420
VB
486 } else {
487 gc->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n");
488 return false;
489 }
490 } else {
0ba48b60
VB
491 if(gc->has_letter('X')) this->x_size = gc->get_value('X'); // override default probe width, will get saved
492 if(gc->has_letter('Y')) this->y_size = gc->get_value('Y'); // override default probe length, will get saved
825e3420 493 }
01cacfc9 494
6c972e51
JL
495 setAdjustFunction(false);
496 reset_bed_level();
497
09080bb3
VB
498 if(gc->has_letter('I')) current_grid_x_size = gc->get_value('I'); // override default grid x size
499 if(gc->has_letter('J')) current_grid_y_size = gc->get_value('J'); // override default grid y size
01cacfc9 500
0ba48b60 501 if((this->current_grid_x_size * this->current_grid_y_size) > (this->configured_grid_x_size * this->configured_grid_y_size)){
01cacfc9 502 gc->stream->printf("Grid size (%d x %d = %d) bigger than configured (%d x %d = %d). Change configuration.\n",
0ba48b60
VB
503 this->current_grid_x_size, this->current_grid_y_size, this->current_grid_x_size*this->current_grid_x_size,
504 this->configured_grid_x_size, this->configured_grid_y_size, this->configured_grid_x_size*this->configured_grid_y_size);
258f176c
VB
505 return false;
506 }
507
6c972e51 508 // find bed, and leave probe probe height above bed
01cacfc9 509 if(!findBed()) {
68ed5df7 510 gc->stream->printf("Finding bed failed, check the initial height setting\n");
6c972e51
JL
511 return false;
512 }
513
01cacfc9 514 gc->stream->printf("Probe start ht is %f mm, rectangular bed width %fmm, height %fmm, grid size is %dx%d\n", zprobe->getProbeHeight(), x_size, y_size, current_grid_x_size, current_grid_y_size);
6c972e51
JL
515
516 // do first probe for 0,0
517 float mm;
0ba48b60 518 if(!zprobe->doProbeAt(mm, this->x_start - X_PROBE_OFFSET_FROM_EXTRUDER, this->y_start - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false;
6c972e51
JL
519 float z_reference = zprobe->getProbeHeight() - mm; // this should be zero
520 gc->stream->printf("probe at 0,0 is %f mm\n", z_reference);
521
c77455c3 522 // probe all the points of the grid
0ba48b60
VB
523 for (int yCount = 0; yCount < this->current_grid_y_size; yCount++) {
524 float yProbe = this->y_start + (this->y_size / (this->current_grid_y_size - 1)) * yCount;
6c972e51
JL
525 int xStart, xStop, xInc;
526 if (yCount % 2) {
0ba48b60 527 xStart = this->current_grid_x_size - 1;
6c972e51
JL
528 xStop = -1;
529 xInc = -1;
b7b6110a
VB
530 } else {
531 xStart = 0;
0ba48b60 532 xStop = this->current_grid_x_size;
b7b6110a 533 xInc = 1;
6c972e51
JL
534 }
535
536 for (int xCount = xStart; xCount != xStop; xCount += xInc) {
0ba48b60 537 float xProbe = this->x_start + (this->x_size / (this->current_grid_x_size - 1)) * xCount;
6c972e51
JL
538
539 if(!zprobe->doProbeAt(mm, xProbe - X_PROBE_OFFSET_FROM_EXTRUDER, yProbe - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false;
540 float measured_z = zprobe->getProbeHeight() - mm - z_reference; // this is the delta z from bed at 0,0
953d8b14 541 gc->stream->printf("DEBUG: X%1.4f, Y%1.4f, Z%1.4f\n", xProbe, yProbe, measured_z);
0ba48b60 542 grid[xCount + (this->current_grid_x_size * yCount)] = measured_z;
6c972e51
JL
543 }
544 }
545
6c972e51
JL
546 print_bed_level(gc->stream);
547
548 setAdjustFunction(true);
549
550 return true;
551}
552
6c972e51 553void CartGridStrategy::doCompensation(float *target, bool inverse)
92143a7e 554{
6c972e51 555 // Adjust print surface height by linear interpolation over the bed_level array.
92143a7e 556
04f8c534
IA
557 // find min/maxes, and handle the case where size is negative (assuming this is possible? Legacy code supported this)
558 float min_x = std::min(this->x_start, this->x_start + this->x_size);
559 float max_x = std::max(this->x_start, this->x_start + this->x_size);
560 float min_y = std::min(this->y_start, this->y_start + this->y_size);
561 float max_y = std::max(this->y_start, this->y_start + this->y_size);
562
563 // clamp the input to the bounds of the compensation grid
564 // if a point is beyond the bounds of the grid, it will get the offset of the closest grid point
565 float x_target = std::min(std::max(target[X_AXIS], min_x), max_x);
566 float y_target = std::min(std::max(target[Y_AXIS], min_y), max_y);
567
568 float grid_x = std::max(0.001F, (x_target - this->x_start) / (this->x_size / (this->current_grid_x_size - 1)));
569 float grid_y = std::max(0.001F, (y_target - this->y_start) / (this->y_size / (this->current_grid_y_size - 1)));
570 int floor_x = floorf(grid_x);
571 int floor_y = floorf(grid_y);
572 float ratio_x = grid_x - floor_x;
573 float ratio_y = grid_y - floor_y;
574 float z1 = grid[(floor_x) + ((floor_y) * this->current_grid_x_size)];
575 float z2 = grid[(floor_x) + ((floor_y + 1) * this->current_grid_x_size)];
576 float z3 = grid[(floor_x + 1) + ((floor_y) * this->current_grid_x_size)];
577 float z4 = grid[(floor_x + 1) + ((floor_y + 1) * this->current_grid_x_size)];
578 float left = (1 - ratio_y) * z1 + ratio_y * z2;
579 float right = (1 - ratio_y) * z3 + ratio_y * z4;
580 float offset = (1 - ratio_x) * left + ratio_x * right;
581 // offset scale: 1 for default (use offset as is)
582 float scale = 1.0;
583
584 if (!isnan(this->damping_interval)) {
585 // first let's find out our 'world coordinate' positions for checking the limits:
586 Robot::wcs_t world_coordinates = THEROBOT->mcs2wcs(THEROBOT->get_axis_position());
587 float current_z = THEROBOT->from_millimeters(std::get<Z_AXIS>(world_coordinates));
588 // THEKERNEL->streams->printf("//DEBUG: Current Z: %f\n", current_z);
589 // if the height is below our compensation limit:
590 if(current_z <= this->height_limit) {
591 // scale the offset as necessary:
592 if( current_z >= this->dampening_start)
593 scale = ( 1- ( (current_z - this->dampening_start ) / this->damping_interval) );
594 // else leave scale at 1.0;
595 } else
596 scale = 0.0; // if Z is higher than max, no compensation
597 }
598
599 if(inverse)
600 target[Z_AXIS] -= offset * scale;
601 else
602 target[Z_AXIS] += offset * scale;
603
604 /*THEKERNEL->streams->printf("//DEBUG: TARGET: %f, %f, %f\n", target[0], target[1], target[2]);
605 THEKERNEL->streams->printf("//DEBUG: grid_x= %f\n", grid_x);
606 THEKERNEL->streams->printf("//DEBUG: grid_y= %f\n", grid_y);
607 THEKERNEL->streams->printf("//DEBUG: floor_x= %d\n", floor_x);
608 THEKERNEL->streams->printf("//DEBUG: floor_y= %d\n", floor_y);
609 THEKERNEL->streams->printf("//DEBUG: ratio_x= %f\n", ratio_x);
610 THEKERNEL->streams->printf("//DEBUG: ratio_y= %f\n", ratio_y);
611 THEKERNEL->streams->printf("//DEBUG: z1= %f\n", z1);
612 THEKERNEL->streams->printf("//DEBUG: z2= %f\n", z2);
613 THEKERNEL->streams->printf("//DEBUG: z3= %f\n", z3);
614 THEKERNEL->streams->printf("//DEBUG: z4= %f\n", z4);
615 THEKERNEL->streams->printf("//DEBUG: left= %f\n", left);
616 THEKERNEL->streams->printf("//DEBUG: right= %f\n", right);
617 THEKERNEL->streams->printf("//DEBUG: offset= %f\n", offset);
618 THEKERNEL->streams->printf("//DEBUG: scale= %f\n", scale);
619 */
92143a7e 620
6c972e51
JL
621}
622
623
624// Print calibration results for plotting or manual frame adjustment.
625void CartGridStrategy::print_bed_level(StreamOutput *stream)
626{
2f05c122
VB
627 if(!human_readable){
628 for (int y = 0; y < current_grid_y_size; y++) {
629 for (int x = 0; x < current_grid_x_size; x++) {
8683c07c 630 stream->printf("%1.4f ", grid[x + (current_grid_x_size * y)]);
2f05c122
VB
631 }
632 stream->printf("\n");
633 }
634 } else {
01cacfc9 635
2f05c122
VB
636 int xStart = (x_size>0) ? 0 : (current_grid_x_size - 1);
637 int xStop = (x_size>0) ? current_grid_x_size : -1;
638 int xInc = (x_size>0) ? 1: -1;
639
640 int yStart = (y_size<0) ? 0 : (current_grid_y_size - 1);
641 int yStop = (y_size<0) ? current_grid_y_size : -1;
642 int yInc = (y_size<0) ? 1: -1;
643
644 for (int y = yStart; y != yStop; y += yInc) {
0ba48b60 645 stream->printf("%10.4f|", y * (y_size / (current_grid_y_size - 1)));
2f05c122
VB
646 for (int x = xStart; x != xStop; x += xInc) {
647 stream->printf("%10.4f ", grid[x + (current_grid_x_size * y)]);
648 }
649 stream->printf("\n");
650 }
651 stream->printf(" ");
652 for (int x = xStart; x != xStop; x += xInc) {
653 stream->printf("-----+-----");
6c972e51
JL
654 }
655 stream->printf("\n");
2f05c122
VB
656 stream->printf(" ");
657 for (int x = xStart; x != xStop; x += xInc) {
0ba48b60 658 stream->printf("%1.4f ", x * (x_size / (current_grid_x_size - 1)));
2f05c122
VB
659 }
660 stream->printf("\n");
661
6c972e51 662 }
2f05c122 663
6c972e51
JL
664}
665
666// Reset calibration results to zero.
667void CartGridStrategy::reset_bed_level()
668{
258f176c
VB
669 for (int y = 0; y < current_grid_y_size; y++) {
670 for (int x = 0; x < current_grid_x_size; x++) {
671 grid[x + (current_grid_x_size * y)] = NAN;
6c972e51
JL
672 }
673 }
674}