Merge pull request #1274 from ahmetipkin/edge
[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
daa8b036
IA
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;
146 }
92143a7e 147
0ba48b60
VB
148 this->x_start = 0.0F;
149 this->y_start = 0.0F;
150 this->x_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, x_size_checksum)->by_default(0.0F)->as_number();
151 this->y_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, y_size_checksum)->by_default(0.0F)->as_number();
152 if (this->x_size == 0.0F || this->y_size == 0.0F) {
6c972e51
JL
153 THEKERNEL->streams->printf("Error: Invalid config, x_size and y_size must be defined\n");
154 return false;
155 }
156
157 // the initial height above the bed we stop the intial move down after home to find the bed
158 // 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)
159 this->initial_height = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, initial_height_checksum)->by_default(10)->as_number();
160
161 // Probe offsets xxx,yyy,zzz
162 {
163 std::string po = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string();
164 std::vector<float> v = parse_number_list(po.c_str());
165 if(v.size() >= 3) {
166 this->probe_offsets = std::make_tuple(v[0], v[1], v[2]);
167 }
168 }
169
170 // allocate in AHB0
258f176c 171 grid = (float *)AHB0.alloc(configured_grid_x_size * configured_grid_y_size * sizeof(float));
6c972e51 172
fc6c9aac
JM
173 if(grid == nullptr) {
174 THEKERNEL->streams->printf("Error: Not enough memory\n");
175 return false;
176 }
177
6c972e51
JL
178 reset_bed_level();
179
180 return true;
181}
182
183void CartGridStrategy::save_grid(StreamOutput *stream)
184{
825e3420
VB
185 if(only_by_two_corners){
186 stream->printf("error:Unable to save grid in only_by_two_corners mode\n");
187 return;
188 }
189
6c972e51
JL
190 if(isnan(grid[0])) {
191 stream->printf("error:No grid to save\n");
192 return;
193 }
194
258f176c
VB
195 if((current_grid_x_size != configured_grid_x_size) || (current_grid_y_size != configured_grid_y_size)) {
196 stream->printf("error:Unable to save grid with size different from configured\n");
197 return;
198 }
199
bf2abd76 200 FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "w"):fopen(GRIDFILE_NM, "w");
6c972e51
JL
201 if(fp == NULL) {
202 stream->printf("error:Failed to open grid file %s\n", GRIDFILE);
203 return;
204 }
44b5bc10
VB
205 uint8_t tmp_configured_grid_size = configured_grid_x_size;
206 if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) {
258f176c
VB
207 stream->printf("error:Failed to write grid x size\n");
208 fclose(fp);
209 return;
210 }
211
44b5bc10 212 tmp_configured_grid_size = configured_grid_y_size;
bf2abd76 213 if(configured_grid_y_size != configured_grid_x_size){
44b5bc10 214 if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) {
bf2abd76
VB
215 stream->printf("error:Failed to write grid y size\n");
216 fclose(fp);
217 return;
218 }
6c972e51
JL
219 }
220
221 if(fwrite(&x_size, sizeof(float), 1, fp) != 1) {
222 stream->printf("error:Failed to write x_size\n");
223 fclose(fp);
224 return;
225 }
226
227 if(fwrite(&y_size, sizeof(float), 1, fp) != 1) {
228 stream->printf("error:Failed to write y_size\n");
229 fclose(fp);
230 return;
231 }
232
258f176c
VB
233 for (int y = 0; y < configured_grid_y_size; y++) {
234 for (int x = 0; x < configured_grid_x_size; x++) {
235 if(fwrite(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) {
6c972e51
JL
236 stream->printf("error:Failed to write grid\n");
237 fclose(fp);
238 return;
239 }
240 }
241 }
242 stream->printf("grid saved to %s\n", GRIDFILE);
243 fclose(fp);
244}
245
246bool CartGridStrategy::load_grid(StreamOutput *stream)
247{
825e3420
VB
248 if(only_by_two_corners){
249 stream->printf("error:Unable to load grid in only_by_two_corners mode\n");
250 return false;
251 }
01cacfc9 252
bf2abd76 253 FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "r"):fopen(GRIDFILE_NM, "r");
6c972e51
JL
254 if(fp == NULL) {
255 stream->printf("error:Failed to open grid %s\n", GRIDFILE);
256 return false;
257 }
258
258f176c 259 uint8_t load_grid_x_size, load_grid_y_size;
6c972e51
JL
260 float x, y;
261
258f176c
VB
262 if(fread(&load_grid_x_size, sizeof(uint8_t), 1, fp) != 1) {
263 stream->printf("error:Failed to read grid size\n");
264 fclose(fp);
265 return false;
266 }
267
268 if(load_grid_x_size != configured_grid_x_size) {
269 stream->printf("error:grid size x is different read %d - config %d\n", load_grid_x_size, configured_grid_x_size);
270 fclose(fp);
271 return false;
272 }
273
bf2abd76 274 load_grid_y_size = load_grid_x_size;
6c972e51 275
bf2abd76
VB
276 if(configured_grid_x_size != configured_grid_y_size){
277 if(fread(&load_grid_y_size, sizeof(uint8_t), 1, fp) != 1) {
278 stream->printf("error:Failed to read grid size\n");
279 fclose(fp);
280 return false;
281 }
282
283 if(load_grid_y_size != configured_grid_y_size) {
284 stream->printf("error:grid size y is different read %d - config %d\n", load_grid_y_size, configured_grid_x_size);
285 fclose(fp);
286 return false;
287 }
6c972e51
JL
288 }
289
290 if(fread(&x, sizeof(float), 1, fp) != 1) {
291 stream->printf("error:Failed to read grid x size\n");
292 fclose(fp);
293 return false;
294 }
295
296 if(fread(&y, sizeof(float), 1, fp) != 1) {
297 stream->printf("error:Failed to read grid y size\n");
298 fclose(fp);
299 return false;
300 }
301
302 if(x != x_size || y != y_size) {
303 stream->printf("error:bed dimensions changed read (%f, %f) - config (%f,%f)\n", x, y, x_size, y_size);
304 fclose(fp);
305 return false;
306 }
307
258f176c
VB
308 for (int y = 0; y < configured_grid_y_size; y++) {
309 for (int x = 0; x < configured_grid_x_size; x++) {
310 if(fread(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) {
6c972e51
JL
311 stream->printf("error:Failed to read grid\n");
312 fclose(fp);
313 return false;
314 }
315 }
316 }
258f176c 317 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
318 fclose(fp);
319 return true;
320}
321
0ba48b60 322bool CartGridStrategy::probe_grid(int n, int m, float _x_start, float _y_start, float _x_size, float _y_size, StreamOutput *stream)
6c972e51 323{
608a0f26 324 if((n < 5)||(m < 5)) {
6c972e51
JL
325 stream->printf("Need at least a 5x5 grid to probe\n");
326 return true;
327 }
328
01cacfc9
JM
329
330 if(!findBed()) return false;
6c972e51 331
0ba48b60
VB
332 float x_step = _x_size / n;
333 float y_step = _y_size / m;
5dcdfcd7 334 for (int c = 0; c < m; ++c) {
0ba48b60 335 float y = _y_start + y_step * c;
6c972e51 336 for (int r = 0; r < n; ++r) {
0ba48b60 337 float x = _x_start + x_step * r;
6c972e51 338 float z = 0.0F;
c77455c3
JM
339 float mm;
340 if(!zprobe->doProbeAt(mm, x, y)) return false;
341 z = zprobe->getProbeHeight() - mm;
953d8b14 342 stream->printf("%1.4f ", z);
6c972e51
JL
343 }
344 stream->printf("\n");
345 }
346 return true;
347}
348
349bool CartGridStrategy::handleGcode(Gcode *gcode)
350{
351 if(gcode->has_g) {
352 if (gcode->g == 29) { // do a probe to test flatness
353 // first wait for an empty queue i.e. no moves left
354 THEKERNEL->conveyor->wait_for_idle();
355
09080bb3
VB
356 int n = gcode->has_letter('I') ? gcode->get_value('I') : configured_grid_x_size;
357 int m = gcode->has_letter('J') ? gcode->get_value('J') : configured_grid_y_size;
01cacfc9 358
0ba48b60
VB
359 float _x_size = this->x_size, _y_size = this->x_size;
360 float _x_start = this->x_start, _y_start = this->y_start;
825e3420
VB
361
362 if(only_by_two_corners){
27e1ee3d 363 if(gcode->has_letter('X') && gcode->has_letter('Y') && gcode->has_letter('A') && gcode->has_letter('B')){
0ba48b60
VB
364 _x_start = gcode->get_value('X'); // override default probe start point
365 _y_start = gcode->get_value('Y'); // override default probe start point
366 _x_size = gcode->get_value('A'); // override default probe width
367 _y_size = gcode->get_value('B'); // override default probe length
825e3420
VB
368 } else {
369 gcode->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n");
370 return true;
371 }
372 } else {
0ba48b60
VB
373 if(gcode->has_letter('X')) _x_size = gcode->get_value('X'); // override default probe width
374 if(gcode->has_letter('Y')) _y_size = gcode->get_value('Y'); // override default probe length
825e3420
VB
375 }
376
0ba48b60 377 probe_grid(n, m, _x_start, _y_start, _x_size, _y_size, gcode->stream);
c77455c3 378
6c972e51
JL
379 return true;
380
737e15be 381 } else if( gcode->g == 31 || gcode->g == 32) { // do a grid probe
6c972e51
JL
382 // first wait for an empty queue i.e. no moves left
383 THEKERNEL->conveyor->wait_for_idle();
384
385 if(!doProbe(gcode)) {
386 gcode->stream->printf("Probe failed to complete, check the initial probe height and/or initial_height settings\n");
387 } else {
388 gcode->stream->printf("Probe completed\n");
389 }
390 return true;
391 }
392
393 } else if(gcode->has_m) {
394 if(gcode->m == 370 || gcode->m == 561) { // M370: Clear bed, M561: Set Identity Transform
395 // delete the compensationTransform in robot
396 setAdjustFunction(false);
397 reset_bed_level();
398 gcode->stream->printf("grid cleared and disabled\n");
399 return true;
400
401 } else if(gcode->m == 374) { // M374: Save grid, M374.1: delete saved grid
402 if(gcode->subcode == 1) {
403 remove(GRIDFILE);
404 gcode->stream->printf("%s deleted\n", GRIDFILE);
405 } else {
406 save_grid(gcode->stream);
407 }
408
409 return true;
410
411 } else if(gcode->m == 375) { // M375: load grid, M375.1 display grid
412 if(gcode->subcode == 1) {
413 print_bed_level(gcode->stream);
414 } else {
415 if(load_grid(gcode->stream)) setAdjustFunction(true);
416 }
417 return true;
418
419 } else if(gcode->m == 565) { // M565: Set Z probe offsets
420 float x = 0, y = 0, z = 0;
421 if(gcode->has_letter('X')) x = gcode->get_value('X');
422 if(gcode->has_letter('Y')) y = gcode->get_value('Y');
423 if(gcode->has_letter('Z')) z = gcode->get_value('Z');
424 probe_offsets = std::make_tuple(x, y, z);
425 return true;
426
427 } else if(gcode->m == 500 || gcode->m == 503) { // M500 save, M503 display
428 float x, y, z;
429 std::tie(x, y, z) = probe_offsets;
430 gcode->stream->printf(";Probe offsets:\nM565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z);
431 if(save) {
432 if(!isnan(grid[0])) gcode->stream->printf(";Load saved grid\nM375\n");
433 else if(gcode->m == 503) gcode->stream->printf(";WARNING No grid to save\n");
434 }
435 return true;
436 }
437 }
438
439 return false;
440}
441
6c972e51
JL
442#define X_PROBE_OFFSET_FROM_EXTRUDER std::get<0>(probe_offsets)
443#define Y_PROBE_OFFSET_FROM_EXTRUDER std::get<1>(probe_offsets)
444#define Z_PROBE_OFFSET_FROM_EXTRUDER std::get<2>(probe_offsets)
445
446void CartGridStrategy::setAdjustFunction(bool on)
447{
448 if(on) {
449 // set the compensationTransform in robot
450 using std::placeholders::_1;
451 using std::placeholders::_2;
452 THEROBOT->compensationTransform = std::bind(&CartGridStrategy::doCompensation, this, _1, _2); // [this](float *target, bool inverse) { doCompensation(target, inverse); };
453 } else {
454 // clear it
455 THEROBOT->compensationTransform = nullptr;
456 }
457}
458
01cacfc9 459bool CartGridStrategy::findBed()
6c972e51
JL
460{
461 if (do_home) zprobe->home();
01cacfc9
JM
462 float z = initial_height;
463 zprobe->coordinated_move(NAN, NAN, z, zprobe->getFastFeedrate()); // move Z only to initial_height
036275de 464 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
465
466 // find bed at 0,0 run at slow rate so as to not hit bed hard
467 float mm;
01cacfc9 468 if(!zprobe->run_probe_return(mm, zprobe->getSlowFeedrate())) return false;
6c972e51 469
01cacfc9 470 // leave head probe_height above bed
6c972e51
JL
471 float dz = zprobe->getProbeHeight() - mm;
472 zprobe->coordinated_move(NAN, NAN, dz, zprobe->getFastFeedrate(), true); // relative move
473
01cacfc9 474 return true;
6c972e51
JL
475}
476
477bool CartGridStrategy::doProbe(Gcode *gc)
478{
4138ce97 479 gc->stream->printf("Rectangular Grid Probe...\n");
825e3420
VB
480
481 if(only_by_two_corners){
ea856391 482 if(gc->has_letter('X') && gc->has_letter('Y') && gc->has_letter('A') && gc->has_letter('B')){
0ba48b60
VB
483 this->x_start = gc->get_value('X'); // override default probe start point, will get saved
484 this->y_start = gc->get_value('Y'); // override default probe start point, will get saved
485 this->x_size = gc->get_value('A'); // override default probe width, will get saved
486 this->y_size = gc->get_value('B'); // override default probe length, will get saved
825e3420
VB
487 } else {
488 gc->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n");
489 return false;
490 }
491 } else {
0ba48b60
VB
492 if(gc->has_letter('X')) this->x_size = gc->get_value('X'); // override default probe width, will get saved
493 if(gc->has_letter('Y')) this->y_size = gc->get_value('Y'); // override default probe length, will get saved
825e3420 494 }
01cacfc9 495
6c972e51
JL
496 setAdjustFunction(false);
497 reset_bed_level();
498
09080bb3
VB
499 if(gc->has_letter('I')) current_grid_x_size = gc->get_value('I'); // override default grid x size
500 if(gc->has_letter('J')) current_grid_y_size = gc->get_value('J'); // override default grid y size
01cacfc9 501
0ba48b60 502 if((this->current_grid_x_size * this->current_grid_y_size) > (this->configured_grid_x_size * this->configured_grid_y_size)){
01cacfc9 503 gc->stream->printf("Grid size (%d x %d = %d) bigger than configured (%d x %d = %d). Change configuration.\n",
0ba48b60
VB
504 this->current_grid_x_size, this->current_grid_y_size, this->current_grid_x_size*this->current_grid_x_size,
505 this->configured_grid_x_size, this->configured_grid_y_size, this->configured_grid_x_size*this->configured_grid_y_size);
258f176c
VB
506 return false;
507 }
508
6c972e51 509 // find bed, and leave probe probe height above bed
01cacfc9 510 if(!findBed()) {
68ed5df7 511 gc->stream->printf("Finding bed failed, check the initial height setting\n");
6c972e51
JL
512 return false;
513 }
514
01cacfc9 515 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
516
517 // do first probe for 0,0
518 float mm;
0ba48b60 519 if(!zprobe->doProbeAt(mm, this->x_start - X_PROBE_OFFSET_FROM_EXTRUDER, this->y_start - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false;
6c972e51
JL
520 float z_reference = zprobe->getProbeHeight() - mm; // this should be zero
521 gc->stream->printf("probe at 0,0 is %f mm\n", z_reference);
522
c77455c3 523 // probe all the points of the grid
0ba48b60
VB
524 for (int yCount = 0; yCount < this->current_grid_y_size; yCount++) {
525 float yProbe = this->y_start + (this->y_size / (this->current_grid_y_size - 1)) * yCount;
6c972e51
JL
526 int xStart, xStop, xInc;
527 if (yCount % 2) {
0ba48b60 528 xStart = this->current_grid_x_size - 1;
6c972e51
JL
529 xStop = -1;
530 xInc = -1;
b7b6110a
VB
531 } else {
532 xStart = 0;
0ba48b60 533 xStop = this->current_grid_x_size;
b7b6110a 534 xInc = 1;
6c972e51
JL
535 }
536
537 for (int xCount = xStart; xCount != xStop; xCount += xInc) {
0ba48b60 538 float xProbe = this->x_start + (this->x_size / (this->current_grid_x_size - 1)) * xCount;
6c972e51
JL
539
540 if(!zprobe->doProbeAt(mm, xProbe - X_PROBE_OFFSET_FROM_EXTRUDER, yProbe - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false;
541 float measured_z = zprobe->getProbeHeight() - mm - z_reference; // this is the delta z from bed at 0,0
953d8b14 542 gc->stream->printf("DEBUG: X%1.4f, Y%1.4f, Z%1.4f\n", xProbe, yProbe, measured_z);
0ba48b60 543 grid[xCount + (this->current_grid_x_size * yCount)] = measured_z;
6c972e51
JL
544 }
545 }
546
6c972e51
JL
547 print_bed_level(gc->stream);
548
549 setAdjustFunction(true);
550
551 return true;
552}
553
6c972e51 554void CartGridStrategy::doCompensation(float *target, bool inverse)
daa8b036 555{
6c972e51 556 // Adjust print surface height by linear interpolation over the bed_level array.
daa8b036
IA
557
558 // find min/maxes, and handle the case where size is negative (assuming this is possible? Legacy code supported this)
559 float min_x = std::min(this->x_start, this->x_start + this->x_size);
560 float max_x = std::max(this->x_start, this->x_start + this->x_size);
561 float min_y = std::min(this->y_start, this->y_start + this->y_size);
562 float max_y = std::max(this->y_start, this->y_start + this->y_size);
563
564 // clamp the input to the bounds of the compensation grid
565 // if a point is beyond the bounds of the grid, it will get the offset of the closest grid point
566 float x_target = std::min(std::max(target[X_AXIS], min_x), max_x);
567 float y_target = std::min(std::max(target[Y_AXIS], min_y), max_y);
568
569 float grid_x = std::max(0.001F, (x_target - this->x_start) / (this->x_size / (this->current_grid_x_size - 1)));
570 float grid_y = std::max(0.001F, (y_target - this->y_start) / (this->y_size / (this->current_grid_y_size - 1)));
571 int floor_x = floorf(grid_x);
572 int floor_y = floorf(grid_y);
573 float ratio_x = grid_x - floor_x;
574 float ratio_y = grid_y - floor_y;
575 float z1 = grid[(floor_x) + ((floor_y) * this->current_grid_x_size)];
576 float z2 = grid[(floor_x) + ((floor_y + 1) * this->current_grid_x_size)];
577 float z3 = grid[(floor_x + 1) + ((floor_y) * this->current_grid_x_size)];
578 float z4 = grid[(floor_x + 1) + ((floor_y + 1) * this->current_grid_x_size)];
579 float left = (1 - ratio_y) * z1 + ratio_y * z2;
580 float right = (1 - ratio_y) * z3 + ratio_y * z4;
581 float offset = (1 - ratio_x) * left + ratio_x * right;
582 // offset scale: 1 for default (use offset as is)
583 float scale = 1.0;
584
585 if (!isnan(this->damping_interval)) {
586 // first let's find out our 'world coordinate' positions for checking the limits:
587 Robot::wcs_t world_coordinates = THEROBOT->mcs2wcs(THEROBOT->get_axis_position());
588 float current_z = std::get<Z_AXIS>(world_coordinates); // no need to convert to mm, if machine is in inches; so is config!
589 // THEKERNEL->streams->printf("//DEBUG: Current Z: %f\n", current_z);
590 // if the height is below our compensation limit:
591 if(current_z <= this->height_limit) {
592 // scale the offset as necessary:
593 if( current_z >= this->dampening_start) {
594 scale = ( 1- ( (current_z - this->dampening_start ) / this->damping_interval) );
595 } // else leave scale at 1.0;
596 } else {
597 scale = 0.0; // if Z is higher than max, no compensation
598 }
599 }
600
601 if (inverse) {
602 target[Z_AXIS] -= offset * scale;
603 } else {
604 target[Z_AXIS] += offset * scale;
605 }
04f8c534
IA
606
607 /*THEKERNEL->streams->printf("//DEBUG: TARGET: %f, %f, %f\n", target[0], target[1], target[2]);
daa8b036
IA
608 THEKERNEL->streams->printf("//DEBUG: grid_x= %f\n", grid_x);
609 THEKERNEL->streams->printf("//DEBUG: grid_y= %f\n", grid_y);
610 THEKERNEL->streams->printf("//DEBUG: floor_x= %d\n", floor_x);
611 THEKERNEL->streams->printf("//DEBUG: floor_y= %d\n", floor_y);
612 THEKERNEL->streams->printf("//DEBUG: ratio_x= %f\n", ratio_x);
613 THEKERNEL->streams->printf("//DEBUG: ratio_y= %f\n", ratio_y);
614 THEKERNEL->streams->printf("//DEBUG: z1= %f\n", z1);
615 THEKERNEL->streams->printf("//DEBUG: z2= %f\n", z2);
616 THEKERNEL->streams->printf("//DEBUG: z3= %f\n", z3);
617 THEKERNEL->streams->printf("//DEBUG: z4= %f\n", z4);
618 THEKERNEL->streams->printf("//DEBUG: left= %f\n", left);
619 THEKERNEL->streams->printf("//DEBUG: right= %f\n", right);
620 THEKERNEL->streams->printf("//DEBUG: offset= %f\n", offset);
621 THEKERNEL->streams->printf("//DEBUG: scale= %f\n", scale);
622 */
6c972e51
JL
623}
624
625
626// Print calibration results for plotting or manual frame adjustment.
627void CartGridStrategy::print_bed_level(StreamOutput *stream)
628{
2f05c122
VB
629 if(!human_readable){
630 for (int y = 0; y < current_grid_y_size; y++) {
631 for (int x = 0; x < current_grid_x_size; x++) {
8683c07c 632 stream->printf("%1.4f ", grid[x + (current_grid_x_size * y)]);
2f05c122
VB
633 }
634 stream->printf("\n");
635 }
636 } else {
01cacfc9 637
2f05c122
VB
638 int xStart = (x_size>0) ? 0 : (current_grid_x_size - 1);
639 int xStop = (x_size>0) ? current_grid_x_size : -1;
640 int xInc = (x_size>0) ? 1: -1;
641
642 int yStart = (y_size<0) ? 0 : (current_grid_y_size - 1);
643 int yStop = (y_size<0) ? current_grid_y_size : -1;
644 int yInc = (y_size<0) ? 1: -1;
645
646 for (int y = yStart; y != yStop; y += yInc) {
0ba48b60 647 stream->printf("%10.4f|", y * (y_size / (current_grid_y_size - 1)));
2f05c122
VB
648 for (int x = xStart; x != xStop; x += xInc) {
649 stream->printf("%10.4f ", grid[x + (current_grid_x_size * y)]);
650 }
651 stream->printf("\n");
652 }
653 stream->printf(" ");
654 for (int x = xStart; x != xStop; x += xInc) {
655 stream->printf("-----+-----");
6c972e51
JL
656 }
657 stream->printf("\n");
2f05c122
VB
658 stream->printf(" ");
659 for (int x = xStart; x != xStop; x += xInc) {
0ba48b60 660 stream->printf("%1.4f ", x * (x_size / (current_grid_x_size - 1)));
2f05c122
VB
661 }
662 stream->printf("\n");
663
6c972e51 664 }
2f05c122 665
6c972e51
JL
666}
667
668// Reset calibration results to zero.
669void CartGridStrategy::reset_bed_level()
670{
258f176c
VB
671 for (int y = 0; y < current_grid_y_size; y++) {
672 for (int x = 0; x < current_grid_x_size; x++) {
673 grid[x + (current_grid_x_size * y)] = NAN;
6c972e51
JL
674 }
675 }
676}