zprobe moves must use G53 so they move in machine space
[clinton/Smoothieware.git] / src / modules / tools / zprobe / ZGridStrategy.cpp
CommitLineData
77fbe368
QH
1/*
2 Author: Quentin Harley (quentin.harley@gmail.com)
3 License: GPL3 or better see <http://www.gnu.org/licenses/>
4
5 Summary
6 -------
7 Probes user defined amount of calculated points on the bed and creates compensation grid data of the bed surface.
8 Bilinear
9 As the head moves in X and Y it will adjust Z to keep the head level with the bed.
10
11 Configuration
12 -------------
13 The strategy must be enabled in the config as well as zprobe.
14
c880f6dd 15 leveling-strategy.ZGrid-leveling.enable true
77fbe368
QH
16
17 The bed size limits must be defined, in order for the module to calculate the calibration points
18
c880f6dd
QH
19 leveling-strategy.ZGrid-leveling.bed_x 200
20 leveling-strategy.ZGrid-leveling.bed_y 200
fc29aeae
RM
21
22 Machine height, used to determine probe attachment point (bed_z / 2)
23
c880f6dd 24 leveling-strategy.ZGrid-leveling.bed_z 20
77fbe368 25
dea3c8c0
QH
26 Configure for Machines with bed 0:0 at center of platform
27 leveling-strategy.ZGrid-leveling.bed_zero false
28
29 configure for Machines with circular beds
30 leveling-strategy.ZGrid-leveling.bed_circular false
31
77fbe368
QH
32
33 The number of divisions for X and Y should be defined
34
c880f6dd
QH
35 leveling-strategy.ZGrid-leveling.rows 7 # X divisions (Default 5)
36 leveling-strategy.ZGrid-leveling.cols 9 # Y divisions (Default 5)
77fbe368
QH
37
38
39 The probe offset should be defined, default to zero offset
40
c880f6dd 41 leveling-strategy.ZGrid-leveling.probe_offsets 0,0,16.3
77fbe368 42
c880f6dd 43 The machine can be told to wait for probe attachment and confirmation
77fbe368 44
c880f6dd 45 leveling-strategy.ZGrid-leveling.wait_for_probe true
77fbe368 46
962b3c72
QH
47 The machine can be told to home in one of the following modes:
48
36256be6 49 leveling-strategy.ZGrid-leveling.home_before_probe homexyz; # nohome homexy homexyz (default)
962b3c72 50
c880f6dd 51
a4bc47fd 52 Slow feedrate can be defined for probe positioning speed. Note this is not Probing slow rate - it can be set to a fast speed if required.
c880f6dd 53
a4bc47fd 54 leveling-strategy.ZGrid-leveling.slow_feedrate 100 # ZGrid probe positioning feedrate
77fbe368
QH
55
56
57
58 Usage
59 -----
47044040
QH
60 G32 : probes the probe points and defines the bed ZGrid, this will remain in effect until reset or M370
61 G31 : reports the status - Display probe data points
77fbe368 62
47044040
QH
63 M370 : clears the ZGrid and the bed levelling is disabled until G32 is run again
64 M370 X7 Y9 : allocates a new grid size of 7x9 and clears as above
77fbe368 65
47044040
QH
66 M371 : moves the head to the next calibration position without saving for manual calibration
67 M372 : move the head to the next calibration position after saving the current probe point to memory - manual calbration
68 M373 : completes calibration and enables the Z compensation grid
77fbe368 69
47044040
QH
70 M374 : Save the grid to "Zgrid" on SD card
71 M374 S### : Save custom grid to "Zgrid.###" on SD card
77fbe368 72
47044040
QH
73 M375 : Loads grid file "Zgrid" from SD
74 M375 S### : Load custom grid file "Zgrid.###"
77fbe368 75
47044040
QH
76 M565 X### Y### Z### : Set new probe offsets
77
78 M500 : saves the probe offsets
79 M503 : displays the current settings
77fbe368
QH
80*/
81
82#include "ZGridStrategy.h"
83#include "Kernel.h"
84#include "Config.h"
85#include "Robot.h"
86#include "StreamOutputPool.h"
87#include "Gcode.h"
88#include "checksumm.h"
89#include "ConfigValue.h"
90#include "PublicDataRequest.h"
91#include "PublicData.h"
92#include "EndstopsPublicAccess.h"
93#include "Conveyor.h"
94#include "ZProbe.h"
95#include "libs/FileStream.h"
96#include "nuts_bolts.h"
97#include "platform_memory.h"
98#include "MemoryPool.h"
99#include "libs/utils.h"
100
77fbe368
QH
101#include <string>
102#include <algorithm>
103#include <cstdlib>
104#include <cmath>
d223207b 105#include <unistd.h>
77fbe368
QH
106
107#define bed_x_checksum CHECKSUM("bed_x")
108#define bed_y_checksum CHECKSUM("bed_y")
109#define bed_z_checksum CHECKSUM("bed_z")
110
111#define slow_feedrate_checksum CHECKSUM("slow_feedrate")
112#define probe_offsets_checksum CHECKSUM("probe_offsets")
c880f6dd 113#define wait_for_probe_checksum CHECKSUM("wait_for_probe")
962b3c72 114#define home_before_probe_checksum CHECKSUM("home_before_probe")
dea3c8c0
QH
115#define center_zero_checksum CHECKSUM("center_zero")
116#define circular_bed_checksum CHECKSUM("circular_bed")
117#define cal_offset_x_checksum CHECKSUM("cal_offset_x")
118#define cal_offset_y_checksum CHECKSUM("cal_offset_y")
119
962b3c72
QH
120#define NOHOME 0
121#define HOMEXY 1
122#define HOMEXYZ 2
77fbe368
QH
123
124#define cols_checksum CHECKSUM("cols")
125#define rows_checksum CHECKSUM("rows")
126
127#define probe_points (this->numRows * this->numCols)
128
129
130
131ZGridStrategy::ZGridStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe)
132{
133 this->cal[X_AXIS] = 0.0f;
134 this->cal[Y_AXIS] = 0.0f;
135 this->cal[Z_AXIS] = 30.0f;
136
137 this->in_cal = false;
6100791e 138 this->pData = nullptr;
77fbe368
QH
139}
140
141ZGridStrategy::~ZGridStrategy()
142{
143 // Free program memory for the pData grid
144 if(this->pData != nullptr) AHB0.dealloc(this->pData);
145}
146
147bool ZGridStrategy::handleConfig()
148{
149 this->bed_x = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, bed_x_checksum)->by_default(200.0F)->as_number();
150 this->bed_y = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, bed_y_checksum)->by_default(200.0F)->as_number();
fc29aeae 151 this->bed_z = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, bed_z_checksum)->by_default(20.0F)->as_number();
77fbe368
QH
152
153 this->slow_rate = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, slow_feedrate_checksum)->by_default(20.0F)->as_number();
154
155 this->numRows = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, rows_checksum)->by_default(5)->as_number();
156 this->numCols = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, cols_checksum)->by_default(5)->as_number();
157
36256be6
QH
158 this->wait_for_probe = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, wait_for_probe_checksum)->by_default(true)->as_bool(); // Morgan default = true
159
160 std::string home_mode = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, home_before_probe_checksum)->by_default("homexyz")->as_string();
161 if (home_mode.compare("nohome") == 0) {
162 this->home_before_probe = NOHOME;
163 }
164 else if (home_mode.compare("homexy") == 0) {
165 this->home_before_probe = HOMEXY;
166 }
167 else { // Default
168 this->home_before_probe = HOMEXYZ;
169 }
170
171
172 //this->home_before_probe = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, home_before_probe_checksum)->by_default(HOMEXYZ)->as_number(); // Morgan default = HOMEXYZ
c880f6dd 173
dea3c8c0
QH
174 this->center_zero = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, center_zero_checksum)->by_default(false)->as_bool();
175 this->circular_bed = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, circular_bed_checksum)->by_default(false)->as_bool();
176
177 // configures calbration positioning offset. Defaults to 0 for standard cartesian space machines, and to negative half of the current bed size in X and Y
178 this->cal_offset_x = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, cal_offset_x_checksum)->by_default( this->center_zero ? this->bed_x / -2.0F : 0.0F )->as_number();
179 this->cal_offset_y = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, cal_offset_y_checksum)->by_default( this->center_zero ? this->bed_y / -2.0F : 0.0F )->as_number();
180
c880f6dd 181
77fbe368
QH
182 // Probe offsets xxx,yyy,zzz
183 std::string po = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string();
184 this->probe_offsets= parseXYZ(po.c_str());
185
d223207b 186 this->calcConfig(); // Run calculations for Grid size and allocate initial grid memory
77fbe368 187
d8a0c34b
QH
188 for (int i=0; i<(probe_points); i++){
189 this->pData[i] = 0.0F; // Clear the grid
77fbe368
QH
190 }
191
192 return true;
193}
194
195void ZGridStrategy::calcConfig()
196{
d223207b 197 this->bed_div_x = this->bed_x / float(this->numRows-1); // Find divisors to calculate the calbration points
77fbe368
QH
198 this->bed_div_y = this->bed_y / float(this->numCols-1);
199
200 // Ensure free program memory for the pData grid
201 if(this->pData != nullptr) AHB0.dealloc(this->pData);
202
203 // Allocate program memory for the pData grid
204 this->pData = (float *)AHB0.alloc(probe_points * sizeof(float));
205}
206
207bool ZGridStrategy::handleGcode(Gcode *gcode)
208{
209 string args = get_arguments(gcode->get_command());
210
211 // G code processing
212 if(gcode->has_g) {
213 if( gcode->g == 31 ) { // report status
214
215 // Bed ZGrid data as gcode:
216 gcode->stream->printf(";Bed Level settings:\r\n");
217
218 for (int x=0; x<this->numRows; x++){
219 gcode->stream->printf("X%i",x);
220 for (int y=0; y<this->numCols; y++){
221 gcode->stream->printf(" %c%1.2f", 'A'+y, this->pData[(x*this->numCols)+y]);
222 }
223 gcode->stream->printf("\r\n");
224 }
225 return true;
226
227 } else if( gcode->g == 32 ) { //run probe
228 // first wait for an empty queue i.e. no moves left
229 THEKERNEL->conveyor->wait_for_empty_queue();
230
231 this->setAdjustFunction(false); // Disable leveling code
232 if(!doProbing(gcode->stream)) {
233 gcode->stream->printf("Probe failed to complete, probe not triggered or other error\n");
234 } else {
235 this->setAdjustFunction(true); // Enable leveling code
236 gcode->stream->printf("Probe completed, bed grid defined\n");
237 }
238 return true;
239 }
240
241 } else if(gcode->has_m) {
242 switch( gcode->m ) {
243
244 // manual bed ZGrid calbration: M370 - M375
245 // M370: Clear current ZGrid for calibration, and move to first position
246 case 370: {
247 this->setAdjustFunction(false); // Disable leveling code
47044040
QH
248 this->cal[Z_AXIS] = std::get<Z_AXIS>(this->probe_offsets) + zprobe->getProbeHeight();
249
77fbe368
QH
250
251 if(gcode->has_letter('X')) // Rows (X)
252 this->numRows = gcode->get_value('X');
253 if(gcode->has_letter('Y')) // Cols (Y)
254 this->numCols = gcode->get_value('Y');
255
256 this->calcConfig(); // Run calculations for Grid size and allocate grid memory
257
258 this->homexyz();
259 for (int i=0; i<probe_points; i++){
260 this->pData[i] = 0.0F; // Clear the ZGrid
261 }
262
263 this->cal[X_AXIS] = 0.0f; // Clear calibration position
264 this->cal[Y_AXIS] = 0.0f;
265 this->in_cal = true; // In calbration mode
266
267 }
268 return true;
269 // M371: Move to next manual calibration position
270 case 371: {
271 if (in_cal){
272 this->move(this->cal, slow_rate);
273 this->next_cal();
274 }
275
276 }
277 return true;
278 // M372: save current position in ZGrid, and move to next calibration position
279 case 372: {
280 if (in_cal){
281 float cartesian[3];
282 int pindex = 0;
283
284 THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot
285
286 pindex = int(cartesian[X_AXIS]/this->bed_div_x + 0.25)*this->numCols + int(cartesian[Y_AXIS]/this->bed_div_y + 0.25);
287
288 this->move(this->cal, slow_rate); // move to the next position
289 this->next_cal(); // to not cause damage to machine due to Z-offset
290
291 this->pData[pindex] = cartesian[Z_AXIS]; // save the offset
292
293 }
294 }
295 return true;
296 // M373: finalize calibration
297 case 373: {
d223207b
QH
298 // normalize the grid
299 this->normalize_grid();
300
77fbe368
QH
301 this->in_cal = false;
302 this->setAdjustFunction(true); // Enable leveling code
303
304 }
305 return true;
306
307 // M374: Save grid
308 case 374:{
d1069930 309 char gridname[5];
be587e68 310
d1069930 311 if(gcode->has_letter('S')) // Custom grid number
56d27ea6 312 snprintf(gridname, sizeof(gridname), "S%03.0f", gcode->get_value('S'));
d1069930
QH
313 else
314 gridname[0] = '\0';
be587e68
QH
315
316 if(this->saveGrid(gridname)) {
d1069930
QH
317 gcode->stream->printf("Grid saved: Filename: /sd/Zgrid.%s\n",gridname);
318 }
319 else {
320 gcode->stream->printf("Error: Grid not saved: Filename: /sd/Zgrid.%s\n",gridname);
77fbe368
QH
321 }
322 }
323 return true;
324
be587e68
QH
325 case 375:{ // Load grid values
326 char gridname[5];
327
328 if(gcode->has_letter('S')) // Custom grid number
56d27ea6 329 snprintf(gridname, sizeof(gridname), "S%03.0f", gcode->get_value('S'));
d1069930
QH
330 else
331 gridname[0] = '\0';
be587e68 332
d1069930 333 if(this->loadGrid(gridname)) {
77fbe368 334 this->setAdjustFunction(true); // Enable leveling code
d1069930
QH
335 gcode->stream->printf("Grid loaded: /sd/Zgrid.%s\n",gridname);
336 }
337 else {
338 gcode->stream->printf("Error: Grid not loaded: /sd/Zgrid.%s\n",gridname);
77fbe368 339 }
be587e68 340 }
77fbe368
QH
341 return true;
342
d223207b 343/* case 376: { // Check grid value calculations: For debug only.
77fbe368
QH
344 float target[3];
345
346 for(char letter = 'X'; letter <= 'Z'; letter++) {
347 if( gcode->has_letter(letter) ) {
348 target[letter - 'X'] = gcode->get_value(letter);
349 }
350 }
351 gcode->stream->printf(" Z0 %1.3f\n",getZOffset(target[0], target[1]));
352
353 }
354 return true;
355*/
356 case 565: { // M565: Set Z probe offsets
357 float x= 0, y= 0, z= 0;
358 if(gcode->has_letter('X')) x = gcode->get_value('X');
359 if(gcode->has_letter('Y')) y = gcode->get_value('Y');
360 if(gcode->has_letter('Z')) z = gcode->get_value('Z');
361 probe_offsets = std::make_tuple(x, y, z);
362 }
363 return true;
364
d223207b 365 case 500: // M500 saves probe_offsets config override file
d1069930 366 gcode->stream->printf(";Load default grid\nM375\n");
e87e5be3 367
77fbe368
QH
368
369 case 503: { // M503 just prints the settings
370
371 float x,y,z;
372 gcode->stream->printf(";Probe offsets:\n");
373 std::tie(x, y, z) = probe_offsets;
374 gcode->stream->printf("M565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z);
77fbe368
QH
375 break;
376 }
377
378 return true;
379 }
380 }
381
382 return false;
383}
384
d223207b 385
e87e5be3 386bool ZGridStrategy::saveGrid(std::string args)
77fbe368 387{
e87e5be3
QH
388 args = "/sd/Zgrid." + args;
389 StreamOutput *ZMap_file = new FileStream(args.c_str());
77fbe368 390
d223207b 391 ZMap_file->printf("P%i %i %i %1.3f\n", probe_points, this->numRows, this->numCols, getZhomeoffset()); // Store probe points to prevent loading undefined grid files
77fbe368
QH
392
393 for (int pos = 0; pos < probe_points; pos++){
394 ZMap_file->printf("%1.3f\n", this->pData[pos]);
395 }
396 delete ZMap_file;
397
398 return true;
399
400}
401
e87e5be3 402bool ZGridStrategy::loadGrid(std::string args)
77fbe368
QH
403{
404 char flag[20];
405
406 int fpoints, GridX = 5, GridY = 5; // for 25point file
407 float val, GridZ;
408
e87e5be3
QH
409 args = "/sd/Zgrid." + args;
410 FILE *fd = fopen(args.c_str(), "r");
77fbe368
QH
411 if(fd != NULL) {
412 fscanf(fd, "%s\n", flag);
413
77fbe368 414 if (flag[0] == 'P'){
77fbe368
QH
415
416 sscanf(flag, "P%i\n", &fpoints); // read number of points, and Grid X and Y
d223207b 417 fscanf(fd, "%i %i %f\n", &GridX, &GridY, &GridZ); // read number of points, and Grid X and Y and ZHoming offset
77fbe368
QH
418 fscanf(fd, "%f\n", &val); // read first value from file
419
420 } else { // original 25point file -- Backwards compatibility
421 fpoints = 25;
422 sscanf(flag, "%f\n", &val); // read first value from string
423 }
424
425 if (GridX != this->numRows || GridY != this->numCols){
426 this->numRows = GridX; // Change Rows and Columns to match the saved data
427 this->numCols = GridY;
428 this->calcConfig(); // Reallocate memory for the grid according to the grid loaded
429 }
430
431 this->pData[0] = val; // Place the first read value in grid
432
433 for (int pos = 1; pos < probe_points; pos++){
434 fscanf(fd, "%f\n", &val);
435 this->pData[pos] = val;
436 }
437
438 fclose(fd);
439
440 this->setZoffset(GridZ);
441
442 return true;
443
444 } else {
445 return false;
446 }
447
448}
449
d223207b
QH
450float ZGridStrategy::getZhomeoffset()
451{
452 void* rd;
453
4d7c32b3 454 bool ok = PublicData::get_value( endstops_checksum, home_offset_checksum, &rd );
d223207b 455
4d7c32b3
RM
456 if (ok) {
457 return ((float*)rd)[2];
458 }
459
460 return 0;
d223207b
QH
461}
462
77fbe368
QH
463void ZGridStrategy::setZoffset(float zval)
464{
465 char cmd[64];
466
467 // Assemble Gcode to add onto the queue
468 snprintf(cmd, sizeof(cmd), "M206 Z%1.3f", zval); // Send saved Z homing offset
469
470 Gcode gc(cmd, &(StreamOutput::NullStream));
471 THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc);
472
473}
474
475bool ZGridStrategy::doProbing(StreamOutput *stream) // probed calibration
476{
962b3c72 477 // home first using selected mode: NOHOME, HOMEXY, HOMEXYZ
c449d578 478 this->homexyz();
77fbe368
QH
479
480 // deactivate correction during moves
481 this->setAdjustFunction(false);
482
483 for (int i=0; i<probe_points; i++){
484 this->pData[i] = 0.0F; // Clear the ZGrid
485 }
486
c880f6dd 487 if (this->wait_for_probe){
77fbe368 488
c880f6dd
QH
489 this->cal[X_AXIS] = this->bed_x/2.0f;
490 this->cal[Y_AXIS] = this->bed_y/2.0f;
491 this->cal[Z_AXIS] = this->bed_z/2.0f; // Position head for probe attachment
492 this->move(this->cal, slow_rate); // Move to probe attachment point
77fbe368 493
c880f6dd 494 stream->printf("*** Ensure probe is attached and press probe when done ***\n");
77fbe368 495
c880f6dd
QH
496 while(!zprobe->getProbeStatus()){ // Wait for button press
497 THEKERNEL->call_event(ON_IDLE);
498 };
499 }
77fbe368 500
e76710f3 501 this->in_cal = true; // In calbration mode
77fbe368 502
e76710f3
RM
503 this->cal[X_AXIS] = 0.0f; // Clear calibration position
504 this->cal[Y_AXIS] = 0.0f;
505 this->cal[Z_AXIS] = std::get<Z_AXIS>(this->probe_offsets) + zprobe->getProbeHeight();
77fbe368 506
e76710f3 507 this->move(this->cal, slow_rate); // Move to probe start point
d223207b 508
e76710f3
RM
509 for (int probes = 0; probes < probe_points; probes++){
510 int pindex = 0;
77fbe368 511
47044040
QH
512 // z = z home offset - probed distance
513 float z = getZhomeoffset() -zprobe->probeDistance((this->cal[X_AXIS] + this->cal_offset_x)-std::get<X_AXIS>(this->probe_offsets),
dea3c8c0 514 (this->cal[Y_AXIS] + this->cal_offset_y)-std::get<Y_AXIS>(this->probe_offsets));
d223207b 515
e76710f3 516 pindex = int(this->cal[X_AXIS]/this->bed_div_x + 0.25)*this->numCols + int(this->cal[Y_AXIS]/this->bed_div_y + 0.25);
77fbe368 517
dcd82318
QH
518 this->next_cal(); // Calculate next calibration position
519
520 this->pData[pindex] = z ; // save the offset
e76710f3
RM
521 }
522
dcd82318
QH
523 stream->printf("\nCalibration done.\n");
524 if (this->wait_for_probe) { // Only do this it the config calls for probe removal position
525 this->cal[X_AXIS] = this->bed_x/2.0f;
526 this->cal[Y_AXIS] = this->bed_y/2.0f;
527 this->cal[Z_AXIS] = this->bed_z/2.0f; // Position head for probe removal
528 this->move(this->cal, slow_rate);
529
530 stream->printf("Please remove probe\n");
531
532 }
77fbe368 533
e76710f3
RM
534 // activate correction
535 this->normalize_grid();
536 this->setAdjustFunction(true);
77fbe368 537
e76710f3 538 this->in_cal = false;
77fbe368 539
77fbe368
QH
540 return true;
541}
542
543
d223207b
QH
544void ZGridStrategy::normalize_grid()
545{
546 float min = 100.0F, // set large start value
547 norm_offset = 0;
548
549 // find minimum value in offset grid
550 for (int i = 0; i < probe_points; i++)
551 if (this->pData[i] < min)
552 min = this->pData[i];
553
554 // creates addition offset to set minimum value to zero.
555 norm_offset = -min;
556
557 // adds the offset to create a table of deltas, normalzed to minimum zero
558 for (int i = 0; i < probe_points; i++)
559 this->pData[i] += norm_offset;
560
d8a0c34b
QH
561 // add the offset to the current Z homing offset to preserve full probed offset.
562 this->setZoffset(getZhomeoffset() + norm_offset);
d223207b
QH
563}
564
77fbe368
QH
565void ZGridStrategy::homexyz()
566{
962b3c72
QH
567
568 switch(this->home_before_probe) {
569 case NOHOME : return;
570
571 case HOMEXY : {
4bbbd847 572 Gcode gc("G28 X0 Y0", &(StreamOutput::NullStream));
c449d578
QH
573 THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc);
574 break;
962b3c72
QH
575 }
576
577 case HOMEXYZ : {
c449d578
QH
578 Gcode gc("G28", &(StreamOutput::NullStream));
579 THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc);
580 break;
962b3c72
QH
581 }
582 }
77fbe368
QH
583}
584
585void ZGridStrategy::move(float *position, float feed)
586{
e0be983d
JM
587 // translate the position for non standard cartesian spaces (cal_offset)
588 zprobe->coordinated_move(position[0] + this->cal_offset_x, position[1] + this->cal_offset_y, position[2], feed); // use specified feedrate (mm/sec)
77fbe368 589
47044040 590 //THEKERNEL->streams->printf("DEBUG: move: %s cent: %i\n", cmd, this->center_zero);
77fbe368
QH
591}
592
593
594void ZGridStrategy::next_cal(void){
561af4e3 595 if ((((int) roundf(this->cal[X_AXIS] / this->bed_div_x)) & 1) != 0){ // Odd row
dea3c8c0 596 this->cal[Y_AXIS] -= this->bed_div_y;
561af4e3 597 if (this->cal[Y_AXIS] < (0.0F - (bed_div_y / 2.0f))){
dea3c8c0 598
47044040 599 //THEKERNEL->streams->printf("DEBUG: Y (%f) < cond (%f)\n",this->cal[Y_AXIS], 0.0F);
dea3c8c0 600
77fbe368 601 this->cal[X_AXIS] += bed_div_x;
561af4e3 602 if (this->cal[X_AXIS] > (this->bed_x + (this->bed_div_x / 2.0f))){
dea3c8c0
QH
603 this->cal[X_AXIS] = 0.0F;
604 this->cal[Y_AXIS] = 0.0F;
77fbe368
QH
605 }
606 else
dea3c8c0 607 this->cal[Y_AXIS] = 0.0F;
77fbe368
QH
608 }
609 }
dea3c8c0 610 else { // Even row (0 is an even row - starting point)
77fbe368 611 this->cal[Y_AXIS] += bed_div_y;
561af4e3 612 if (this->cal[Y_AXIS] > (this->bed_y + (bed_div_y / 2.0f))){
dea3c8c0 613
47044040 614 //THEKERNEL->streams->printf("DEBUG: Y (%f) > cond (%f)\n",this->cal[Y_AXIS], this->bed_y);
dea3c8c0 615
77fbe368 616 this->cal[X_AXIS] += bed_div_x;
561af4e3 617 if (this->cal[X_AXIS] > (this->bed_x + (this->bed_div_x / 2.0f))){
dea3c8c0
QH
618 this->cal[X_AXIS] = 0.0F;
619 this->cal[Y_AXIS] = 0.0F;
77fbe368
QH
620 }
621 else
dea3c8c0 622 this->cal[Y_AXIS] = this->bed_y;
77fbe368
QH
623 }
624 }
625}
626
627
628void ZGridStrategy::setAdjustFunction(bool on)
629{
630 if(on) {
631 // set the compensationTransform in robot
632 THEKERNEL->robot->compensationTransform= [this](float target[3]) { target[2] += this->getZOffset(target[0], target[1]); };
633 }else{
634 // clear it
635 THEKERNEL->robot->compensationTransform= nullptr;
636 }
637}
638
639
640// find the Z offset for the point on the plane at x, y
641float ZGridStrategy::getZOffset(float X, float Y)
642{
643 int xIndex2, yIndex2;
644
dea3c8c0
QH
645 // Subtract calibration offsets as applicable
646 X -= this->cal_offset_x;
647 Y -= this->cal_offset_y;
648
77fbe368
QH
649 float xdiff = X / this->bed_div_x;
650 float ydiff = Y / this->bed_div_y;
651
652 float dCartX1, dCartX2;
653
a5f0fa18
TE
654 // Get floor of xdiff. Note that (int) of a negative number is its
655 // ceiling, not its floor.
77fbe368 656
a5f0fa18
TE
657 int xIndex = (int)(floorf(xdiff)); // Get the current sector (X)
658 int yIndex = (int)(floorf(ydiff)); // Get the current sector (Y)
dcd82318 659
a5f0fa18
TE
660 // Index bounds limited to be inside the table
661 if (xIndex < 0) xIndex = 0;
662 else if (xIndex > (this->numRows - 2)) xIndex = this->numRows - 2;
663
664 if (yIndex < 0) yIndex = 0;
665 else if (yIndex > (this->numCols - 2)) yIndex = this->numCols - 2;
666
a5f0fa18 667 xIndex2 = xIndex+1;
a5f0fa18 668 yIndex2 = yIndex+1;
77fbe368
QH
669
670 xdiff -= xIndex; // Find floating point
671 ydiff -= yIndex; // Find floating point
672
673 dCartX1 = (1-xdiff) * this->pData[(xIndex*this->numCols)+yIndex] + (xdiff) * this->pData[(xIndex2)*this->numCols+yIndex];
674 dCartX2 = (1-xdiff) * this->pData[(xIndex*this->numCols)+yIndex2] + (xdiff) * this->pData[(xIndex2)*this->numCols+yIndex2];
675
676 return ydiff * dCartX2 + (1-ydiff) * dCartX1; // Calculated Z-delta
677
678}
679
680// parse a "X,Y,Z" string return x,y,z tuple
681std::tuple<float, float, float> ZGridStrategy::parseXYZ(const char *str)
682{
683 float x = 0, y = 0, z= 0;
684 char *p;
685 x = strtof(str, &p);
686 if(p + 1 < str + strlen(str)) {
687 y = strtof(p + 1, &p);
688 if(p + 1 < str + strlen(str)) {
689 z = strtof(p + 1, nullptr);
690 }
691 }
692 return std::make_tuple(x, y, z);
693}
694