Added leveling region definition using start point and x and y size
[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...
17
258f176c
VB
18 leveling-strategy.rectangular-grid.grid_x_size 7
19 leveling-strategy.rectangular-grid.grid_y_size 7
6c972e51
JL
20
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
5d3233cd 23 The width and length of the rectangle that is probed is set with...
c77455c3 24
5d3233cd
JM
25 leveling-strategy.rectangular-grid.x_size 100
26 leveling-strategy.rectangular-grid.y_size 90
c77455c3 27
6c972e51
JL
28 Optionally probe offsets from the nozzle or tool head can be defined with...
29
c77455c3 30 leveling-strategy.rectangular-grid.probe_offsets 0,0,0 # probe offsetrs x,y,z
6c972e51
JL
31
32 they may also be set with M565 X0 Y0 Z0
33
34 If the saved grid is to be loaded on boot then this must be set in the config...
35
c77455c3 36 leveling-strategy.rectangular-grid.save true
6c972e51
JL
37
38 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
39
40 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 41 leveling-strategy.rectangular-grid.initial_height 10
6c972e51 42
825e3420
VB
43 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
44 XY - start point, AB rectangle size from starting point
45
6c972e51
JL
46
47 Usage
48 -----
c77455c3 49 G29 test probes a rectangle which defaults to the width and height, can be overidden with Xnnn and Ynnn
6c972e51
JL
50
51 G31 probes the grid and turns the compensation on, this will remain in effect until reset or M561/M370
c77455c3 52 optional parameters {{Xn}} {{Yn}} sets the size for this rectangular probe, which gets saved with M375
6c972e51
JL
53
54 M370 clears the grid and turns off compensation
c77455c3
JM
55 M374 Save grid to /sd/cartesian.grid
56 M374.1 delete /sd/cartesian.grid
57 M375 Load the grid from /sd/cartesian.grid and enable compensation
6c972e51
JL
58 M375.1 display the current grid
59 M561 clears the grid and turns off compensation
60 M565 defines the probe offsets from the nozzle or tool head
61
62
63 M500 saves the probe points
64 M503 displays the current settings
65*/
66
67#include "CartGridStrategy.h"
68
69#include "Kernel.h"
70#include "Config.h"
71#include "Robot.h"
72#include "StreamOutputPool.h"
73#include "Gcode.h"
74#include "checksumm.h"
75#include "ConfigValue.h"
76#include "PublicDataRequest.h"
77#include "PublicData.h"
78#include "Conveyor.h"
79#include "ZProbe.h"
80#include "nuts_bolts.h"
81#include "utils.h"
82#include "platform_memory.h"
83
84#include <string>
85#include <algorithm>
86#include <cstdlib>
87#include <cmath>
88#include <fastmath.h>
89
608a0f26 90#define grid_size_checksum CHECKSUM("size")
258f176c
VB
91#define grid_x_size_checksum CHECKSUM("grid_x_size")
92#define grid_y_size_checksum CHECKSUM("grid_y_size")
6c972e51
JL
93#define tolerance_checksum CHECKSUM("tolerance")
94#define save_checksum CHECKSUM("save")
95#define probe_offsets_checksum CHECKSUM("probe_offsets")
96#define initial_height_checksum CHECKSUM("initial_height")
5d3233cd
JM
97#define x_size_checksum CHECKSUM("x_size")
98#define y_size_checksum CHECKSUM("y_size")
6c972e51 99#define do_home_checksum CHECKSUM("do_home")
825e3420 100#define only_by_two_corners_checksum CHECKSUM("only_by_two_corners")
6c972e51
JL
101
102#define GRIDFILE "/sd/cartesian.grid"
bf2abd76 103#define GRIDFILE_NM "/sd/cartesian_nm.grid"
6c972e51
JL
104
105CartGridStrategy::CartGridStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe)
106{
107 grid = nullptr;
108}
109
110CartGridStrategy::~CartGridStrategy()
111{
112 if(grid != nullptr) AHB0.dealloc(grid);
113}
114
115bool CartGridStrategy::handleConfig()
116{
608a0f26
VB
117
118 uint8_t grid_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_size_checksum)->by_default(7)->as_number();
423e6cc9
VB
119 current_grid_x_size = 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();
120 current_grid_y_size = 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
121 tolerance = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, tolerance_checksum)->by_default(0.03F)->as_number();
122 save = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, save_checksum)->by_default(false)->as_bool();
123 do_home = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, do_home_checksum)->by_default(true)->as_bool();
825e3420 124 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();
6c972e51 125
825e3420
VB
126 x_start = 0.0F;
127 y_start = 0.0F;
6c972e51
JL
128 x_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, x_size_checksum)->by_default(0.0F)->as_number();
129 y_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, y_size_checksum)->by_default(0.0F)->as_number();
130 if (x_size == 0.0F || y_size == 0.0F) {
131 THEKERNEL->streams->printf("Error: Invalid config, x_size and y_size must be defined\n");
132 return false;
133 }
134
135 // the initial height above the bed we stop the intial move down after home to find the bed
136 // 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)
137 this->initial_height = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, initial_height_checksum)->by_default(10)->as_number();
138
139 // Probe offsets xxx,yyy,zzz
140 {
141 std::string po = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string();
142 std::vector<float> v = parse_number_list(po.c_str());
143 if(v.size() >= 3) {
144 this->probe_offsets = std::make_tuple(v[0], v[1], v[2]);
145 }
146 }
147
148 // allocate in AHB0
258f176c 149 grid = (float *)AHB0.alloc(configured_grid_x_size * configured_grid_y_size * sizeof(float));
6c972e51 150
fc6c9aac
JM
151 if(grid == nullptr) {
152 THEKERNEL->streams->printf("Error: Not enough memory\n");
153 return false;
154 }
155
6c972e51
JL
156 reset_bed_level();
157
158 return true;
159}
160
161void CartGridStrategy::save_grid(StreamOutput *stream)
162{
825e3420
VB
163 if(only_by_two_corners){
164 stream->printf("error:Unable to save grid in only_by_two_corners mode\n");
165 return;
166 }
167
6c972e51
JL
168 if(isnan(grid[0])) {
169 stream->printf("error:No grid to save\n");
170 return;
171 }
172
258f176c
VB
173 if((current_grid_x_size != configured_grid_x_size) || (current_grid_y_size != configured_grid_y_size)) {
174 stream->printf("error:Unable to save grid with size different from configured\n");
175 return;
176 }
177
bf2abd76 178 FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "w"):fopen(GRIDFILE_NM, "w");
6c972e51
JL
179 if(fp == NULL) {
180 stream->printf("error:Failed to open grid file %s\n", GRIDFILE);
181 return;
182 }
44b5bc10
VB
183 uint8_t tmp_configured_grid_size = configured_grid_x_size;
184 if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) {
258f176c
VB
185 stream->printf("error:Failed to write grid x size\n");
186 fclose(fp);
187 return;
188 }
189
44b5bc10 190 tmp_configured_grid_size = configured_grid_y_size;
bf2abd76 191 if(configured_grid_y_size != configured_grid_x_size){
44b5bc10 192 if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) {
bf2abd76
VB
193 stream->printf("error:Failed to write grid y size\n");
194 fclose(fp);
195 return;
196 }
6c972e51
JL
197 }
198
199 if(fwrite(&x_size, sizeof(float), 1, fp) != 1) {
200 stream->printf("error:Failed to write x_size\n");
201 fclose(fp);
202 return;
203 }
204
205 if(fwrite(&y_size, sizeof(float), 1, fp) != 1) {
206 stream->printf("error:Failed to write y_size\n");
207 fclose(fp);
208 return;
209 }
210
258f176c
VB
211 for (int y = 0; y < configured_grid_y_size; y++) {
212 for (int x = 0; x < configured_grid_x_size; x++) {
213 if(fwrite(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) {
6c972e51
JL
214 stream->printf("error:Failed to write grid\n");
215 fclose(fp);
216 return;
217 }
218 }
219 }
220 stream->printf("grid saved to %s\n", GRIDFILE);
221 fclose(fp);
222}
223
224bool CartGridStrategy::load_grid(StreamOutput *stream)
225{
825e3420
VB
226 if(only_by_two_corners){
227 stream->printf("error:Unable to load grid in only_by_two_corners mode\n");
228 return false;
229 }
230
bf2abd76 231 FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "r"):fopen(GRIDFILE_NM, "r");
6c972e51
JL
232 if(fp == NULL) {
233 stream->printf("error:Failed to open grid %s\n", GRIDFILE);
234 return false;
235 }
236
258f176c 237 uint8_t load_grid_x_size, load_grid_y_size;
6c972e51
JL
238 float x, y;
239
258f176c
VB
240 if(fread(&load_grid_x_size, sizeof(uint8_t), 1, fp) != 1) {
241 stream->printf("error:Failed to read grid size\n");
242 fclose(fp);
243 return false;
244 }
245
246 if(load_grid_x_size != configured_grid_x_size) {
247 stream->printf("error:grid size x is different read %d - config %d\n", load_grid_x_size, configured_grid_x_size);
248 fclose(fp);
249 return false;
250 }
251
bf2abd76 252 load_grid_y_size = load_grid_x_size;
6c972e51 253
bf2abd76
VB
254 if(configured_grid_x_size != configured_grid_y_size){
255 if(fread(&load_grid_y_size, sizeof(uint8_t), 1, fp) != 1) {
256 stream->printf("error:Failed to read grid size\n");
257 fclose(fp);
258 return false;
259 }
260
261 if(load_grid_y_size != configured_grid_y_size) {
262 stream->printf("error:grid size y is different read %d - config %d\n", load_grid_y_size, configured_grid_x_size);
263 fclose(fp);
264 return false;
265 }
6c972e51
JL
266 }
267
268 if(fread(&x, sizeof(float), 1, fp) != 1) {
269 stream->printf("error:Failed to read grid x size\n");
270 fclose(fp);
271 return false;
272 }
273
274 if(fread(&y, sizeof(float), 1, fp) != 1) {
275 stream->printf("error:Failed to read grid y size\n");
276 fclose(fp);
277 return false;
278 }
279
280 if(x != x_size || y != y_size) {
281 stream->printf("error:bed dimensions changed read (%f, %f) - config (%f,%f)\n", x, y, x_size, y_size);
282 fclose(fp);
283 return false;
284 }
285
258f176c
VB
286 for (int y = 0; y < configured_grid_y_size; y++) {
287 for (int x = 0; x < configured_grid_x_size; x++) {
288 if(fread(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) {
6c972e51
JL
289 stream->printf("error:Failed to read grid\n");
290 fclose(fp);
291 return false;
292 }
293 }
294 }
258f176c 295 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
296 fclose(fp);
297 return true;
298}
299
825e3420 300bool CartGridStrategy::probe_grid(int n, int m, float x_start, float y_start, float x_size, float y_size, StreamOutput *stream)
6c972e51 301{
608a0f26 302 if((n < 5)||(m < 5)) {
6c972e51
JL
303 stream->printf("Need at least a 5x5 grid to probe\n");
304 return true;
305 }
306
307 float initial_z = findBed();
308 if(isnan(initial_z)) return false;
309
825e3420
VB
310 float x_step = x_size / n;
311 float y_step = y_size / m;
6c972e51 312 for (int c = 0; c < n; ++c) {
825e3420 313 float y = y_start + y_step * c;
6c972e51 314 for (int r = 0; r < n; ++r) {
825e3420 315 float x = x_start + x_step * r;
6c972e51 316 float z = 0.0F;
c77455c3
JM
317 float mm;
318 if(!zprobe->doProbeAt(mm, x, y)) return false;
319 z = zprobe->getProbeHeight() - mm;
953d8b14 320 stream->printf("%1.4f ", z);
6c972e51
JL
321 }
322 stream->printf("\n");
323 }
324 return true;
325}
326
327bool CartGridStrategy::handleGcode(Gcode *gcode)
328{
329 if(gcode->has_g) {
330 if (gcode->g == 29) { // do a probe to test flatness
331 // first wait for an empty queue i.e. no moves left
332 THEKERNEL->conveyor->wait_for_idle();
333
ed691bbf
VB
334 int n = gcode->has_letter('I') ? gcode->get_value('I') : configured_grid_x_size;
335 int m = gcode->has_letter('J') ? gcode->get_value('J') : configured_grid_y_size;
825e3420
VB
336
337 float x_size = this->x_size, y_size = this->x_size;
338 float x_start = this->x_start, y_start = this->y_start;
339
340 if(only_by_two_corners){
341 if(gcode->has_letter('X') && gcode->has_letter('X') && gcode->has_letter('A') && gcode->has_letter('B')){
342 x_start = gcode->get_value('X'); // override default probe start point
343 y_start = gcode->get_value('Y'); // override default probe start point
344 x_size = gcode->get_value('A'); // override default probe width
345 y_size = gcode->get_value('B'); // override default probe length
346 } else {
347 gcode->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n");
348 return true;
349 }
350 } else {
351 if(gcode->has_letter('X')) x_size = gcode->get_value('X'); // override default probe width
352 if(gcode->has_letter('Y')) y_size = gcode->get_value('Y'); // override default probe length
353 }
354
355 probe_grid(n, m, x_start, y_start, x_size, y_size, gcode->stream);
c77455c3 356
6c972e51
JL
357 return true;
358
737e15be 359 } else if( gcode->g == 31 || gcode->g == 32) { // do a grid probe
6c972e51
JL
360 // first wait for an empty queue i.e. no moves left
361 THEKERNEL->conveyor->wait_for_idle();
362
363 if(!doProbe(gcode)) {
364 gcode->stream->printf("Probe failed to complete, check the initial probe height and/or initial_height settings\n");
365 } else {
366 gcode->stream->printf("Probe completed\n");
367 }
368 return true;
369 }
370
371 } else if(gcode->has_m) {
372 if(gcode->m == 370 || gcode->m == 561) { // M370: Clear bed, M561: Set Identity Transform
373 // delete the compensationTransform in robot
374 setAdjustFunction(false);
375 reset_bed_level();
376 gcode->stream->printf("grid cleared and disabled\n");
377 return true;
378
379 } else if(gcode->m == 374) { // M374: Save grid, M374.1: delete saved grid
380 if(gcode->subcode == 1) {
381 remove(GRIDFILE);
382 gcode->stream->printf("%s deleted\n", GRIDFILE);
383 } else {
384 save_grid(gcode->stream);
385 }
386
387 return true;
388
389 } else if(gcode->m == 375) { // M375: load grid, M375.1 display grid
390 if(gcode->subcode == 1) {
391 print_bed_level(gcode->stream);
392 } else {
393 if(load_grid(gcode->stream)) setAdjustFunction(true);
394 }
395 return true;
396
397 } else if(gcode->m == 565) { // M565: Set Z probe offsets
398 float x = 0, y = 0, z = 0;
399 if(gcode->has_letter('X')) x = gcode->get_value('X');
400 if(gcode->has_letter('Y')) y = gcode->get_value('Y');
401 if(gcode->has_letter('Z')) z = gcode->get_value('Z');
402 probe_offsets = std::make_tuple(x, y, z);
403 return true;
404
405 } else if(gcode->m == 500 || gcode->m == 503) { // M500 save, M503 display
406 float x, y, z;
407 std::tie(x, y, z) = probe_offsets;
408 gcode->stream->printf(";Probe offsets:\nM565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z);
409 if(save) {
410 if(!isnan(grid[0])) gcode->stream->printf(";Load saved grid\nM375\n");
411 else if(gcode->m == 503) gcode->stream->printf(";WARNING No grid to save\n");
412 }
413 return true;
414 }
415 }
416
417 return false;
418}
419
420// These are convenience defines to keep the code as close to the original as possible it also saves memory and flash
421// set the rectangle in which to probe
825e3420 422#define LEFT_PROBE_BED_POSITION (x_start)
6c972e51
JL
423#define RIGHT_PROBE_BED_POSITION (x_size)
424#define BACK_PROBE_BED_POSITION (y_size)
825e3420 425#define FRONT_PROBE_BED_POSITION (y_start)
6c972e51
JL
426
427// probe at the points of a lattice grid
825e3420
VB
428#define AUTO_BED_LEVELING_GRID_X (RIGHT_PROBE_BED_POSITION / (current_grid_x_size - 1))
429#define AUTO_BED_LEVELING_GRID_Y (BACK_PROBE_BED_POSITION / (current_grid_y_size - 1))
6c972e51
JL
430
431#define X_PROBE_OFFSET_FROM_EXTRUDER std::get<0>(probe_offsets)
432#define Y_PROBE_OFFSET_FROM_EXTRUDER std::get<1>(probe_offsets)
433#define Z_PROBE_OFFSET_FROM_EXTRUDER std::get<2>(probe_offsets)
434
435void CartGridStrategy::setAdjustFunction(bool on)
436{
437 if(on) {
438 // set the compensationTransform in robot
439 using std::placeholders::_1;
440 using std::placeholders::_2;
441 THEROBOT->compensationTransform = std::bind(&CartGridStrategy::doCompensation, this, _1, _2); // [this](float *target, bool inverse) { doCompensation(target, inverse); };
442 } else {
443 // clear it
444 THEROBOT->compensationTransform = nullptr;
445 }
446}
447
448float CartGridStrategy::findBed()
449{
450 if (do_home) zprobe->home();
451 // move to an initial position fast so as to not take all day, we move down max_z - initial_height, which is set in config, default 10mm
452 float deltaz = initial_height;
825e3420
VB
453 zprobe->coordinated_move(NAN, NAN, deltaz, zprobe->getFastFeedrate()); //move Z only to initial_height
454 zprobe->coordinated_move(x_start, y_start, NAN, zprobe->getFastFeedrate()); // move at initial_height to x_start, y_start
6c972e51
JL
455
456 // find bed at 0,0 run at slow rate so as to not hit bed hard
457 float mm;
458 if(!zprobe->run_probe_return(mm, zprobe->getSlowFeedrate())) return NAN;
459
460 float dz = zprobe->getProbeHeight() - mm;
461 zprobe->coordinated_move(NAN, NAN, dz, zprobe->getFastFeedrate(), true); // relative move
462
463 return mm + deltaz - zprobe->getProbeHeight(); // distance to move from home to 5mm above bed
464}
465
466bool CartGridStrategy::doProbe(Gcode *gc)
467{
4138ce97 468 gc->stream->printf("Rectangular Grid Probe...\n");
825e3420
VB
469
470 if(only_by_two_corners){
471 if(gc->has_letter('X') && gc->has_letter('X') && gc->has_letter('A') && gc->has_letter('B')){
472 x_start = gc->get_value('X'); // override default probe start point, will get saved
473 y_start = gc->get_value('Y'); // override default probe start point, will get saved
474 x_size = gc->get_value('A'); // override default probe width, will get saved
475 y_size = gc->get_value('B'); // override default probe length, will get saved
476 } else {
477 gc->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n");
478 return false;
479 }
480 } else {
481 if(gc->has_letter('X')) x_size = gc->get_value('X'); // override default probe width, will get saved
482 if(gc->has_letter('Y')) y_size = gc->get_value('Y'); // override default probe length, will get saved
483 }
484
6c972e51
JL
485 setAdjustFunction(false);
486 reset_bed_level();
487
258f176c 488 if(gc->has_letter('I')) current_grid_x_size = gc->get_value('I'); // override default grid x size
258f176c 489 if(gc->has_letter('J')) current_grid_y_size = gc->get_value('J'); // override default grid y size
423e6cc9
VB
490
491 if((current_grid_x_size * current_grid_y_size) > (configured_grid_x_size * configured_grid_y_size)){
825e3420 492 gc->stream->printf("Grid size (%d x %d = %d) bigger than configured (%d x %d = %d). Change configuration.\n", current_grid_x_size, current_grid_y_size, current_grid_x_size*current_grid_x_size, configured_grid_x_size, configured_grid_y_size, configured_grid_x_size*configured_grid_y_size);
258f176c
VB
493 return false;
494 }
495
6c972e51
JL
496 // find bed, and leave probe probe height above bed
497 float initial_z = findBed();
498 if(isnan(initial_z)) {
499 gc->stream->printf("Finding bed failed, check the maxz and initial height settings\n");
500 return false;
501 }
502
258f176c 503 gc->stream->printf("Probe start ht is %f mm, rectangular bed width %fmm, height %fmm, grid size is %dx%d\n", initial_z, x_size, y_size, current_grid_x_size, current_grid_y_size);
6c972e51
JL
504
505 // do first probe for 0,0
506 float mm;
825e3420 507 if(!zprobe->doProbeAt(mm, x_start - X_PROBE_OFFSET_FROM_EXTRUDER, y_start - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false;
6c972e51
JL
508 float z_reference = zprobe->getProbeHeight() - mm; // this should be zero
509 gc->stream->printf("probe at 0,0 is %f mm\n", z_reference);
510
c77455c3 511 // probe all the points of the grid
258f176c 512 for (int yCount = 0; yCount < current_grid_y_size; yCount++) {
6c972e51
JL
513 float yProbe = FRONT_PROBE_BED_POSITION + AUTO_BED_LEVELING_GRID_Y * yCount;
514 int xStart, xStop, xInc;
515 if (yCount % 2) {
258f176c 516 xStart = current_grid_x_size - 1;
6c972e51
JL
517 xStop = -1;
518 xInc = -1;
b7b6110a
VB
519 } else {
520 xStart = 0;
521 xStop = current_grid_x_size;
522 xInc = 1;
6c972e51
JL
523 }
524
525 for (int xCount = xStart; xCount != xStop; xCount += xInc) {
526 float xProbe = LEFT_PROBE_BED_POSITION + AUTO_BED_LEVELING_GRID_X * xCount;
527
528 if(!zprobe->doProbeAt(mm, xProbe - X_PROBE_OFFSET_FROM_EXTRUDER, yProbe - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false;
529 float measured_z = zprobe->getProbeHeight() - mm - z_reference; // this is the delta z from bed at 0,0
953d8b14 530 gc->stream->printf("DEBUG: X%1.4f, Y%1.4f, Z%1.4f\n", xProbe, yProbe, measured_z);
258f176c 531 grid[xCount + (current_grid_x_size * yCount)] = measured_z;
6c972e51
JL
532 }
533 }
534
6c972e51
JL
535 print_bed_level(gc->stream);
536
537 setAdjustFunction(true);
538
539 return true;
540}
541
6c972e51
JL
542void CartGridStrategy::doCompensation(float *target, bool inverse)
543{
544 // Adjust print surface height by linear interpolation over the bed_level array.
825e3420
VB
545 if ((std::min(x_start, x_start + x_size) <= target[X_AXIS]) && (target[X_AXIS] <= std::max(x_start, x_start + x_size)) &&
546 (std::min(y_start, y_start + y_size) <= target[Y_AXIS]) && (target[Y_AXIS] <= std::max(y_start, y_start + y_size))) {
547
548 float grid_x = std::max(0.001F, (target[X_AXIS] - x_start) / AUTO_BED_LEVELING_GRID_X);
549 float grid_y = std::max(0.001F, (target[Y_AXIS] - y_start) / AUTO_BED_LEVELING_GRID_Y);
550 int floor_x = floorf(grid_x);
551 int floor_y = floorf(grid_y);
552 float ratio_x = grid_x - floor_x;
553 float ratio_y = grid_y - floor_y;
554 float z1 = grid[(floor_x) + ((floor_y) * current_grid_x_size)];
555 float z2 = grid[(floor_x) + ((floor_y + 1) * current_grid_x_size)];
556 float z3 = grid[(floor_x + 1) + ((floor_y) * current_grid_x_size)];
557 float z4 = grid[(floor_x + 1) + ((floor_y + 1) * current_grid_x_size)];
558 float left = (1 - ratio_y) * z1 + ratio_y * z2;
559 float right = (1 - ratio_y) * z3 + ratio_y * z4;
560 float offset = (1 - ratio_x) * left + ratio_x * right;
561
562 if(inverse)
563 target[Z_AXIS] -= offset;
564 else
565 target[Z_AXIS] += offset;
6c972e51
JL
566
567
568 /*
569 THEKERNEL->streams->printf("//DEBUG: TARGET: %f, %f, %f\n", target[0], target[1], target[2]);
570 THEKERNEL->streams->printf("//DEBUG: grid_x= %f\n", grid_x);
571 THEKERNEL->streams->printf("//DEBUG: grid_y= %f\n", grid_y);
572 THEKERNEL->streams->printf("//DEBUG: floor_x= %d\n", floor_x);
573 THEKERNEL->streams->printf("//DEBUG: floor_y= %d\n", floor_y);
574 THEKERNEL->streams->printf("//DEBUG: ratio_x= %f\n", ratio_x);
575 THEKERNEL->streams->printf("//DEBUG: ratio_y= %f\n", ratio_y);
576 THEKERNEL->streams->printf("//DEBUG: z1= %f\n", z1);
577 THEKERNEL->streams->printf("//DEBUG: z2= %f\n", z2);
578 THEKERNEL->streams->printf("//DEBUG: z3= %f\n", z3);
579 THEKERNEL->streams->printf("//DEBUG: z4= %f\n", z4);
580 THEKERNEL->streams->printf("//DEBUG: left= %f\n", left);
581 THEKERNEL->streams->printf("//DEBUG: right= %f\n", right);
582 THEKERNEL->streams->printf("//DEBUG: offset= %f\n", offset);
583 */
825e3420 584 }
6c972e51
JL
585}
586
587
588// Print calibration results for plotting or manual frame adjustment.
589void CartGridStrategy::print_bed_level(StreamOutput *stream)
590{
258f176c
VB
591 for (int y = 0; y < current_grid_y_size; y++) {
592 for (int x = 0; x < current_grid_x_size; x++) {
953d8b14 593 stream->printf("%1.4f ", grid[x + (current_grid_x_size * y)]);
6c972e51
JL
594 }
595 stream->printf("\n");
596 }
597}
598
599// Reset calibration results to zero.
600void CartGridStrategy::reset_bed_level()
601{
258f176c
VB
602 for (int y = 0; y < current_grid_y_size; y++) {
603 for (int x = 0; x < current_grid_x_size; x++) {
604 grid[x + (current_grid_x_size * y)] = NAN;
6c972e51
JL
605 }
606 }
607}